Skip to content

Commit 5e1eb67

Browse files
committed
Add neg test for GenLens
1 parent 8f5556e commit 5e1eb67

File tree

2 files changed

+66
-0
lines changed

2 files changed

+66
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
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+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
case class Address(streetNumber: Int, streetName: String)
2+
3+
object Test {
4+
def main(args: Array[String]): Unit = {
5+
val len = GenLens[Address](_.streetNumber + 3) // error
6+
val address = Address(10, "High Street")
7+
assert(len.get(address) == 10)
8+
val addr2 = len.set(5, address)
9+
assert(len.get(addr2) == 5)
10+
}
11+
}

0 commit comments

Comments
 (0)