|
| 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 Quotes): Expr[T] |
| 7 | + |
| 8 | +object Lft { |
| 9 | + given Lft[Int] with |
| 10 | + def toExpr(x: Int)(using 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 Quotes, Type[T]): Expr[Lft[T]] = { |
| 15 | + val tpe = summonExprOrError[Type[T]] |
| 16 | + mirrorExpr match { |
| 17 | + case '{ $mirrorExpr : Mirror.Sum { type MirroredElemTypes = mirroredElemTypes } } => |
| 18 | + val liftables = elemTypesLfts[mirroredElemTypes] |
| 19 | + val liftablesFn = '{ (x: Int) => ${ switchExpr('x, liftables) } } |
| 20 | + '{ new LiftableSum[T, mirroredElemTypes]($mirrorExpr, $liftablesFn)(using $tpe) } |
| 21 | + case '{ $mirrorExpr : Mirror.Product { type MirroredElemTypes = mirroredElemTypes } } => |
| 22 | + val liftableExprs = Expr.ofSeq(elemTypesLfts[mirroredElemTypes]) |
| 23 | + '{ new LiftableProduct[T, mirroredElemTypes]($mirrorExpr, $liftableExprs)(using $tpe) } |
| 24 | + } |
| 25 | + } |
| 26 | + |
| 27 | + class LiftableSum[T, MElemTypes]( |
| 28 | + mirror: Mirror.Sum { type MirroredElemTypes = MElemTypes; type MirroredMonoType = T }, |
| 29 | + liftables: Int => Lft[_] |
| 30 | + )(using Type[T]) extends Lft[T]: |
| 31 | + def toExpr(x: T)(using Quotes): Expr[T] = |
| 32 | + val ordinal = mirror.ordinal(x) |
| 33 | + val liftable = liftables.apply(ordinal).asInstanceOf[Lft[T]] |
| 34 | + liftable.toExpr(x) |
| 35 | + end LiftableSum |
| 36 | + |
| 37 | + class LiftableProduct[T, MElemTypes]( |
| 38 | + mirror: Mirror.Product { type MirroredElemTypes = MElemTypes; type MirroredMonoType = T }, |
| 39 | + liftables: Seq[Lft[_]] |
| 40 | + )(using Type[T]) extends Lft[T]: |
| 41 | + def toExpr(x: T)(using Quotes): Expr[T] = |
| 42 | + val mirrorExpr = summonExprOrError[Mirror.ProductOf[T]] |
| 43 | + val xProduct = x.asInstanceOf[Product] |
| 44 | + val anyLiftables = liftables.asInstanceOf[Seq[Lft[Any]]] |
| 45 | + val elemExprs = |
| 46 | + xProduct.productIterator.zip(anyLiftables.iterator).map { |
| 47 | + (elem, lift) => lift.toExpr(elem) |
| 48 | + }.toSeq |
| 49 | + val elemsTupleExpr = Expr.ofTupleFromSeq(elemExprs) |
| 50 | + '{ $mirrorExpr.fromProduct($elemsTupleExpr) } |
| 51 | + end LiftableProduct |
| 52 | + |
| 53 | + private def elemTypesLfts[X: Type](using Quotes): List[Expr[Lft[_]]] = |
| 54 | + Type.of[X] match |
| 55 | + case '[ head *: tail ] => summonExprOrError[Lft[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 | + |
| 64 | + private def summonExprOrError[T: Type](using Quotes): Expr[T] = |
| 65 | + Expr.summon[T] match |
| 66 | + case Some(expr) => expr |
| 67 | + case None => |
| 68 | + quotes.reflect.report.throwError(s"Could not find implicit ${Type.show[T]}") |
| 69 | + |
| 70 | + private def switchExpr(scrutinee: Expr[Int], seq: List[Expr[Lft[_]]])(using Quotes): Expr[Lft[_]] = |
| 71 | + import quotes.reflect._ |
| 72 | + val cases = seq.zipWithIndex.map { |
| 73 | + (expr, i) => CaseDef(Literal(IntConstant(i)), None, expr.asTerm) |
| 74 | + } |
| 75 | + Match(scrutinee.asTerm, cases).asExprOf[Lft[_]] |
| 76 | + |
| 77 | +} |
0 commit comments