Introducing memeid: An RFC-compliant library for working with UUIDs
by Alejandro Gomez
- •
- February 27, 2020
- •
- scala• open source
- |
- 8 minutes to read.

We’ve recently been working on a library for generating RFC-compliant Universal Unique Identifiers (UUIDs), and we are happy to announce the first release of memeid. memeid
(pronounced /mɪːˈmIːˈɪd/) is a library for generating and working with UUIDs as defined by RFC4122.
A universally unique identifier (UUID) is a 128-bit number used to identify information in computer systems.
When generated according to the standard methods, UUIDs are, for practical purposes, unique. Their uniqueness does not depend on a central registration authority or coordination between the parties generating them, unlike most other numbering schemes. – Wikipedia article on UUIDs
Why?
The standard UUID type of the JVM java.util.UUID
has a number of problems, namely:
- A bug in the comparison function that will never be fixed, which causes UUIDs in Java to be sorted differently than elsewhere https://bugs.java.com/bugdatabase/view_bug.do?bug_id=7025832
- Only provides UUID generation for random (V4) and non-namespaced pseudo-V3 UUIDs
In practice, the comparison bug is not that important, but we wanted a library that allowed us to create the different types of UUIDs defined by the RFC, since each of them has different semantics and performance implications.
Install
Java
Using maven
<dependency>
<groupId>com.47deg</groupId>
<artifactId>memeid</artifactId>
<version>0.1.0</version>
</dependency>
Using gradle
compile group: 'com.47deg', name: 'memeid', version: '0.1.0'
Scala
Add this to your build.sbt
file:
libraryDependencies += "com.47deg" %% "memeid4s" % "0.1.0"
Usage
UUID construction
Time-based (v1)
The time-based (V1) variant of UUIDs is the fastest to generate. It uses a monotonic clock and node information to generate UUIDs.
import memeid4s.UUID
UUID.V1.next
Random (v4)
The cryptographically random variant, equivalent to java.util.UUID/randomUUID
.
UUID.V4.random
Namespaced (v3, v5)
Namespaced UUIDs are generated from a UUID (namespace) and a hashed value (name). V3 uses MD5 and V5 uses SHA1 hash.
val namespace = UUID.V1.next
We can now create UUIDs with the namespace and an arbitrary value as the name. It automatically works with Strings and UUIDs:
UUID.V3(namespace, "my-secret-code")
If you want to hash a custom type, you must provide an implicit memeid4s.digest.Digestible
instance.
import memeid4s.digest.Digestible
case class User(firstName: String, lastName: String)
implicit val digestibleUser: Digestible[User] =
(u: User) => u.firstName.getBytes ++ u.lastName.getBytes
The implicit instance is used to convert your type into a byte array for hashing:
UUID.V3(namespace, User("Federico", "García Lorca"))
Semi-sequential, random (SQUUID)
SQUUIDs are a non-standard variaton of V4 UUIDs that are semi-sequential. They incorporate a time-component in their 32 most significant bits to generate UUIDs that don’t fragment DB indexes.
UUID.V4.squuid
Java interoperability
memeid
provides conversion method between UUID
and java.util.UUID
through:
val j = java.util.UUID.fromString("a5fa7934-501c-46eb-9ea7-16de3086e6d8")
val u = memeid.UUID.fromString("8b4d1529-5fd0-4a91-8f4f-ceee10d1c060")
UUID.fromUUID(j)
// res5: UUID = a5fa7934-501c-46eb-9ea7-16de3086e6d8
u.asJava
// res6: java.util.UUID = 8b4d1529-5fd0-4a91-8f4f-ceee10d1c060
Literal syntax
memeid
provides literal syntax with compile-time verification for UUIDs with the uuid
interpolator. To use it, add this to your build.sbt
:
libraryDependencies += "com.47deg" %% "memeid4s-literal" % "0.1.0"
We can now create UUIDs with literal syntax by importing memeid.literal._
import memeid4s.literal._
uuid"cb096727-6a82-4abd-bc79-fc92be8c5d88"
// res7: UUID = cb096727-6a82-4abd-bc79-fc92be8c5d88
Invalid UUID literals will fail at compile time:
uuid"not-a-uuid"
// error: invalid UUID: not-a-uuid
// uuid"not-a-uuid"
// ^^^^^^^^^^^^^^^^
Integrations
memeid
provides several modules that integrate with popular third-party libraries. If you see something missing don’t hesitate to open an issue or send a patch.
Doobie
The Doobie integration allows you to use the UUID
type mapped to your database’s UUID type.
libraryDependencies += "com.47deg" %% "memeid4s-doobie" % "0.1.0"
To have the UUID mappings available in scope, you can import memeid.doobie.implicits
.
import memeid4s.doobie.implicits._
def select(uuid: UUID): Query0[UUID] =
sql"""SELECT id from test where id = ${uuid}""".query[UUID]
def insert(uuid: UUID): Update0 =
sql"""insert into test (id) values ($uuid)""".update
val example = uuid"58d61328-1b08-1171-1ee7-1283ed639e77"
{
for {
_ <- insert(example).run.transact(transactor)
u <- select(example).unique.transact(transactor)
} yield u
}.unsafeRunSync
// res10: UUID = 58d61328-1b08-1171-1ee7-1283ed639e77
Circe
libraryDependencies += "com.47deg" %% "memeid4s-circe" % "0.1.0"
You can import memeid.circe.implicits
to have the Encoder
and Decoder
instances for UUID
in scope.
import io.circe.{ Json, Encoder, Decoder }
import memeid4s.circe.implicits._
val uuid = uuid"58d61328-1b08-1171-1ee7-1283ed639e77"
val json = Json.fromString(uuid.toString)
Encoder[UUID].apply(uuid)
// res11: Json = JString("58d61328-1b08-1171-1ee7-1283ed639e77")
Decoder[UUID].decodeJson(json)
// res12: Decoder.Result[UUID] = Right(58d61328-1b08-1171-1ee7-1283ed639e77)
Http4s
libraryDependencies += "com.47deg" %% "memeid4s-http4s" % "0.1.0"
Path parameters
Using UUID
companion object, we can extract UUIDs from path parameters in URLs:
import cats.effect._
import org.http4s._
import org.http4s.dsl.io._
HttpRoutes.of[IO] {
case GET -> Root / "user" / UUID(uuid) => Ok(s"Hello, ${uuid}!")
}
Query parameters
The http4s integrations provides implicit instances for QueryParamDecoder[UUID]
and QueryParamEncoder[UUID]
, which you can use to derive matchers for query parameters or send UUID in request query parameters.
import cats.effect._
import org.http4s._
import org.http4s.dsl.io._
import memeid4s.http4s.implicits._
object UUIDParamDecoder extends QueryParamDecoderMatcher[UUID]("uuid")
HttpRoutes.of[IO] {
case GET -> Root / "user" :? UUIDParamDecoder(uuid) => Ok(s"Hello, ${uuid}!")
}
Cats & Cats-effect
libraryDependencies += "com.47deg" %% "memeid4s-cats" % "0.1.0"
The cats integration provides typeclass implementation for UUID
, as well as effectful constructors for UUIDs for integration with programs that use cats-effect
.
Typeclasses
import cats._
import memeid4s.cats.implicits._
Order[UUID]
Hash[UUID]
Eq[UUID]
Show[UUID]
Constructors
UUID.random[IO]
UUID.v3[IO, String](namespace, "my-secret-code")
UUID.v5[IO, String](namespace, "my-secret-code")
Conclusion
memeid
lets you generate and work with UUIDs as defined by RFC4122, and is implemented as a Java library that can be used from any JVM language. In this post, we took a look at its Scala API as well as integrations with some of the most popular Scala libraries, showing how you can use it to generate different types of UUIDs or encoding/decoding UUID values.
We hope you find memeid
useful. Feel free to reach out if you have any questions or comments. The code is open source and available on GitHub.