Skip to content

Instantly share code, notes, and snippets.

@pierangeloc
Last active November 23, 2021 13:25
Show Gist options
  • Save pierangeloc/ec408ca3a864f5d72dbc83a0e793e59a to your computer and use it in GitHub Desktop.
Save pierangeloc/ec408ca3a864f5d72dbc83a0e793e59a to your computer and use it in GitHub Desktop.
Doobie instances for NewType
import io.estatico.newtype.ops._
import io.estatico.newtype.macros.newtype
import eu.timepit.refined.api.Refined
import doobie.implicits._
import doobie.postgres.implicits._
import doobie.refined.implicits._
/**
* Derive a Put (so it can be interpolated in doobie sql expressions) for any newtype backed by a type that is
* supported by doobie (e.g. any wrapper of String, UUID, Boolean etc)
*
* if a: A has a Coercible[A, B] means one can transform a to b: B via a.coerce[B]
*/
implicit def newTypePut[N: Coercible[*, R], R: Put]: Put[N] = Put[R].contramap[N](_.coerce[R])
implicit def newTypeRead[N: Coercible[R, *], R: Read]: Read[N] = Read[R].map(_.asInstanceOf[N])
/** If we have an Eq instance for Repr type R, derive an Eq instance for NewType N. */
implicit def coercibleEq[R, N](implicit ev: Coercible[Eq[R], Eq[N]], R: Eq[R]): Eq[N] =
ev(R)
/*
* With this one can create value classes like these and embed them in doobie fragments
*/
@newtype case class UserId(value: UUID)
val userId: UserId = ???
val fragment = sql"select * from users where user_id = $userId"
@envilogger
Copy link

envilogger commented Jun 9, 2020

Why newTypePut/newTypeRead doesn't the same as coercibleEq? ie if we have Read[R], and Coercible[Read[N], Read[R]] we can get Read[N]? Also the solution doesn't work with latest version of newtype

implicit def newTypePut[R, N](implicit ev: Coercible[Put[R], Put[N]], R: Put[R]): Put[N] = ev(R)
implicit def newTypeRead[R, N](implicit ev: Coercible[Read[R], Read[N]], R: Read[R]): Read[N] = ev(R)

@kolov
Copy link

kolov commented Mar 5, 2021

with newtype 0.4.4, I got

value repr is not a member of type parameter N
  implicit def newTypePut[N: Coercible[R, *], R: Put]: Put[N] = Put[R].contramap[N](_.repr.asInstanceOf[R])

This compiled:

implicit def newTypePut[B, A](implicit ev: Coercible[B, A], evp: Put[A]): Put[B] = evp.contramap[B](ev(_))

@pierangeloc
Copy link
Author

Thanks, I fixed it for 0.4.4

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment