Skip to content

Instantly share code, notes, and snippets.

@francescofrontera
Last active November 11, 2023 05:22
Show Gist options
  • Save francescofrontera/a190b364a769b63d8a44f7a3539a634b to your computer and use it in GitHub Desktop.
Save francescofrontera/a190b364a769b63d8a44f7a3539a634b to your computer and use it in GitHub Desktop.
Apply Aux pattern through Shapeless
import shapeless._, shapeless.ops.hlist._, shapeless.labelled.FieldType
trait Op[A] extends DepFn1[A]
trait LowPriority {
implicit def identity[A]: Op.Aux[A, A] = Op.createInstance(i => i)
}
object Op extends LowPriority {
type Aux[A, OUT0] = Op[A] { type Out = OUT0 }
def createInstance[A, B](f: A => B): Op.Aux[A, B] =
new Op[A] {
type Out = B
def apply(t: A): Out = f(t)
}
implicit val primitiveInt: Op.Aux[Int, String] = createInstance {
case 1 => "UNO"
case 2 => "DUE"
case _ => "Ops.."
}
implicit val hNilOpInstance: Op.Aux[HNil, HNil] = createInstance(_ => HNil)
implicit def hConsInst[
K <: Symbol,
H,
TL <: HList,
TO <: HList,
OUT
](implicit
op: Op.Aux[H, OUT],
opt: Op.Aux[TL, TO]): Op.Aux[
FieldType[K, H] :: TL,
FieldType[K, OUT] :: TO
] = createInstance(i => labelled.field[K](op(i.head)) :: opt(i.tail))
implicit def gen[
S, T,
SR <: HList, TR <: HList,
TK <: HList,
ITS <: HList,
](
implicit
genS: LabelledGeneric.Aux[S, SR],
genT: LabelledGeneric.Aux[T, TR],
map: Op.Aux[SR, TK],
inter: Intersection.Aux[TK, TR, ITS],
align: Align[ITS, TR]
): Op.Aux[S, T] =
createInstance(s => genT.from(align(inter(map(genS.to(s))))))
implicit class OpOps[A](in: A) {
def mapTo[B](implicit op: Op.Aux[A, B]) = op.apply(in)
}
}
object Test extends App {
import To._
import Op._
case class FooExtended(a: (Int, Int), b: (Int, Int), c: (Int, Int))
case class Foo(a: Int, b: Int, c: Int)
val fooExtended: FooExtended = FooExtended((1, 2), (3, 4), (2, 2))
val foo: Foo = fooExtended.last[Foo]
println(s"$fooExtended ~~~ $foo")
// FooExtended((1,2),(3,4),(2,2)) ~~~ Foo(2,4,2)
/* Op Test */
case class Foo(a: Int, b: Int, d: String)
case class FooI(b: String, a: String)
val res = Foo(1, 2, "O.o").mapTo[FooI] //FooI(DUE,UNO)
}
import shapeless._ ,shapeless.ops.hlist._, shapeless.HList.hlistOps
/*
* Working to understand Aux pattern..
* Starting from
* https://stackoverflow.com/questions/43900674/understanding-the-aux-pattern-in-scala-type-system
* to shapeless usage
* */
trait Tuple_2[A] extends DepFn1[A]
object Tuple_2 {
type Aux[A, B0] = Tuple_2[A] { type Out = B0 }
type AId[A] = Aux[A, A]
implicit def scalaTupleInstance[A, C]: Aux[(A, C), C] = new Tuple_2[(A, C)] {
type Out = C
def apply(t: (A, C)): Out = t._2
}
implicit val hnilIns: AId[HNil] = new Tuple_2[HNil] {
type Out = HNil
def apply(t: HNil): Out = t
}
implicit def hListIns[H, TL <: HList, T0, T1 <: HList](
implicit
hhForHead: Tuple_2.Aux[H, T0],
ttForT: Tuple_2.Aux[TL, T1]
): Tuple_2.Aux[H :: TL, T0 :: T1] = new Tuple_2[H :: TL] {
type Out = T0 :: T1
def apply(t: H :: TL): Out = hhForHead(t.head) :: ttForT(t.tail)
}
}
sealed trait To[A, B] {
def apply(in: A): B
}
object To {
implicit def toInstance[
H, H0,
TH <: HList, TH0 <: HList, TH1 <: HList
](
implicit
genA: Generic.Aux[H, TH],
genB: Generic.Aux[H0, TH0],
tuple_2: Lazy[Tuple_2.Aux[TH, TH1]],
intersect: Intersection.Aux[TH1, TH0, TH0]
) = new To[H, H0] {
def apply(in: H): H0 = {
genB.from(
intersect(
tuple_2.value(genA.to(in))
)
)
}
}
implicit class ToOps[A](in: A) {
def last[B](implicit ttL: To[A, B]): B = ttL.apply(in)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment