Skip to content

Instantly share code, notes, and snippets.

@dcastro
Last active December 6, 2018 12:39
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dcastro/ade2ac95d87b42d4a774200269bf81f8 to your computer and use it in GitHub Desktop.
Save dcastro/ade2ac95d87b42d4a774200269bf81f8 to your computer and use it in GitHub Desktop.
"Newtype" deriving for scala
import org.scalacheck.cats.implicits._
import org.scalacheck.Gen
import io.circe.{Decoder, Encoder}
object Test extends App {
case class X(i: Int)
implicit val x: Encoder[X] = Wrapper[X].deriveInstance[Encoder]
implicit val y: Decoder[X] = Wrapper[X].deriveInstance[Decoder].withErrorMessage("some custom error msg")
val z: Gen[X] = Wrapper[X].lift(Gen.choose(0, 10))
}
/** Depends on shapeless and cats. **/
import cats.implicits._
import cats.Invariant
import cats.evidence.Is
import shapeless._
/** Represents the ability to convert between a wrapper type [[A]] and its wrapped type `Wrapped` */
sealed trait Wrapper[A] {
type Wrapped
val to: A => Wrapped
val from: Wrapped => A
def deriveInstance[F[_]: Invariant](implicit instance: F[Wrapped]): F[A] =
lift(instance)
def lift[F[_]: Invariant](instance: F[Wrapped]): F[A] =
instance.imap(from)(to)
}
object Wrapper {
type Aux[A, B] = Wrapper[A] {type Wrapped = B}
def apply[A](implicit ev: Wrapper[A]): Wrapper.Aux[A, ev.Wrapped] = ev
/** Evidence that [[A]] is a wrapper for [[C]] */
implicit def isWrapper[A, B, C](implicit
// evidence that `A` can be converted to some generic representation `B`
gen: Generic.Aux[A, B],
// evidence that `B` is a list with a single field of type `C`
equiv: (C :: HNil) Is B
): Wrapper.Aux[A, C] = {
new Wrapper[A] {
type Wrapped = C
val to: A => C = a => equiv.flip.coerce(gen.to(a)).head
val from: C => A = c => gen.from(equiv.coerce(c :: HNil))
}
}
//////// Optional: depends on `monocle`.
import monocle.Iso
/** Monocle isomorphism between a wrapper type and its wrapped type. */
def wrap[A](implicit w: Wrapper[A]): Iso[A, w.Wrapped] =
Iso[A, w.Wrapped](w.to)(w.from)
def unwrap[A](implicit w: Wrapper[A]): Iso[w.Wrapped, A] =
wrap.reverse
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment