1+ abstract class Lens [S , T ] {
2+ def get (s : S ): T
3+ def set (t : T , s : S ) : S
4+ }
5+
6+ import scala .quoted ._
7+ import scala .tasty ._
8+
9+ object Lens {
10+ def apply [S , T ](_get : S => T )(_set : T => S => S ): Lens [S , T ] = new Lens {
11+ def get (s : S ): T = _get(s)
12+ def set (t : T , s : S ): S = _set(t)(s)
13+ }
14+
15+ def impl [S : Type , T : Type ](getter : Expr [S => T ])(implicit refl : Reflection ): Expr [Lens [S , T ]] = {
16+ import refl ._
17+ import util ._
18+ import quoted .Toolbox .Default ._
19+
20+ // obj.copy(field = value)
21+ def setterBody (obj : Expr [S ], value : Expr [T ], field : String ): Expr [S ] =
22+ Term .Select .overloaded(obj.unseal, " copy" , Nil , Term .NamedArg (field, value.unseal) :: Nil ).seal[S ]
23+
24+ // exception: getter.unseal.underlyingArgument
25+ getter.unseal match {
26+ case Term .Inlined (
27+ None , Nil ,
28+ Term .Block (
29+ DefDef (_, Nil , (param :: Nil ) :: Nil , _, Some (Term .Select (o, field))) :: Nil ,
30+ Term .Lambda (meth, _)
31+ )
32+ ) if o.symbol == param.symbol =>
33+ ' {
34+ val setter = (t : T ) => (s : S ) => ~ setterBody('(s), ' (t), field)
35+ apply(~ getter)(setter)
36+ }
37+ case _ =>
38+ throw new QuoteError (" Unsupported syntax. Example: `GenLens[Address](_.streetNumber)`" )
39+ }
40+ }
41+ }
42+
43+ object GenLens {
44+ /** case class Address(streetNumber: Int, streetName: String)
45+ *
46+ * GenLens[Address](_.streetNumber) ~~>
47+ *
48+ * Lens[Address, Int](_.streetNumber)(n => a => a.copy(streetNumber = n))
49+ */
50+
51+ def apply [S ] = new MkGenLens [S ]
52+ class MkGenLens [S ] {
53+ inline def apply [T ](get : => (S => T )): Lens [S , T ] = ~ Lens .impl('(get))
54+ }
55+ }
0 commit comments