Last active
November 11, 2023 05:22
-
-
Save francescofrontera/a190b364a769b63d8a44f7a3539a634b to your computer and use it in GitHub Desktop.
Apply Aux pattern through Shapeless
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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