这是对无形和注解的跟进。最初的问题是在Scala 2和Shapeless2的上下文中提出的。Shapeless2中的一些特性被迁移到Shapeless3中,例如注释。问题是,如何将解决方案迁移到Shapeless3?特别是围绕Poly2的代码
下面是要迁移到Shapeless3的解决方案的副本/粘贴:
import shapeless.ops.hlist.{RightFolder, Zip}
import shapeless.{::, Annotations, Generic, HList, HNil, Lazy, Poly2}
import scala.annotation.StaticAnnotation
object App {
case class MyAnnotation(func: String) extends StaticAnnotation
object Collector extends Poly2 {
// implicit def myCase[ACC <: HList, E] = at[(E, Option[PII]), ACC] {
// case ((e, None), acc) => e :: acc
// case ((e, Some(MyAnnotation(func))), acc) => {
// println(func)
// e :: acc
// }
// }
implicit def someCase[ACC <: HList, E]: Case.Aux[(E, Some[MyAnnotation]), ACC, E :: ACC] = at {
case ((e, Some(MyAnnotation(func))), acc) =>
println(func)
e :: acc
}
implicit def noneCase[ACC <: HList, E]: Case.Aux[(E, None.type), ACC, E :: ACC] = at {
case ((e, None), acc) => e :: acc
}
}
trait Modifier[T] {
def modify(t: T): T
}
implicit def hListModifier[HL <: HList]: Modifier[HL] = identity(_)
// added as an example, you should replace this with your Modifier for HList
implicit def genericModifier[T, HL <: HList, AL <: HList, ZL <: HList](implicit
gen: Generic.Aux[T, HL],
ser: Lazy[Modifier[HL]],
annots: Annotations.Aux[MyAnnotation, T, AL],
zip: Zip.Aux[HL :: AL :: HNil, ZL],
rightFolder: RightFolder.Aux[ZL, HNil/*.type*/, Collector.type, HL /*added*/]
): Modifier[T] = new Modifier[T] {
override def modify(t: T): T = {
val generic = gen.to(t)
println(generic)
val annotations = annots()
println(annotations)
val zipped = zip(generic :: annotations :: HNil)
println(zipped)
val modified = zipped.foldRight(HNil : HNil /*added*/)(Collector)
println(modified)
val typed = gen.from(modified)
typed
}
}
case class Test(a: String, @MyAnnotation("sha1") b: String)
val test = Test("A", "B")
val modifier: Modifier[Test] = implicitly[Modifier[Test]]
def main(args: Array[String]): Unit = {
val test1 = modifier.modify(test) // prints "sha1"
println(test1) // Test(A,B)
}
}发布于 2022-11-08 11:40:36
在Scala 3中,Tuple用于HList,Mirror用于Generic/LabelledGeneric。存在多态函数,但它们是参数多态多态,而不是像Poly那样的ad多态多态.
无型3有Annotations、Typeable和派生工具(包装Mirror)。
实现缺失部分(Generic、Coproduct、Poly、type类等)并不难。
import shapeless3.deriving.Annotations
import scala.deriving.Mirror
import scala.util.NotGiven
import scala.annotation.StaticAnnotation
//================= GENERIC ====================
trait Generic[T] {
type Repr
def to(t: T): Repr
def from(r: Repr): T
}
object Generic {
type Aux[T, Repr0] = Generic[T] {type Repr = Repr0}
def instance[T, Repr0](f: T => Repr0, g: Repr0 => T): Aux[T, Repr0] =
new Generic[T] {
override type Repr = Repr0
override def to(t: T): Repr0 = f(t)
override def from(r: Repr0): T = g(r)
}
object ops {
extension[A] (a: A) {
def toRepr(using g: Generic[A]): g.Repr = g.to(a)
}
extension[Repr] (a: Repr) {
def to[A](using g: Generic.Aux[A, Repr]): A = g.from(a)
}
}
given [T <: Product](using
// ev: NotGiven[T <:< Tuple],
// ev1: NotGiven[T <:< Coproduct],
m: Mirror.ProductOf[T],
m1: Mirror.ProductOf[m.MirroredElemTypes]
): Aux[T, m.MirroredElemTypes] = instance(
m1.fromProduct(_),
m.fromProduct(_)
)
// given[T, C <: Coproduct](using
// // ev: NotGiven[T <:< Tuple],
// // ev1: NotGiven[T <:< Coproduct],
// m: Mirror.SumOf[T],
// ev2: Coproduct.ToCoproduct[m.MirroredElemTypes] =:= C
// ): Generic.Aux[T, C/*Coproduct.ToCoproduct[m.MirroredElemTypes]*/] = {
// instance(
// t => Coproduct.unsafeToCoproduct(m.ordinal(t), t).asInstanceOf[C],
// Coproduct.unsafeFromCoproduct(_).asInstanceOf[T]
// )
// }
}
//================= COPRODUCT ====================
//sealed trait Coproduct extends Product with Serializable
//sealed trait +:[+H, +T <: Coproduct] extends Coproduct
//final case class Inl[+H, +T <: Coproduct](head: H) extends (H +: T)
//final case class Inr[+H, +T <: Coproduct](tail: T) extends (H +: T)
//sealed trait CNil extends Coproduct
//
//object Coproduct {
// def unsafeToCoproduct(length: Int, value: Any): Coproduct =
// (0 until length).foldLeft[Coproduct](Inl(value))((c, _) => Inr(c))
//
// @scala.annotation.tailrec
// def unsafeFromCoproduct(c: Coproduct): Any = c match {
// case Inl(h) => h
// case Inr(c) => unsafeFromCoproduct(c)
// case _: CNil => sys.error("impossible")
// }
//
// type ToCoproduct[T <: Tuple] <: Coproduct = T match {
// case EmptyTuple => CNil
// case h *: t => h +: ToCoproduct[t]
// }
//
// type ToTuple[C <: Coproduct] <: Tuple = C match {
// case CNil => EmptyTuple
// case h +: t => h *: ToTuple[t]
// }
//}
//================= POLY ====================
trait Cases {
type Case1[Fn, A] = poly.Case[Fn, A *: EmptyTuple]
object Case1 {
type Aux[Fn, A, Result] = poly.Case.Aux[Fn, A *: EmptyTuple, Result]
def apply[Fn, A, Result](fn: A => Result): Case1.Aux[Fn, A, Result] =
poly.Case { case a *: EmptyTuple => fn(a) }
}
type Case2[Fn, A, B] = poly.Case[Fn, A *: B *: EmptyTuple]
object Case2 {
type Aux[Fn, A, B, Result] = poly.Case.Aux[Fn, A *: B *: EmptyTuple, Result]
def apply[Fn, A, B, Result](fn: (A, B) => Result): Case2.Aux[Fn, A, B, Result] =
poly.Case { case a *: b *: EmptyTuple => fn(a, b) }
}
}
trait CaseInst {
given inst1[Fn <: Poly, A, Res]: Conversion[poly.Case.Aux[Fn, A *: EmptyTuple, Res], A => Res] =
cse => a => cse.value(a *: EmptyTuple)
given inst2[Fn <: Poly, A, B, Res]: Conversion[poly.Case.Aux[Fn, A *: B *: EmptyTuple, Res], (A, B) => Res] =
cse => (a, b) => cse.value(a *: b *: EmptyTuple)
}
object poly extends Cases {
trait Case[P, L <: Tuple] {
type Result
val value: L => Result
def apply(t: L): Result = value(t)
def apply()(using ev: EmptyTuple =:= L): Result = value(EmptyTuple)
def apply[T](t: T)(using ev: (T *: EmptyTuple) =:= L): Result = value(t *: EmptyTuple)
def apply[T, U](t: T, u: U)(using ev: (T *: U *: EmptyTuple) =:= L): Result = value(t *: u *: EmptyTuple)
}
object Case extends CaseInst {
type Aux[P, L <: Tuple, Result0] = Case[P, L] {type Result = Result0}
def apply[P, L <: Tuple, R](v: L => R): Aux[P, L, R] = new Case[P, L] {
type Result = R
val value = v
}
}
}
trait PolyApply {
type λ <: Singleton
def apply[A](a: A)(using cse: poly.Case[λ, A *: EmptyTuple]): cse.Result = cse(a *: EmptyTuple)
def apply[A, B](a: A, b: B)(using cse: poly.Case[λ, A *: B *: EmptyTuple]): cse.Result = cse(a *: b *: EmptyTuple)
}
trait Poly extends PolyApply {
type λ = this.type
type ProductCase[L <: Tuple] = poly.Case[this.type, L]
object ProductCase extends Serializable {
type Aux[L <: Tuple, Result0] = ProductCase[L] {type Result = Result0}
def apply[L <: Tuple, R](v: L => R) = new ProductCase[L] {
type Result = R
val value = v
}
}
def apply[R](using c: ProductCase.Aux[EmptyTuple, R]): R = c()
}
trait PolyInst {
implicit def inst0(p: Poly)(implicit cse: p.ProductCase[EmptyTuple]): cse.Result = cse()
implicit def inst1[A](fn: Poly)(implicit cse: fn.ProductCase[A *: EmptyTuple]): A => cse.Result =
a => cse(a *: EmptyTuple)
implicit def inst2[A, B](fn: Poly)(implicit cse: fn.ProductCase[A *: B *: EmptyTuple]): (A, B) => cse.Result =
(a, b) => cse(a *: b *: EmptyTuple)
}
object Poly extends PolyInst
trait Poly0 extends Poly {
type Case0[T] = ProductCase.Aux[EmptyTuple, T]
def at[T](t: T) = new ProductCase[EmptyTuple] {
type Result = T
val value = _ => t
}
}
trait Poly1 extends Poly { self =>
type Case[A] = poly.Case[self.type, A *: EmptyTuple]
object Case {
type Aux[A, Result0] = poly.Case.Aux[self.type, A *: EmptyTuple, Result0]
}
class CaseBuilder1[A] {
def apply[Res](fn: A => Res): Case.Aux[A, Res] = poly.Case { case a *: EmptyTuple => fn(a) }
}
def at[A]: CaseBuilder1[A] = new CaseBuilder1[A]
}
trait Poly2 extends Poly { self =>
type Case[A, B] = poly.Case[self.type, A *: B *: EmptyTuple]
object Case {
type Aux[A, B, Result0] = poly.Case.Aux[self.type, A *: B *: EmptyTuple, Result0]
}
class CaseBuilder2[A, B] {
def apply[Res](fn: (A, B) => Res): Case.Aux[A, B, Res] = poly.Case { case a *: b *: EmptyTuple => fn(a, b) }
}
def at[A, B]: CaseBuilder2[A, B] = new CaseBuilder2[A, B]
}
//================= TYPE CLASSES ====================
trait DepFn0 {
type Out
def apply(): Out
}
trait DepFn1[T] {
type Out
def apply(t: T): Out
}
trait DepFn2[T, U] {
type Out
def apply(t: T, u: U): Out
}
trait ConstMapper[C, L <: Tuple] extends DepFn2[C, L] {
type Out <: Tuple
}
object ConstMapper {
def apply[C, L <: Tuple](using mapper: ConstMapper[C, L]): Aux[C, L, mapper.Out] = mapper
type Aux[C, L <: Tuple, Out0 <: Tuple] = ConstMapper[C, L] {type Out = Out0}
given hnilConstMapper[C]: Aux[C, EmptyTuple, EmptyTuple] =
new ConstMapper[C, EmptyTuple] {
type Out = EmptyTuple
def apply(c: C, l: EmptyTuple): Out = l
}
given hlistConstMapper[H, T <: Tuple, C, OutT <: Tuple]
(using mct: ConstMapper.Aux[C, T, OutT]): Aux[C, H *: T, C *: OutT] =
new ConstMapper[C, H *: T] {
type Out = C *: OutT
def apply(c: C, l: H *: T): Out = c *: mct(c, l.tail)
}
}
trait ZipOne[H <: Tuple, T <: Tuple] extends DepFn2[H, T] {
type Out <: Tuple
}
object ZipOne extends LowPriorityZipOne {
given zipOne0: Aux[EmptyTuple, EmptyTuple, EmptyTuple] =
new ZipOne[EmptyTuple, EmptyTuple] {
type Out = EmptyTuple
def apply(h: EmptyTuple, t: EmptyTuple): Out = EmptyTuple
}
given zipOne3[H, T <: Tuple]: Aux[H *: EmptyTuple, T *: EmptyTuple, (H *: T) *: EmptyTuple] =
new ZipOne[H *: EmptyTuple, T *: EmptyTuple] {
type Out = (H *: T) *: EmptyTuple
def apply(h: H *: EmptyTuple, t: T *: EmptyTuple): Out = (h.head *: t.head) *: EmptyTuple
}
}
trait LowPriorityZipOne {
def apply[H <: Tuple, T <: Tuple](using zip: ZipOne[H, T]): Aux[H, T, zip.Out] = zip
type Aux[H <: Tuple, T <: Tuple, Out0 <: Tuple] = ZipOne[H, T] {type Out = Out0}
given zipOne1[H <: Tuple]: Aux[H, EmptyTuple, EmptyTuple] =
new ZipOne[H, EmptyTuple] {
type Out = EmptyTuple
def apply(h: H, t: EmptyTuple): Out = EmptyTuple
}
given zipOne2[T <: Tuple]: Aux[EmptyTuple, T, EmptyTuple] =
new ZipOne[EmptyTuple, T] {
type Out = EmptyTuple
def apply(h: EmptyTuple, t: T): Out = EmptyTuple
}
given zipOne4[HH, HT <: Tuple, TH <: Tuple, TT <: Tuple, ZotOut <: Tuple]
(using zot: ZipOne.Aux[HT, TT, ZotOut], ev: Tuple.Head[TH *: TT] =:= TH /*???*/): Aux[HH *: HT, TH *: TT, (HH *: TH) *: ZotOut] =
new ZipOne[HH *: HT, TH *: TT] {
type Out = (HH *: TH) *: ZotOut
def apply(h: HH *: HT, t: TH *: TT): Out = (h.head *: ev(t.head)) *: zot(h.tail, t.tail)
}
}
trait Transposer[L <: Tuple] extends DepFn1[L] {
type Out <: Tuple
}
object Transposer {
def apply[L <: Tuple](using transposer: Transposer[L]): Aux[L, transposer.Out] = transposer
type Aux[L <: Tuple, Out0 <: Tuple] = Transposer[L] {type Out = Out0}
given hnilTransposer: Aux[EmptyTuple, EmptyTuple] =
new Transposer[EmptyTuple] {
type Out = EmptyTuple
def apply(l: EmptyTuple): Out = l
}
given hlistTransposer1[H <: Tuple, MC <: Tuple, Out0 <: Tuple]
(using mc: ConstMapper.Aux[EmptyTuple, H, MC], zo: ZipOne.Aux[H, MC, Out0]): Aux[H *: EmptyTuple, Out0] =
new Transposer[H *: EmptyTuple] {
type Out = Out0
def apply(l: H *: EmptyTuple): Out = zo(l.head, mc(EmptyTuple, l.head))
}
given hlistTransposer2[H <: Tuple, TH <: Tuple, TT <: Tuple, OutT <: Tuple, Out0 <: Tuple]
(using tt: Aux[TH *: TT, OutT], zo: ZipOne.Aux[H, OutT, Out0]): Aux[H *: TH *: TT, Out0] =
new Transposer[H *: TH *: TT] {
type Out = Out0
def apply(l: H *: TH *: TT): Out = zo(l.head, tt(l.tail))
}
}
trait Zip[L <: Tuple] extends DepFn1[L] {
type Out <: Tuple
}
object Zip {
def apply[L <: Tuple](using zip: Zip[L]): Aux[L, zip.Out] = zip
type Aux[L <: Tuple, Out0 <: Tuple] = Zip[L] {type Out = Out0}
given zipper[L <: Tuple, OutT <: Tuple]
(using
transposer: Transposer.Aux[L, OutT]
): Aux[L, OutT] =
new Zip[L] {
type Out = OutT
def apply(l: L): Out = l.transpose
}
}
extension [L <: Tuple](l: L) {
def transpose(using transpose: Transposer[L]): transpose.Out = transpose(l)
def foldRight[R](z : R)(op : Poly)(using folder: RightFolder[L, R, op.type]): folder.Out = folder(l, z)
}
trait RightFolder[L <: Tuple, In, HF] extends DepFn2[L, In]
object RightFolder {
def apply[L <: Tuple, In, F](using folder: RightFolder[L, In, F]): Aux[L, In, F, folder.Out] = folder
type Aux[L <: Tuple, In, HF, Out0] = RightFolder[L, In, HF] {type Out = Out0}
given hnilRightFolder[In, HF]: Aux[EmptyTuple, In, HF, In] =
new RightFolder[EmptyTuple, In, HF] {
type Out = In
def apply(l: EmptyTuple, in: In): Out = in
}
given hlistRightFolder[H, T <: Tuple, In, HF, OutT]
(using ft: RightFolder.Aux[T, In, HF, OutT], f: poly.Case2[HF, H, OutT]): Aux[H *: T, In, HF, f.Result] =
new RightFolder[H *: T, In, HF] {
type Out = f.Result
def apply(l: H *: T, in: In): Out = f(l.head, ft(l.tail, in))
}
}
//================= YOUR SETTING ====================
case class MyAnnotation(func: String) extends StaticAnnotation
object Collector extends Poly2 {
given [ACC <: Tuple, E]: Case.Aux[(E, Some[MyAnnotation]), ACC, E *: ACC] = at {
case ((e, Some(MyAnnotation(func))), acc) =>
println(func)
e *: acc
}
given [ACC <: Tuple, E]: Case.Aux[(E, None.type), ACC, E *: ACC] = at {
case ((e, None), acc) => e *: acc
}
}
trait Modifier[T] {
def modify(t: T): T
}
given hListModifier[HL <: Tuple]: Modifier[HL] = identity(_)
// added as an example, you should replace this with your Modifier for HList
given genericModifier[T, HL <: Tuple, AL <: Tuple, ZL <: Tuple](using
gen: Generic.Aux[T, HL],
ser: /*Lazy[*/Modifier[HL]/*]*/,
annots: Annotations.Aux[MyAnnotation, T, AL],
zip: Zip.Aux[HL *: AL *: EmptyTuple, ZL],
rightFolder: RightFolder.Aux[ZL, EmptyTuple, Collector.type, HL]
): Modifier[T] = new Modifier[T] {
override def modify(t: T): T = {
val generic = gen.to(t)
println(generic)
val annotations = annots()
println(annotations)
val zipped = zip(generic *: annotations *: EmptyTuple)
println(zipped)
val modified = zipped.foldRight(EmptyTuple)(Collector)
println(modified)
val typed = gen.from(modified)
typed
}
}
case class Test(a: String, @MyAnnotation("sha1") b: String)
val test = Test("A", "B")
val modifier: Modifier[Test] = summon[Modifier[Test]]
@main def run = {
val test1 = modifier.modify(test) // prints "sha1"
println(test1) // Test(A,B)
}实现Lazy可能很棘手。还不清楚是否需要。这里有名隐式,但它们并不等同于Lazy (1 2)。原则上,Lazy可以在Scala 3中实现,因为Scala 3中内嵌的编译器内部与Scala 2 (1 2 3.)相似。
https://stackoverflow.com/questions/74355212
复制相似问题