1
+ trait 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
+
21
+ // obj.copy(a = obj.a.copy(b = a.b.copy(c = v)))
22
+ def setterBody (obj : Term , value : Term , parts : List [String ]): Term = {
23
+ // o.copy(field = value)
24
+ def helper (obj : Term , value : Term , field : String ): Term =
25
+ Term .Select .overloaded(obj, " copy" , Nil , Term .NamedArg (field, value) :: Nil )
26
+
27
+ parts match {
28
+ case field :: Nil => helper(obj, value, field)
29
+ case field :: parts =>
30
+ helper(obj, setterBody(Term .Select .unique(obj, field), value, parts), field)
31
+ }
32
+ }
33
+
34
+ object Path {
35
+ private def recur (tree : Term , selects : List [String ]): Option [(Term , List [String ])] = tree match {
36
+ case Term .Ident (_) if selects.nonEmpty => Some ((tree, selects))
37
+ case Term .Select (qual, name) => recur(qual, name :: selects)
38
+ case _ => None
39
+ }
40
+
41
+ def unapply (t : Term ): Option [(Term , List [String ])] = recur(t, Nil )
42
+ }
43
+
44
+ object Function {
45
+ def unapply (t : Term ): Option [(List [ValDef ], Term )] = t match {
46
+ case Term .Inlined (
47
+ None , Nil ,
48
+ Term .Block (
49
+ (ddef @ DefDef (_, Nil , params :: Nil , _, Some (body))) :: Nil ,
50
+ Term .Lambda (meth, _)
51
+ )
52
+ ) if meth.symbol == ddef.symbol => Some ((params, body))
53
+ case _ => None
54
+ }
55
+ }
56
+
57
+ // exception: getter.unseal.underlyingArgument
58
+ getter.unseal match {
59
+ case Function (param :: Nil , Path (o, parts)) if o.symbol == param.symbol =>
60
+ ' {
61
+ val setter = (t : T ) => (s : S ) => $ { setterBody((' s ).unseal, (' t ).unseal, parts).seal[S ] }
62
+ apply($getter)(setter)
63
+ }
64
+ case _ =>
65
+ throw new QuoteError (" Unsupported syntax. Example: `GenLens[Address](_.streetNumber)`" )
66
+ }
67
+ }
68
+ }
69
+
70
+ object GenLens {
71
+ /** case class Address(streetNumber: Int, streetName: String)
72
+ *
73
+ * GenLens[Address](_.streetNumber) ~~>
74
+ *
75
+ * Lens[Address, Int](_.streetNumber)(n => a => a.copy(streetNumber = n))
76
+ */
77
+
78
+ def apply [S ] = new MkGenLens [S ]
79
+ class MkGenLens [S ] {
80
+ inline def apply [T ](get : => (S => T )): Lens [S , T ] = $ { Lens .impl(' get ) }
81
+ }
82
+ }
83
+
84
+ trait Iso [S , A ] {
85
+ def from (a : A ): S
86
+ def to (s : S ): A
87
+ }
88
+
89
+ object Iso {
90
+ def apply [S , A ](_from : A => S )(_to : S => A ): Iso [S , A ] = new Iso {
91
+ def from (a : A ): S = _from(a)
92
+ def to (s : S ): A = _to(s)
93
+ }
94
+
95
+ def impl [S : Type , A : Type ](implicit refl : Reflection ): Expr [Iso [S , A ]] = {
96
+ import refl ._
97
+ import util ._
98
+ import quoted .Toolbox .Default ._
99
+
100
+ val tpS = typeOf[S ]
101
+ val tpA = typeOf[A ]
102
+
103
+ // 1. S must be a case class
104
+ // 2. A must be a tuple
105
+ // 3. The parameters of S must match A
106
+ if (tpS.classSymbol.flatMap(cls => if (cls.flags.is(Flags .Case )) Some (true ) else None ).isEmpty)
107
+ throw new QuoteError (" Only support generation for case classes" )
108
+
109
+ val cls = tpS.classSymbol.get
110
+
111
+ val companion = tpS match {
112
+ case Type .SymRef (sym, prefix) => Type .TermRef (prefix, sym.name)
113
+ case Type .TypeRef (name, prefix) => Type .TermRef (prefix, name)
114
+ }
115
+
116
+ if (cls.caseFields.size != 1 )
117
+ throw new QuoteError (" Use GenIso.fields for case classes more than one parameter" )
118
+
119
+ val fieldTp = tpS.memberType(cls.caseFields.head)
120
+ if (! (fieldTp =:= tpA))
121
+ throw new QuoteError (s " The type of case class field $fieldTp does not match $tpA" )
122
+
123
+ ' {
124
+ // (p: S) => p._1
125
+ val to = (p : S ) => $ { Term .Select .unique((' p ).unseal, " _1" ).seal[A ] }
126
+ // (p: A) => S(p)
127
+ val from = (p : A ) => $ { Term .Select .overloaded(Term .Ident (companion), " apply" , Nil , (' p ).unseal :: Nil ).seal[S ] }
128
+ apply(from)(to)
129
+ }
130
+ }
131
+
132
+ def implUnit [S : Type ](implicit refl : Reflection ): Expr [Iso [S , 1 ]] = {
133
+ import refl ._
134
+ import util ._
135
+ import quoted .Toolbox .Default ._
136
+
137
+ val tpS = typeOf[S ]
138
+
139
+ if (tpS.isSingleton) {
140
+ val ident = Term .Ident (tpS.asInstanceOf [TermRef ]).seal[S ]
141
+ ' {
142
+ Iso [S , 1 ](Function .const($ident))(Function .const(1 ))
143
+ }
144
+ }
145
+ else if (tpS.classSymbol.flatMap(cls => if (cls.flags.is(Flags .Case )) Some (true ) else None ).nonEmpty) {
146
+ val cls = tpS.classSymbol.get
147
+
148
+ if (cls.caseFields.size != 0 )
149
+ throw new QuoteError (" Use GenIso.fields for case classes more than one parameter" )
150
+
151
+ val companion = tpS match {
152
+ case Type .SymRef (sym, prefix) => Type .TermRef (prefix, sym.name)
153
+ case Type .TypeRef (name, prefix) => Type .TermRef (prefix, name)
154
+ }
155
+
156
+ val obj = Term .Select .overloaded(Term .Ident (companion), " apply" , Nil , Nil ).seal[S ]
157
+
158
+ ' {
159
+ Iso [S , 1 ](Function .const($obj))(Function .const(1 ))
160
+ }
161
+ }
162
+ else {
163
+ throw new QuoteError (" Only support generation for case classes or singleton types" )
164
+ }
165
+ }
166
+
167
+ // TODO: require whitebox macro
168
+ def implFields [S : Type ](implicit refl : Reflection ): Expr [Iso [S , Any ]] = ???
169
+ }
170
+
171
+ object GenIso {
172
+ /**
173
+ * GenIso[Person, String] ~~>
174
+ *
175
+ * Iso[Person, String]
176
+ * { p => p._1 }
177
+ * { p => Person(p) }
178
+ */
179
+ inline def apply [S , A ]: Iso [S , A ] = $ { Iso .impl[S , A ] }
180
+
181
+ // TODO: require whitebox macro
182
+ inline def fields [S ]: Iso [S , Any ] = $ { Iso .implFields[S ] }
183
+
184
+ inline def unit [S ]: Iso [S , 1 ] = $ { Iso .implUnit[S ] }
185
+ }
186
+
187
+ trait Prism [S , A ] { outer =>
188
+ def getOption (s : S ): Option [A ]
189
+ def apply (a : A ): S
190
+
191
+ def composeIso [B ](iso : Iso [A , B ]): Prism [S , B ] = new Prism {
192
+ def getOption (s : S ): Option [B ] = outer.getOption(s).map(a => iso.to(a))
193
+ def apply (b : B ): S = outer(iso.from(b))
194
+ }
195
+ }
196
+
197
+ object Prism {
198
+ def apply [S , A ](getOpt : S => Option [A ])(app : A => S ): Prism [S , A ] = new Prism {
199
+ def getOption (s : S ): Option [A ] = getOpt(s)
200
+ def apply (a : A ): S = app(a)
201
+ }
202
+
203
+ def impl [S : Type , A <: S : Type ](implicit refl : Reflection ): Expr [Prism [S , A ]] = {
204
+ import refl ._
205
+ import util ._
206
+
207
+ ' {
208
+ val get = (p : S ) => if (p.isInstanceOf [A ]) Some (p.asInstanceOf [A ]) else None
209
+ val app = (p : A ) => p
210
+ apply(get)(app)
211
+ }
212
+ }
213
+ }
214
+
215
+ object GenPrism {
216
+ /**
217
+ * GenPrism[Json, JStr] ~~>
218
+ *
219
+ * Prism[Json, JStr]{
220
+ * case JStr(v) => Some(v)
221
+ * case _ => None
222
+ * }(jstr => jstr)
223
+ */
224
+ inline def apply [S , A <: S ]: Prism [S , A ] = $ { Prism .impl[S , A ] }
225
+ }
0 commit comments