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 assumed to be Universally Unique (hence
the name).
UUIDs have version numbers (1-5), which describe how they were created, and variant numbers (1-2) which describes how they are stored. This module can read all UUIDs, but can only currently create versions 3, 5 (namespaced, heirarchical systems) and 4 (a randomly-generated UUID). It creates variant 1 UUIDs (probably the best choice if you don't have a specific need for variant 2), but can convert them to variant 2.
One type of UUID not defined above is the nil UUID. This is a "special form of UUID" defined as "00000000-0000-0000-0000-000000000000". I suppose it can be used as a placeholder for something that doesn't have a UUID yet?
Randomly-generated UUIDs are called version 4 UUIDs. This package provides a
Random.Generator for the elm/random library.
Generating a random UUID 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. Here is an example of how you might use the UUID generator:
import Random
type Comment
= Comment String UUID
makeComment : String -> Random.Seed -> ( Comment, Random.Seed )
makeComment comment seed =
UUID.generator
|> Random.map (Comment comment)
|> Random.step seedUUIDs 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 (using SHA-1) is the recommended version to use.
Once generated, these UUIDs can then be used as a namespace, making a hierarchy! I think this is pretty cool! You can use this method for making predictable UUIDs from data, and it also has the added bonus that you don't have to deal with random generators/seeds.
Start with an existing UUID as a "parent" UUID, and provide a name to create a new UUID.
grandparent : UUID
grandparent = nil
parent : UUID
parent = grandparent |> childNamed "parent"
parentsSibling : UUID
parentsSibling = grandparent |> childNamed "parent's sibling"
child1 : UUID
child1 = parent |> childNamed "child1"
child2 : UUID
child2 = parent |> childNamed "child2"
cousin : UUID
cousin = parentsSibling |> childNamed "cousin"
canonical child2
--> "8081b7b9-1fb0-54a5-bcf3-e27b4b07f381"Alias for childNamed, for parity with
v3ChildNamed, and just in case you need to make extra-clear
which version UUID you are using!
Start with an existing UUID as a "parent" UUID, and provide a name to create
a new UUID. Note: unless you have good reason to use version 3 UUIDs, it is
recommended you create version 5 UUIDs with childNamed.
grandparent : UUID grandparent = nil
parent : UUID
parent = grandparent |> v3ChildNamed "parent"
parentsSibling : UUID
parentsSibling = grandparent |> v3ChildNamed "parent's sibling"
child1 : UUID
child1 = parent |> v3ChildNamed "child1"
child2 : UUID
child2 = parent |> v3ChildNamed "child2"
cousin : UUID
cousin = parentsSibling |> v3ChildNamed "cousin"
canonical child2
--> "4cacaf93-fcc5-3a02-bc41-c0a3e359e11d"The RFC defining UUIDs defines some base UUIDs to start your hierarchy.
A UUID for the DNS namespace, "6ba7b810-9dad-11d1-80b4-00c04fd430c8".
UUID.dns |> v3ChildNamed "elm-lang.org" |> UUID.canonical
--> "bb125218-fead-3b3f-bcc5-05fbbe879f4d"A UUID for the URL namespace, "6ba7b811-9dad-11d1-80b4-00c04fd430c8"
UUID.url |> v3ChildNamed "https://package.elm-lang.org" |> UUID.canonical
--> "2035dad9-ee50-3732-a2a5-d76a84f42a8a"A UUID for the ISO object ID (OID) namespace, "6ba7b812-9dad-11d1-80b4-00c04fd430c8". I am not going to try to explain what an OID is, if you need to use this, you probably have a better grasp of them than I do!
UUID.oid |> v3ChildNamed "1.2.250.1" |> UUID.canonical --> "2eab101c-060d-3424-aad5-8805218a67bc"
A UUID for the X.500 Distinguished Name (DN) namespace, "6ba7b814-9dad-11d1-80b4-00c04fd430c8". I am not going to try to explain what a X.500 DN is, if you need to use this, you probably have a better grasp of them than I do!
The above functions for creating UUIDs all create variant 1 UUIDs. If you need
to create a variant 2 UUID, any UUID can be converted to a variant 2 UUID with
toVariant2. Note that variant 2 UUIDs cannot be converted to variant 1 UUIDs.
Variant 2 UUIDs are very similar to variant 1 UUIDs, the main end-user difference being that they provide 1 fewer bit of randomness, but are a Microsoft standard. Note the single digit change in the following example:
var1UUID : UUID
var1UUID =
fromString "12345678-1234-4321-bcde-123456789abc"
|> Result.withDefault nil
canonical var1UUID
--> "12345678-1234-4321-bcde-123456789abc"
canonical (toVariant2 var1UUID)
-->"12345678-1234-4321-dcde-123456789abc"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 "UUID was not correct length"
fromString "c72c207b-0847-386d-bdbc-2e5def81cg81"
--> Err "UUID contained non-hexadecimal digits"
fromString "00000000-0000-0000-0000-000000000000"
--> Ok nil
fromString "urn:uuid:00000000-0000-0000-0000-000000000000"
--> Ok nil
fromString "{00000000-0000-0000-0000-000000000000}"
--> Ok nil
fromString "\n\n {urn:uuid: 00 000000-0000-0000-0-000-000000000000}"
--> Ok nilNote: if you are decoding from JSON, you may like json-extra's fromResult function.
uuidDecoder = Json.Decode.string |> Json.Decode.andThen (Json.Decode.Extra.fromResult << UUID.fromString)
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
Microsoft GUID representation and a URN representation.
This is just an alias for canonical, the most common way to represent a UUID.
import Random
someUUID : UUID
someUUID = Random.step generator (Random.initialSeed 345465) |> Tuple.first
toString someUUID
--> canonical someUUIDConvert UUID to canonical textual representation
canonical nil
--> "00000000-0000-0000-0000-000000000000"Convert UUID to Microsoft GUID representation
microsoftGUID nil
--> "{00000000-0000-0000-0000-000000000000}"Convert UUID to URN-namespaced representation
urn nil
--> "urn:uuid:00000000-0000-0000-0000-000000000000"Sometimes you may need to check a UUID's version, variant, or whether it's nil.
If the bits of the UUID indicate that it is properly versioned UUID e.g. version 1,
returns e.g. Just 1. Otherwise, Nothing!
version nil == Nothing
version (nil |> v3ChildNamed "Hello") == Just 3True if the given UUID is the given version number. Always False is the
integer provided is not 1, 2, 3, 4 or 5!
isVersion 8 someUUID -- always False!
isVersion 4 nil -- False!
someJsonValue
|> Json.Decode.decodeValue UUID.decoder
|> UUID.isVersion 5
isVersion 3 (nil |> v3ChildNamed "hello") -- TrueIf the bits of the UUID indicate that it is a variant 1 or 2 UUID, returns
Just 1 or Just 2, respectively. Otherwise, Nothing!
variant nil == Nothing
variant (nil |> v3ChildNamed "Hello") == Just 1True if the given UUID is the given variant number. Always False is the
integer provided is not 1 or 2!
isVariant 4 someUUID -- Always False, variant 4 doesn't exist!
someJsonValue
|> Json.Decode.decodeValue UUID.decoder
|> UUID.isVariant 1
isVariant 2 (someUUID |> toVariant2) -- TrueTrue if the given UUID correctly defines a version and variant. Note: the
nil UUID is not valid!
isValid nil == False
Result.map UUID.isValid (UUID.fromString "c72c207b-0847-386d-bdbc-2e5def81cf81") == True
Result.map UUID.isValid (UUID.fromString "c72c207b-0847-986d-bdbc-2e5def81cf81") == FalseSometimes you may need to ensure that a UUID is definitely a certain version or variant. If you're decoding from JSON, you may find the following functions useful in conjunction with json-extra's fromResult function.
import Json.Decode import Json.Decode.Extra
uuidDecoder : Json.Decode.Decoder UUID
uuidDecoder =
Json.Decode.string
|> Json.Decode.andThen (Json.Decode.Extra.fromResult << UUID.fromString)
|> Json.Decode.andThen (Json.Decode.Extra.fromResult << UUID.checkVersion 4)
|> Json.Decode.andThen (Json.Decode.Extra.fromResult << UUID.checkVariant 1)
Json.Decode.decodeString uuidDecoder "\"00000000-0000-0000-0000-000000000000\""
|> Result.toMaybe
--> Nothing
Json.Decode.decodeString uuidDecoder "\"7d25c9af-c80d-4304-a984-1d0b20fc581a\""
|> Result.map UUID.canonical
--> Ok "7d25c9af-c80d-4304-a984-1d0b20fc581a"A simple function to use while chaining Results. Makes sure the UUID
you're dealing with isn't the Nil UUID!
"8b681345-171d-48a0-9406-469d0a9de997"
|> UUID.fromString
|> Result.andThen checkNotNil
|> Result.map canonical
--> Ok "8b681345-171d-48a0-9406-469d0a9de997"Check which version a UUID is, and if it's not the right one, return an Err.
import Random
someVersion4UUID : UUID
someVersion4UUID = Random.step generator (Random.initialSeed 3476326) |> Tuple.first
someVersion4UUID |> checkVersion 4
--> Ok someVersion4UUID
nil |> v3ChildNamed "hello" |> checkVersion 4
--> Err "UUID is not Version 4"
nil |> checkVersion 1
--> Err "UUID does not define a valid version"Check which variant a UUID is, and if it's not the right one, return an Err.
import Random
someVariant1UUID : UUID
someVariant1UUID = Random.step generator (Random.initialSeed 2355673) |> Tuple.first
someVariant1UUID |> checkVariant 1
--> Ok someVariant1UUID
nil |> v3ChildNamed "hello" |> checkVariant 2
--> Err "UUID is not variant 2"
nil |> checkVariant 2
--> Err "UUID does not define a valid variant"Checks if the UUID properly defines a version and variant, and if not return
an Err. Note: the nil UUID is not valid!
checkValid nil == Err "UUID is nil"
UUID.fromString "c72c207b-0847-386d-bdbc-2e5def81cf81"
|> Result.andThen UUID.checkValid
|> Result.map UUID.canonical
|> (==) Ok "c72c207b-0847-386d-bdbc-2e5def81cf81"
UUID.fromString "c72c207b-0847-986d-bdbc-2e5def81cf81"
|> Result.andThen UUID.checkValid
|> (==) Err "Version not defined"
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: