Skip to content

Commit 7712e1d

Browse files
committed
Add quoted Liftable derivation
1 parent 16a45c4 commit 7712e1d

File tree

3 files changed

+74
-0
lines changed

3 files changed

+74
-0
lines changed
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import scala.compiletime.{erasedValue, summonFrom}
2+
import scala.deriving._
3+
import scala.quoted._
4+
5+
6+
trait Lft[T]:
7+
def toExpr(x: T): QuoteContext ?=> Expr[T]
8+
9+
object Lft {
10+
given Lft[Int]:
11+
def toExpr(x: Int) = Expr(x)
12+
13+
inline given derived[T](using inline m: Mirror.Of[T]) as Lft[T] = ${ derivedExpr('m) }
14+
15+
private def derivedExpr[T](mirrorExpr: Expr[Mirror.Of[T]])(using qctx: QuoteContext, tpe: Type[T]): Expr[Lft[T]] = {
16+
def elemTypesLfts(tp: Type[_]): List[Expr[Lft[_]]] =
17+
tp match
18+
case '[ $head *: $tail ] =>
19+
Expr.summon(using '[Lft[$head]]).getOrElse(qctx.throwError(s"Could not find given Lft[${head.show}]")) :: elemTypesLfts(tail)
20+
case '[ Unit ] => Nil
21+
22+
mirrorExpr match {
23+
case '{ $mirrorExpr : Mirror.Sum { type MirroredElemTypes = $mirroredElemTypes } } =>
24+
val liftableExprs = elemTypesLfts(mirroredElemTypes)
25+
'{
26+
new Lft[T]:
27+
def toExpr(x: T) =
28+
val mirror = $mirrorExpr
29+
val liftable = ${Expr.ofSeq(liftableExprs)}.apply(mirror.ordinal(x)).asInstanceOf[Lft[T]] // TODO generate switch
30+
liftable.toExpr(x)
31+
}
32+
33+
case '{ $mirrorExpr : Mirror.Product { type MirroredElemTypes = $mirroredElemTypes } } =>
34+
val liftableExprs = Expr.ofList(elemTypesLfts(mirroredElemTypes))
35+
'{
36+
new Lft[T]:
37+
def toExpr(x: T) =
38+
val liftables = $liftableExprs
39+
val lifted = Expr.ofSeq(liftables.zipWithIndex.map { (liftable, i) =>
40+
liftable.asInstanceOf[Lft[AnyRef]].toExpr(productElement(x, i))
41+
})
42+
val liftedProduct = '{ new ArrayProduct(Array[AnyRef]($lifted: _*)) }
43+
val mirror = Expr.summon(using '[Mirror.ProductOf[T]]).get
44+
'{ $mirror.fromProduct($liftedProduct) }
45+
}
46+
}
47+
}
48+
49+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
2+
sealed trait Opt[+T] derives Lft
3+
case class Sm[T](t: T) extends Opt[T] derives Lft
4+
case object Nn extends Opt[Nothing] derives Lft
5+
6+
object Lib {
7+
8+
import scala.quoted._
9+
import Opt.{given _}
10+
11+
inline def smTwo = ${smTwoExpr}
12+
inline def none = ${noneExpr}
13+
14+
private def smTwoExpr(using QuoteContext): Expr[Opt[Int]] =
15+
summon[Lft[Sm[Int]]].toExpr(Sm(2))
16+
17+
private def noneExpr(using QuoteContext): Expr[Opt[Int]] =
18+
summon[Lft[Nn.type]].toExpr(Nn)
19+
}
20+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
object Test extends App {
2+
import Opt._
3+
assert(Lib.smTwo == Sm(2))
4+
assert(Lib.none == Nn)
5+
}

0 commit comments

Comments
 (0)