fuuid - Functional UUID

Java UUID’s aren’t “exceptionally” safe. Operations throw and are not referentially transparent. We can fix that.
Currently this is the minimal implementation that keeps me content. This aims to offer a baseline set of tools for interacting with FUUID’s. Modules add functionality by bringing in additional dependencies to support additional interactions.
Quick Start
To use fuuid in an existing SBT project with Scala 2.11 or a later version, add the following dependency to your
build.sbt
:
libraryDependencies += "io.chrisdavenport" %% "fuuid" % "<version>"
Examples
import io.chrisdavenport.fuuid.FUUID
// import io.chrisdavenport.fuuid.FUUID
import cats.effect.IO
// import cats.effect.IO
// We place it in IO because this requires a Sync constraint
val create: IO[FUUID] = FUUID.randomFUUID[IO]
// create: cats.effect.IO[io.chrisdavenport.fuuid.FUUID] = IO$1649918362
val fromString : Either[Throwable, FUUID] = FUUID.fromString("d6faceab-4193-4508-86ca-e1561d38fea6")
// fromString: Either[Throwable,io.chrisdavenport.fuuid.FUUID] = Right(d6faceab-4193-4508-86ca-e1561d38fea6)
val failsReferentiallyTransparently : Either[Throwable, FUUID] = FUUID.fromString("Not a UUID")
// failsReferentiallyTransparently: Either[Throwable,io.chrisdavenport.fuuid.FUUID] = Left(java.lang.IllegalArgumentException: Invalid UUID string: Not a UUID)
// For some syntax improvements
import cats.implicits._
// import cats.implicits._
// Uses cats Eq
val equalToItself : IO[Boolean] = for {
fuuid <- FUUID.randomFUUID[IO]
} yield fuuid === fuuid
// equalToItself: cats.effect.IO[Boolean] = <function1>
equalToItself.unsafeRunSync
// res3: Boolean = true
// Uses cats Order
val laterGreaterThanEarlier : IO[Boolean] = for {
fuuid1 <- FUUID.randomFUUID[IO]
fuuid2 <- FUUID.randomFUUID[IO]
} yield fuuid2 > fuuid1
// laterGreaterThanEarlier: cats.effect.IO[Boolean] = IO$1205647000
laterGreaterThanEarlier.unsafeRunSync
// res5: Boolean = false
Circe Integration
To use fuuid directly in you circe Json handling, add to your build.sbt
:
libraryDependencies += "io.chrisdavenport" %% "fuuid-circe" % "<version>"
An example using this integration:
import io.chrisdavenport.fuuid.circe._
// import io.chrisdavenport.fuuid.circe._
import io.circe.syntax._
// import io.circe.syntax._
// Running UnsafeRunSync For Tut Purposes - Do Not Do this in your code please.
val circeFUUID = FUUID.randomFUUID[IO].unsafeRunSync
// circeFUUID: io.chrisdavenport.fuuid.FUUID = fdc68884-e719-4103-b0a4-cafadf6b04a5
val circeFUUIDJson = circeFUUID.asJson
// circeFUUIDJson: io.circe.Json = "fdc68884-e719-4103-b0a4-cafadf6b04a5"
val reparsing = circeFUUIDJson.as[FUUID]
// reparsing: io.circe.Decoder.Result[io.chrisdavenport.fuuid.FUUID] = Right(fdc68884-e719-4103-b0a4-cafadf6b04a5)
Http4s Integration
To use fuuid to define http4s paths, add to your build.sbt
:
libraryDependencies += "io.chrisdavenport" %% "fuuid-http4s" % "<version>"
An example using this integration:
import io.chrisdavenport.fuuid.http4s.FUUIDVar
// import io.chrisdavenport.fuuid.http4s.FUUIDVar
import org.http4s._, org.http4s.dsl.io._
// import org.http4s._
// import org.http4s.dsl.io._
def getEntityByUuid(id: FUUID): IO[String] = ???
// getEntityByUuid: (id: io.chrisdavenport.fuuid.FUUID)cats.effect.IO[String]
val service: HttpRoutes[IO] =
HttpRoutes.of[IO] {
case GET -> Root / "uuid" / FUUIDVar(id) =>
for {
entity <- getEntityByUuid(id)
response <- Ok(entity)
} yield response
}
// service: org.http4s.HttpRoutes[cats.effect.IO] = Kleisli(org.http4s.HttpRoutes$$$Lambda$5219/978096034@453be608)
Doobie Integration
To use fuuid to store UUID’s using doobie, add to your build.sbt
:
libraryDependencies += "io.chrisdavenport" %% "fuuid-doobie" % "<version>"
This dependency will provide a Meta[FUUID]
which depends on Meta[UUID]
to provide support for FUUID
.
You will need to provide the instance of Meta[UUID]
in scope. Firstly, we import:
import doobie._
import doobie.implicits._
import io.chrisdavenport.fuuid.doobie.implicits._
Postgres Example
An example of a query and an insert using this integration in Postgres.
// This importe will provide `Meta[UUID]` support for postgres
import doobie.postgres.implicits._
// import doobie.postgres.implicits._
// This is the table we'll use for the insert and update below
def createdTable: Update0 = {
sql"""
CREATE TABLE tablewithUUIDid (
id UUID NOT NULL
)""".update
}
// createdTable: doobie.Update0
def queryBy(fuuid: FUUID): Query0[FUUID] = {
sql"""SELECT id from tablewithUUIDid where id = ${fuuid}""".query[FUUID]
}
// queryBy: (fuuid: io.chrisdavenport.fuuid.FUUID)doobie.Query0[io.chrisdavenport.fuuid.FUUID]
def insertId(fuuid: FUUID): Update0 = {
sql"""INSERT into tablewithUUIDid (id) VALUES ($fuuid)""".update
}
// insertId: (fuuid: io.chrisdavenport.fuuid.FUUID)doobie.Update0
H2 Example
An example of a query and an insert using this integration in H2:
// This importe will provide `Meta[UUID]` support for h2
import doobie.h2.implicits._
// import doobie.h2.implicits._
// This is the table we'll use for the insert and update below
def createdTable: Update0 = {
sql"""
CREATE TABLE tablewithUUIDid (
id UUID NOT NULL
)""".update
}
// createdTable: doobie.Update0
def queryBy(fuuid: FUUID): Query0[FUUID] = {
sql"""SELECT id from tablewithUUIDid where id = ${fuuid}""".query[FUUID]
}
// queryBy: (fuuid: io.chrisdavenport.fuuid.FUUID)doobie.Query0[io.chrisdavenport.fuuid.FUUID]
def insertId(fuuid: FUUID): Update0 = {
sql"""INSERT into tablewithUUIDid (id) VALUES ($fuuid)""".update
}
// insertId: (fuuid: io.chrisdavenport.fuuid.FUUID)doobie.Update0