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