A UUID looks something like e1631449-6321-4a58-920c-5440029b092e, and can
be used as an identifier for anything. Each 128-bit number, usually represented
as 32 hexadecimal digits, is generally used under the assumption that it is
Universally Unique (hence the name).
This package supports variant 1 UUIDs (those outlined in [RFC 4122][rfc]), which covers the vast majority of those in use (versions 1-5).
You can attempt to create a UUID from a string. This function can interpret a fairly broad range of formatted (and mis-formatted) UUIDs, including ones with too much whitespace, too many (or not enough) hyphens, or uppercase characters.
fromString "c72c207b-0847-386d-bdbc-2e5def81cf811"
--> Err WrongLength
fromString "c72c207b-0847-386d-bdbc-2e5def81cg81"
--> Err WrongFormat
fromString "c72c207b-0847-386d-bdbc-2e5def81cf81"
|> Result.map version
--> Ok 3
fromString "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
--> Ok dnsNamespaceThis enumerates all the possible errors that can happen when reading existing UUIDs.
import Bytes.Extra exposing (fromByteValues)
-- Note the non-hexadecimal characters in this string!
fromString "12345678-9abc-4567-89ab-cdefghijklmn"
--> Err WrongFormat
fromBytes (fromByteValues [ 223 ])
--> Err WrongLength
-- This package only supports variant 1 UUIDs
fromString "urn:uuid:12345678-9abc-4567-c234-abcd12345678"
--> Err UnsupportedVariant
fromBytes (fromByteValues [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
--> Err IsNil
fromString "{00000000-0000-0000-0000-000000000000}"
--> Err IsNil
fromString "6ba7b814-9dad-71d1-80b4-00c04fd430c8"
--> Err NoVersionGenerating a random UUID (version 4) is, I think, the most straightforward way of making a UUID, and I see them used all the time. There are a couple of ways of using a generator to create a value, which are described nicely in the elm/random docs.
import Random
Random.step UUID.generator (Random.initialSeed 12345)
|> Tuple.first
|> UUID.toRepresentation Urn
--> "urn:uuid:5b58931d-bb69-406d-81a9-7746c300838c"UUIDs can be created using a namespace UUID and a name, which is then hashed to create a new UUID. The hash function used depends on the version of UUID: verison 3 UUIDs use MD5, and version 5 UUIDs use SHA-1. Version 5 is the officially recommended version to use.
Although the RFC defining UUIDs defines some base namespaces, any UUID can be used as a namespace, making a hierarchy. I think this is pretty cool! You can use this method for consistently making the same UUID from the same data, which can be very useful in some situations.
Create a version 5 UUID from a String and a namespace, which should be a
UUID. The same name and namespace will always produce the same UUID, which can
be used to your advantage. Furthermore, the UUID created from this can be used
as a namespace for another UUID, creating a hierarchy of sorts.
apiNamespace : UUID
apiNamespace =
forName "https://api.example.com/v2/" dnsNamespace
widgetNamespace : UUID
widgetNamespace =
forName "Widget" apiNamespace
toString apiNamespace
--> "bad122ad-b5b6-527c-b544-4406328d8b13"
toString widgetNamespace
--> "7b0db628-d793-550b-a883-937a276f4908"Create a version 5 UUID from Bytes and a namespace, which should be a
UUID. The same name and namespace will always produce the same UUID, which can
be used to your advantage. Furthermore, the UUID created from this can be used
as a namespace for another UUID, creating a hierarchy of sorts.
import Bytes
import Bytes.Encode
type alias Widget = { name: String, value: Float }
apiNamespace : UUID
apiNamespace =
forName "https://api.example.com/v2/" dnsNamespace
widgetNamespace : UUID
widgetNamespace =
forName "Widget" apiNamespace
uuidForWidget : Widget -> UUID
uuidForWidget { name, value } =
let
bytes =
Bytes.Encode.encode <| Bytes.Encode.sequence
[ Bytes.Encode.unsignedInt32 Bytes.BE (String.length name)
, Bytes.Encode.string name
, Bytes.Encode.float64 Bytes.BE value
]
in
forBytes bytes widgetNamespace
Widget "Exponent" 0.233
|> uuidForWidget
|> toString
--> "46dab34b-5447-5c5c-8e24-fe4e3daab014"Similar to forName, creates a UUID based on the given String
and namespace UUID, but creates a version 3 UUID. If you can choose between the
two, it is recommended that you choose version 5 UUIDs.
apiNamespace : UUID
apiNamespace =
forNameV3 "https://api.example.com/v1/" dnsNamespace
toString apiNamespace
--> "f72dd2ff-b8e7-3c58-afe1-352d5e273de4"Similar to forBytes, creates a UUID based on the given Bytes
and namespace UUID, but creates a version 3 UUID. If you can choose between the
two, it is recommended that you choose version 5 UUIDs.
import Bytes
import Bytes.Encode
apiNamespace : UUID
apiNamespace =
forName "https://api.example.com/v2/" dnsNamespace
forBytesV3 (Bytes.Encode.encode (Bytes.Encode.unsignedInt8 245)) apiNamespace
|> toString
--> "4ad1f4bf-cf83-3c1d-b4f5-975f7d0fc9ba"A UUID for the DNS namespace, "6ba7b810-9dad-11d1-80b4-00c04fd430c8".
forName "elm-lang.org" dnsNamespace
|> toString
--> "c6d62c23-3406-5fc7-836e-9d6bef13e18c"A UUID for the URL namespace, "6ba7b811-9dad-11d1-80b4-00c04fd430c8".
forName "https://package.elm-lang.org" urlNamespace
|> toString
--> "e1dba7a5-c338-53f3-bc90-353f045447be"A UUID for the ISO object ID (OID) namespace, "6ba7b812-9dad-11d1-80b4-00c04fd430c8".
forName "1.2.250.1" oidNamespace |> toString --> "87a2ac60-306f-5131-b748-3787f9f55685"
A UUID for the X.500 Distinguished Name (DN) namespace, "6ba7b814-9dad-11d1-80b4-00c04fd430c8".
If you don't know what this is for, I can't help you, because I don't know either.
You can attempt to create a UUID from some Bytes, the only contents of which much be the UUID.
import Bytes.Extra exposing (fromByteValues)
fromBytes (fromByteValues [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
--> Err IsNil
fromBytes (fromByteValues [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
--> Err WrongLength
fromBytes (fromByteValues [0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8])
--> Ok x500NamespaceA Bytes.Decode.Decoder, for integrating with a broader decode.
import Bytes
import Bytes.Decode exposing (Decoder)
import Bytes.Extra exposing (fromByteValues)
type alias Record =
{ name : String
, value : Int
, id : UUID
}
recordDecoder : Decoder Record
recordDecoder =
Bytes.Decode.map3 Record
(Bytes.Decode.unsignedInt16 Bytes.BE |> Bytes.Decode.andThen Bytes.Decode.string)
(Bytes.Decode.unsignedInt32 Bytes.BE)
UUID.decoder
Bytes.Decode.decode recordDecoder <| fromByteValues
[ 0x00, 0x0b -- 11
, 0x68, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x77, 0x6f, 0x72, 0x6c, 0x64 -- "hello world"
, 0x00, 0x00, 0x00, 0xFF -- 255
, 0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8 -- UUID
]
--> Just
--> { name = "hello world"
--> , value = 255
--> , id = urlNamespace
--> }Convert a UUID to Bytes.
import Bytes.Extra exposing (toByteValues)
toBytes dnsNamespace
|> toByteValues
--> [0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8]An encoder for encoding data that includes a UUID.
import Bytes.Extra exposing (toByteValues)
import Bytes.Encode
import Bytes.Encode.Extra
defaultNamespaces : List UUID
defaultNamespaces =
[ dnsNamespace, urlNamespace, oidNamespace, x500Namespace ]
Bytes.Encode.Extra.list UUID.encoder defaultNamespaces
|> Bytes.Encode.encode
|> toByteValues
--> [ 0x6b, 0xa7, 0xb8, 0x10, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8
--> , 0x6b, 0xa7, 0xb8, 0x11, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8
--> , 0x6b, 0xa7, 0xb8, 0x12, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8
--> , 0x6b, 0xa7, 0xb8, 0x14, 0x9d, 0xad, 0x11, 0xd1, 0x80, 0xb4, 0x00, 0xc0, 0x4f, 0xd4, 0x30, 0xc8
--> ]UUIDs are generally represented by 32 hexadecimal digits in the form
00112233-4455-M677-N899-aabbccddeeff, where the four bits at position M
denote the UUID version, and the first two or three bits at position N denote
the variant. This is the "canonical" representation, but there is also a URN
representation and a format used mostly by Microsoft, where they are more
commonly named GUIDs.
The canonical representation of the UUID. This is the same as
toRepresentation Canonical.
toString dnsNamespace
--> "6ba7b810-9dad-11d1-80b4-00c04fd430c8"Convert UUID to a given format
toRepresentation Canonical dnsNamespace
--> "6ba7b810-9dad-11d1-80b4-00c04fd430c8"
toRepresentation Urn dnsNamespace
--> "urn:uuid:6ba7b810-9dad-11d1-80b4-00c04fd430c8"
toRepresentation Guid dnsNamespace
--> "{6ba7b810-9dad-11d1-80b4-00c04fd430c8}"There are three typical human-readable representations of UUID: canonical, a Uniform Resource Name and Microsoft's formatting for its GUIDs (which are just version 4 UUIDs nowadays).
Get the version number of a UUID. Only versions 3, 4 and 5 are supported
in this package, so you should only expect the returned Int to be 3, 4 or
5.
import Random
Random.step generator (Random.initialSeed 1)
|> Tuple.first
|> version
--> 4
fromString "{12345678-1234-5678-8888-0123456789ab}"
|> Result.map version
--> Ok 5One more, special UUID is the nil UUID. This is just
"00000000-0000-0000-0000-000000000000". I suppose it can work as a placeholder
in some cases, but elm has Maybe for that! Nevertheless, you might need to
check for nil UUIDs, or print the nil UUID.
128 bits of nothing.
True if the given bytes are the nil UUID (00000000-0000-0000-0000-000000000000).
isNilBytes nilBytes
--> TruenilString
--> "00000000-0000-0000-0000-000000000000"True if the given string represents the nil UUID (00000000-0000-0000-0000-000000000000).
nilRepresentation Canonical
--> "00000000-0000-0000-0000-000000000000"
nilRepresentation Urn
--> "urn:uuid:00000000-0000-0000-0000-000000000000"
nilRepresentation Guid
--> "{00000000-0000-0000-0000-000000000000}"
This modules provides a UUID type, and functions to work with them. It is an opaque type, which basically means you have to use the provided functions if you want to do anything with it!
Here is an example of a
Bookmodel, which uses UUIDs to identify both the book and its authors: