Skip to content

Instantly share code, notes, and snippets.

@REDNBLACK
Created February 24, 2019 17:26
Show Gist options
  • Save REDNBLACK/943928d6d9bae7483555195bd9e61e96 to your computer and use it in GitHub Desktop.
Save REDNBLACK/943928d6d9bae7483555195bd9e61e96 to your computer and use it in GitHub Desktop.
Learning shapeless features
trait Transformer[A, B] {
def transform(a: A): B
}
object Transformer {
import shapeless.{::, Generic, HList, HNil, Lazy, =:!=}
import cats.Functor
import cats.~>
import cats.syntax.functor._
trait LowPriorityInstances {
// This way natFunctorT has more priority and `q` field works correctly
implicit def identityT[A](implicit ev: A =:!= HNil): Transformer[A, A] = identity(_)
}
trait DefaultInstances {
implicit val intToStringT: Transformer[Int, String] = (_: Int).toString
implicit val stringToDoubleT: Transformer[String, Double] = (_: String).toDouble
implicit val doubleToInt: Transformer[Double, Int] = (_: Double).toInt
}
object instances extends DefaultInstances with LowPriorityInstances {
implicit def natIdentity[F[_]]: F ~> F = λ[F ~> F](identity(_))
implicit def functorNatT[F[_]: Functor, G[_], A, B](implicit T: Transformer[A, B], N: F ~> G): Transformer[F[A], G[B]] =
(a: F[A]) => N(a.map(T.transform))
// We can also add this for optimization
// implicit def functorIdT[F[_]: Functor, A]: Transformer[F[A], F[A]] = identity(_)
// implicit def functorT[F[_]: Functor, A, B](implicit T: Transformer[A, B]): Transformer[F[A], F[B]] = _.map(T.transform)
// Conflicts with identityT
// implicit val hnilToHnilT: Transformer[HNil, HNil] = identity(_)
implicit def genericT[A, B, AR <: HList, BR <: HList](
implicit
genA: Generic.Aux[A, AR],
genB: Generic.Aux[B, BR],
trans: Lazy[Transformer[AR, BR]]
): Transformer[A, B] = (a: A) => genB.from(trans.value.transform(genA.to(a)))
implicit def hconsT[AH, BH, AT <: HList, BT <: HList](
implicit
hTrans: Lazy[Transformer[AH, BH]],
tTrans: Transformer[AT, BT]
): Transformer[AH :: AT, BH :: BT] = (a: AH :: AT) => hTrans.value.transform(a.head) :: tTrans.transform(a.tail)
}
object syntax {
implicit class Ops[A](val a: A) extends AnyVal {
def into[B](implicit T: Transformer[A, B]): B = T.transform(a)
}
}
}
// Definitions
case class Foo(s: String, i: Int, d: Double, x: Option[Int], y: Option[Int], z: String, q: Option[String])
case class Bar(s: Double, i: String, d: Int, x: List[String], y: Option[String], z: String, q: Option[String])
{
import cats.~>
import Transformer.instances._
import Transformer.syntax._
// For `x` field
implicit val optToList: Option ~> List = λ[Option ~> List](_.toList)
// For `y` field
import cats.instances.option._
println(1.into[String])
println("1.0".into[Double])
println(2.0.into[Int])
println(Foo("2.0", 3, 4.0, Some(9), Some(22), "str2", Some("str3")).into[Bar])
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment