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