|
| 1 | +import scala.compiletime.{erasedValue, summonFrom} |
| 2 | +import scala.deriving._ |
| 3 | +import scala.quoted._ |
| 4 | + |
| 5 | +trait Lft[T]: |
| 6 | + def toExpr(x: T)(using Type[T], Quotes): Expr[T] // TODO remove `Type[T]` |
| 7 | + |
| 8 | +object Lft { |
| 9 | + given Lft[Int] with |
| 10 | + def toExpr(x: Int)(using Type[Int], Quotes) = Expr(x) |
| 11 | + |
| 12 | + inline given derived[T](using inline m: Mirror.Of[T]): Lft[T] = ${ derivedExpr('m) } |
| 13 | + |
| 14 | + private def derivedExpr[T](mirrorExpr: Expr[Mirror.Of[T]])(using qctx: Quotes, tpe: Type[T]): Expr[Lft[T]] = { |
| 15 | + mirrorExpr match { |
| 16 | + case '{ $mirrorExpr : Mirror.Sum { type MirroredElemTypes = mirroredElemTypes } } => |
| 17 | + val liftables = Expr.ofSeq(elemTypesLfts[mirroredElemTypes]) |
| 18 | + '{ new LiftableSum[T, mirroredElemTypes]($mirrorExpr, $liftables) } |
| 19 | + case '{ $mirrorExpr : Mirror.Product { type MirroredElemTypes = mirroredElemTypes } } => |
| 20 | + val liftableExprs = Expr.ofSeq(elemTypesLfts[mirroredElemTypes]) |
| 21 | + '{ new LiftableProduct[T, mirroredElemTypes]($mirrorExpr, $liftableExprs) } |
| 22 | + } |
| 23 | + } |
| 24 | + |
| 25 | + class LiftableSum[T, MElemTypes]( |
| 26 | + mirror: Mirror.Sum { type MirroredElemTypes = MElemTypes; type MirroredMonoType = T }, |
| 27 | + liftables: Seq[Lft[_]] // TODO make Lft creation lazy |
| 28 | + ) extends Lft[T]: |
| 29 | + def toExpr(x: T)(using Type[T], Quotes): Expr[T] = |
| 30 | + val ordinal = mirror.ordinal(x) |
| 31 | + val tp = Expr.summon[Mirror.SumOf[T]].get match |
| 32 | + case '{ $mirrorExpr : Mirror.Sum { type MirroredElemTypes = mirroredElemTypes } } => |
| 33 | + elemType[mirroredElemTypes](ordinal) |
| 34 | + val liftable = liftables.apply(ordinal).asInstanceOf[Lft[T]] |
| 35 | + liftable.toExpr(x)(using tp.asInstanceOf[Type[T]], summon[Quotes]) |
| 36 | + end LiftableSum |
| 37 | + |
| 38 | + class LiftableProduct[T, MElemTypes]( |
| 39 | + mirror: Mirror.Product { type MirroredElemTypes = MElemTypes; type MirroredMonoType = T }, |
| 40 | + liftables: Seq[Lft[_]] |
| 41 | + ) extends Lft[T]: |
| 42 | + def toExpr(x: T)(using Type[T], Quotes): Expr[T] = |
| 43 | + val mirrorExpr = Expr.summon[Mirror.ProductOf[T]].get |
| 44 | + val elemExprs = |
| 45 | + x.asInstanceOf[Product].productIterator.zip(liftables.iterator).map { (elem, lift) => |
| 46 | + lift.asInstanceOf[Lft[Any]].toExpr(elem) |
| 47 | + }.toSeq |
| 48 | + val elemsTupleExpr = Expr.ofTupleFromSeq(elemExprs) |
| 49 | + '{ $mirrorExpr.fromProduct($elemsTupleExpr) } |
| 50 | + end LiftableProduct |
| 51 | + |
| 52 | + private def elemTypesLfts[X: Type](using Quotes): List[Expr[Lft[_]]] = |
| 53 | + Type.of[X] match |
| 54 | + case '[ head *: tail ] => |
| 55 | + Expr.summon[Lft[head]].getOrElse(quotes.reflect.report.throwError(s"Could not find given Lft[${Type.show[head]}]")) :: elemTypesLfts[tail] |
| 56 | + case '[ EmptyTuple ] => Nil |
| 57 | + |
| 58 | + private def elemType[X: Type](ordinal: Int)(using Quotes): Type[_] = |
| 59 | + Type.of[X] match |
| 60 | + case '[ head *: tail ] => |
| 61 | + if ordinal == 0 then Type.of[head] |
| 62 | + else elemType[tail](ordinal - 1) |
| 63 | +} |
0 commit comments