Skip to content

Commit 678bb4f

Browse files
committed
Add quoted Liftable derivation
1 parent adc44da commit 678bb4f

File tree

3 files changed

+75
-0
lines changed

3 files changed

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