From 82274593ee4ce736de132b693f93334bad2d3ed1 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Mon, 18 Feb 2019 21:50:09 +0100 Subject: [PATCH 01/13] Fix #5941: implement GenLens macro --- tests/run-with-compiler/i5941/macro_1.scala | 43 +++++++++++++++++++++ tests/run-with-compiler/i5941/usage_2.scala | 12 ++++++ 2 files changed, 55 insertions(+) create mode 100644 tests/run-with-compiler/i5941/macro_1.scala create mode 100644 tests/run-with-compiler/i5941/usage_2.scala diff --git a/tests/run-with-compiler/i5941/macro_1.scala b/tests/run-with-compiler/i5941/macro_1.scala new file mode 100644 index 000000000000..6154706bc75f --- /dev/null +++ b/tests/run-with-compiler/i5941/macro_1.scala @@ -0,0 +1,43 @@ +abstract class Lens[S, T] { + def get(s: S): T + def set(t: T, s: S) :S +} + +import scala.quoted._ +import scala.tasty._ + +object Lens { + def apply[S, T](_get: S => T)(_set: T => S => S): Lens[S, T] = new Lens { + def get(s: S): T = _get(s) + def set(t: T, s: S): S = _set(t)(s) + } + + /** case class Address(streetNumber: Int, streetName: String) + * + * Lens.gen[Address, Int](_.streetNumber) ~~> + * + * Lens[Address, Int](_.streetNumber)(n => a => a.copy(streetNumber = n)) + */ + inline def gen[S, T](get: S => T): Lens[S, T] = ~impl('(get)) + + def impl[S: Type, T: Type](getter: Expr[S => T])(implicit refl: Reflection): Expr[Lens[S, T]] = { + import refl._ + import util._ + import quoted.Toolbox.Default._ + + // obj.copy(field = value) + def setterBody(obj: Expr[S], value: Expr[T], field: String): Expr[S] = + Term.Select.overloaded(obj.unseal, "copy", Nil, Term.NamedArg(field, value.unseal) :: Nil).seal[S] + + getter.unseal.underlyingArgument match { + case Term.Block( + DefDef(_, Nil, (param :: Nil) :: Nil, _, Some(Term.Select(o, field))) :: Nil, + Term.Lambda(meth, _) + ) => + '{ + val setter = (t: T) => (s: S) => ~setterBody('(s), '(t), field) + apply(~getter)(setter) + } + } + } +} diff --git a/tests/run-with-compiler/i5941/usage_2.scala b/tests/run-with-compiler/i5941/usage_2.scala new file mode 100644 index 000000000000..6883acfd555a --- /dev/null +++ b/tests/run-with-compiler/i5941/usage_2.scala @@ -0,0 +1,12 @@ +case class Address(streetNumber: Int, streetName: String) + +object Test { + def main(args: Array[String]): Unit = { + // val len = Lens.gen[Address, Int](_.streetNumber) + val len = Lens.gen[Address, Int]( (a: Address) => a.streetNumber) + val address = Address(10, "High Street") + assert(len.get(address) == 10) + val addr2 = len.set(5, address) + assert(len.get(addr2) == 5) + } +} \ No newline at end of file From aa428eeba15fdd7e8231e50e779904facee9654f Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Mon, 18 Feb 2019 22:12:27 +0100 Subject: [PATCH 02/13] Walkaround type inference problem (thanks @milessabin) --- tests/run-with-compiler/i5941/macro_1.scala | 22 +++++++++++++-------- tests/run-with-compiler/i5941/usage_2.scala | 3 +-- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/tests/run-with-compiler/i5941/macro_1.scala b/tests/run-with-compiler/i5941/macro_1.scala index 6154706bc75f..2fef9413afdb 100644 --- a/tests/run-with-compiler/i5941/macro_1.scala +++ b/tests/run-with-compiler/i5941/macro_1.scala @@ -12,14 +12,6 @@ object Lens { def set(t: T, s: S): S = _set(t)(s) } - /** case class Address(streetNumber: Int, streetName: String) - * - * Lens.gen[Address, Int](_.streetNumber) ~~> - * - * Lens[Address, Int](_.streetNumber)(n => a => a.copy(streetNumber = n)) - */ - inline def gen[S, T](get: S => T): Lens[S, T] = ~impl('(get)) - def impl[S: Type, T: Type](getter: Expr[S => T])(implicit refl: Reflection): Expr[Lens[S, T]] = { import refl._ import util._ @@ -41,3 +33,17 @@ object Lens { } } } + +object GenLens { + /** case class Address(streetNumber: Int, streetName: String) + * + * Lens.gen[Address, Int](_.streetNumber) ~~> + * + * Lens[Address, Int](_.streetNumber)(n => a => a.copy(streetNumber = n)) + */ + + def apply[S] = new MkGenLens[S] + class MkGenLens[S] { + inline def apply[T](get: S => T): Lens[S, T] = ~Lens.impl('(get)) + } +} \ No newline at end of file diff --git a/tests/run-with-compiler/i5941/usage_2.scala b/tests/run-with-compiler/i5941/usage_2.scala index 6883acfd555a..e0b14cba1eaf 100644 --- a/tests/run-with-compiler/i5941/usage_2.scala +++ b/tests/run-with-compiler/i5941/usage_2.scala @@ -2,8 +2,7 @@ case class Address(streetNumber: Int, streetName: String) object Test { def main(args: Array[String]): Unit = { - // val len = Lens.gen[Address, Int](_.streetNumber) - val len = Lens.gen[Address, Int]( (a: Address) => a.streetNumber) + val len = GenLens[Address](_.streetNumber) val address = Address(10, "High Street") assert(len.get(address) == 10) val addr2 = len.set(5, address) From 6351bd147ca85d05be61747791c12c9309d0c846 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Tue, 19 Feb 2019 09:33:17 +0100 Subject: [PATCH 03/13] Walkaround for exception in Term.underlyingArgument --- tests/run-with-compiler/i5941/macro_1.scala | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/tests/run-with-compiler/i5941/macro_1.scala b/tests/run-with-compiler/i5941/macro_1.scala index 2fef9413afdb..678392eecb7f 100644 --- a/tests/run-with-compiler/i5941/macro_1.scala +++ b/tests/run-with-compiler/i5941/macro_1.scala @@ -21,15 +21,21 @@ object Lens { def setterBody(obj: Expr[S], value: Expr[T], field: String): Expr[S] = Term.Select.overloaded(obj.unseal, "copy", Nil, Term.NamedArg(field, value.unseal) :: Nil).seal[S] - getter.unseal.underlyingArgument match { - case Term.Block( - DefDef(_, Nil, (param :: Nil) :: Nil, _, Some(Term.Select(o, field))) :: Nil, - Term.Lambda(meth, _) - ) => + // exception: getter.unseal.underlyingArgument + getter.unseal match { + case Term.Inlined( + None, Nil, + Term.Block( + DefDef(_, Nil, (param :: Nil) :: Nil, _, Some(Term.Select(o, field))) :: Nil, + Term.Lambda(meth, _) + ) + ) if o.symbol == param.symbol => '{ val setter = (t: T) => (s: S) => ~setterBody('(s), '(t), field) apply(~getter)(setter) } + case _ => + throw new QuoteError("Unsupported syntax. Example: `GenLens[Address](_.streetNumber)`") } } } @@ -37,13 +43,13 @@ object Lens { object GenLens { /** case class Address(streetNumber: Int, streetName: String) * - * Lens.gen[Address, Int](_.streetNumber) ~~> + * GenLens[Address](_.streetNumber) ~~> * * Lens[Address, Int](_.streetNumber)(n => a => a.copy(streetNumber = n)) */ def apply[S] = new MkGenLens[S] class MkGenLens[S] { - inline def apply[T](get: S => T): Lens[S, T] = ~Lens.impl('(get)) + inline def apply[T](get: => (S => T)): Lens[S, T] = ~Lens.impl('(get)) } } \ No newline at end of file From a0c65c78a7b782a6ad4ac70da61d55b61ffb6b43 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Tue, 19 Feb 2019 09:40:34 +0100 Subject: [PATCH 04/13] Add neg test for GenLens --- tests/neg-with-compiler/i5941/macro_1.scala | 55 +++++++++++++++++++++ tests/neg-with-compiler/i5941/usage_2.scala | 11 +++++ 2 files changed, 66 insertions(+) create mode 100644 tests/neg-with-compiler/i5941/macro_1.scala create mode 100644 tests/neg-with-compiler/i5941/usage_2.scala diff --git a/tests/neg-with-compiler/i5941/macro_1.scala b/tests/neg-with-compiler/i5941/macro_1.scala new file mode 100644 index 000000000000..678392eecb7f --- /dev/null +++ b/tests/neg-with-compiler/i5941/macro_1.scala @@ -0,0 +1,55 @@ +abstract class Lens[S, T] { + def get(s: S): T + def set(t: T, s: S) :S +} + +import scala.quoted._ +import scala.tasty._ + +object Lens { + def apply[S, T](_get: S => T)(_set: T => S => S): Lens[S, T] = new Lens { + def get(s: S): T = _get(s) + def set(t: T, s: S): S = _set(t)(s) + } + + def impl[S: Type, T: Type](getter: Expr[S => T])(implicit refl: Reflection): Expr[Lens[S, T]] = { + import refl._ + import util._ + import quoted.Toolbox.Default._ + + // obj.copy(field = value) + def setterBody(obj: Expr[S], value: Expr[T], field: String): Expr[S] = + Term.Select.overloaded(obj.unseal, "copy", Nil, Term.NamedArg(field, value.unseal) :: Nil).seal[S] + + // exception: getter.unseal.underlyingArgument + getter.unseal match { + case Term.Inlined( + None, Nil, + Term.Block( + DefDef(_, Nil, (param :: Nil) :: Nil, _, Some(Term.Select(o, field))) :: Nil, + Term.Lambda(meth, _) + ) + ) if o.symbol == param.symbol => + '{ + val setter = (t: T) => (s: S) => ~setterBody('(s), '(t), field) + apply(~getter)(setter) + } + case _ => + throw new QuoteError("Unsupported syntax. Example: `GenLens[Address](_.streetNumber)`") + } + } +} + +object GenLens { + /** case class Address(streetNumber: Int, streetName: String) + * + * GenLens[Address](_.streetNumber) ~~> + * + * Lens[Address, Int](_.streetNumber)(n => a => a.copy(streetNumber = n)) + */ + + def apply[S] = new MkGenLens[S] + class MkGenLens[S] { + inline def apply[T](get: => (S => T)): Lens[S, T] = ~Lens.impl('(get)) + } +} \ No newline at end of file diff --git a/tests/neg-with-compiler/i5941/usage_2.scala b/tests/neg-with-compiler/i5941/usage_2.scala new file mode 100644 index 000000000000..c419f9beb0c3 --- /dev/null +++ b/tests/neg-with-compiler/i5941/usage_2.scala @@ -0,0 +1,11 @@ +case class Address(streetNumber: Int, streetName: String) + +object Test { + def main(args: Array[String]): Unit = { + val len = GenLens[Address](_.streetNumber + 3) // error + val address = Address(10, "High Street") + assert(len.get(address) == 10) + val addr2 = len.set(5, address) + assert(len.get(addr2) == 5) + } +} \ No newline at end of file From d4dbaac1201082d1bb0832c0fe5f8c13587e4ebd Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Tue, 19 Feb 2019 10:13:17 +0100 Subject: [PATCH 05/13] Support GenLens[Employee](_.addr.streetNumber) --- tests/run-with-compiler/i5941/macro_1.scala | 49 ++++++++++++++++----- tests/run-with-compiler/i5941/usage_2.scala | 9 ++++ 2 files changed, 47 insertions(+), 11 deletions(-) diff --git a/tests/run-with-compiler/i5941/macro_1.scala b/tests/run-with-compiler/i5941/macro_1.scala index 678392eecb7f..7299a44a44a5 100644 --- a/tests/run-with-compiler/i5941/macro_1.scala +++ b/tests/run-with-compiler/i5941/macro_1.scala @@ -17,21 +17,48 @@ object Lens { import util._ import quoted.Toolbox.Default._ - // obj.copy(field = value) - def setterBody(obj: Expr[S], value: Expr[T], field: String): Expr[S] = - Term.Select.overloaded(obj.unseal, "copy", Nil, Term.NamedArg(field, value.unseal) :: Nil).seal[S] + + // obj.copy(a = obj.a.copy(b = a.b.copy(c = v))) + def setterBody(obj: Term, value: Term, fields: List[String]): Term = { + // o.copy(field = value) + def helper(obj: Term, value: Term, field: String): Term = + Term.Select.overloaded(obj, "copy", Nil, Term.NamedArg(field, value) :: Nil) + + fields match { + case field :: Nil => helper(obj, value, field) + case field :: fields => + helper(obj, setterBody(Term.Select.unique(obj, field), value, fields), field) + } + } + + object Path { + private def recur(tree: Term, selects: List[String]): Option[(Term, List[String])] = tree match { + case Term.Ident(_) if selects.nonEmpty => Some((tree, selects)) + case Term.Select(qual, name) => recur(qual, name :: selects) + case _ => None + } + + def unapply(t: Term): Option[(Term, List[String])] = recur(t, Nil) + } + + object Function { + def unapply(t: Term): Option[(List[ValDef], Term)] = t match { + case Term.Inlined( + None, Nil, + Term.Block( + (ddef @ DefDef(_, Nil, params :: Nil, _, Some(body))) :: Nil, + Term.Lambda(meth, _) + ) + ) if meth.symbol == ddef.symbol => Some((params, body)) + case _ => None + } + } // exception: getter.unseal.underlyingArgument getter.unseal match { - case Term.Inlined( - None, Nil, - Term.Block( - DefDef(_, Nil, (param :: Nil) :: Nil, _, Some(Term.Select(o, field))) :: Nil, - Term.Lambda(meth, _) - ) - ) if o.symbol == param.symbol => + case Function(param :: Nil, Path(o, fields)) if o.symbol == param.symbol => '{ - val setter = (t: T) => (s: S) => ~setterBody('(s), '(t), field) + val setter = (t: T) => (s: S) => ~setterBody(('(s)).unseal, ('(t)).unseal, fields).seal[S] apply(~getter)(setter) } case _ => diff --git a/tests/run-with-compiler/i5941/usage_2.scala b/tests/run-with-compiler/i5941/usage_2.scala index e0b14cba1eaf..14d06e858d82 100644 --- a/tests/run-with-compiler/i5941/usage_2.scala +++ b/tests/run-with-compiler/i5941/usage_2.scala @@ -1,5 +1,7 @@ case class Address(streetNumber: Int, streetName: String) +case class Employee(name: String, addr: Address) + object Test { def main(args: Array[String]): Unit = { val len = GenLens[Address](_.streetNumber) @@ -7,5 +9,12 @@ object Test { assert(len.get(address) == 10) val addr2 = len.set(5, address) assert(len.get(addr2) == 5) + + val len2 = GenLens[Employee](_.addr.streetNumber) + val employee = Employee("Bob", Address(10, "High Street")) + assert(len2.get(employee) == 10) + val employee2 = len2.set(5, employee) + assert(employee2.name == "Bob") + assert(len2.get(employee2) == 5) } } \ No newline at end of file From 4f36ec019bfda65f1330f1da4cb4b86d601c5861 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Tue, 19 Feb 2019 14:12:11 +0100 Subject: [PATCH 06/13] Support GenIso for case class with one field --- .../tools/dotc/tastyreflect/TreeOpsImpl.scala | 3 + .../tastyreflect/TypeOrBoundsOpsImpl.scala | 7 ++ library/src/scala/tasty/reflect/TreeOps.scala | 2 +- .../scala/tasty/reflect/TypeOrBoundsOps.scala | 2 + tests/run-with-compiler/i5941/macro_1.scala | 103 ++++++++++++++++-- tests/run-with-compiler/i5941/usage_2.scala | 16 ++- 6 files changed, 124 insertions(+), 9 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala index beb38c381ac4..1390caf5df24 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala @@ -383,6 +383,9 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with RootPositionImpl with } object Ident extends IdentModule { + def apply(tmref: TermRef)(implicit ctx: Context): Ident = + withDefaultPos(implicit ctx => tpd.Ident(tmref)) + def copy(original: Tree)(name: String)(implicit ctx: Context): Ident = tpd.cpy.Ident(original)(name.toTermName) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala index f375733f5e00..cf3689585e56 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala @@ -1,6 +1,7 @@ package dotty.tools.dotc.tastyreflect import dotty.tools.dotc.core.{Contexts, Names, Types} +import dotty.tools.dotc.core.Decorators._ trait TypeOrBoundsOpsImpl extends scala.tasty.reflect.TypeOrBoundsOps with CoreImpl { @@ -23,6 +24,9 @@ trait TypeOrBoundsOpsImpl extends scala.tasty.reflect.TypeOrBoundsOps with CoreI if (tpe.classSymbol.exists) Some(tpe.classSymbol.asClass) else None def typeSymbol(implicit ctx: Context): Symbol = tpe.typeSymbol + + def memberType(member: Symbol)(implicit ctx: Context): Type = + member.info.asSeenFrom(tpe, member.owner) } def ConstantTypeDeco(x: ConstantType): Type.ConstantTypeAPI = new Type.ConstantTypeAPI { @@ -181,6 +185,9 @@ trait TypeOrBoundsOpsImpl extends scala.tasty.reflect.TypeOrBoundsOps with CoreI } object TermRef extends TermRefModule { + def apply(qual: TypeOrBounds, name: String)(implicit ctx: Context): TermRef = + Types.TermRef(qual, name.toTermName) + def unapply(x: TypeOrBounds)(implicit ctx: Context): Option[(String, TypeOrBounds /* Type | NoPrefix */)] = x match { case tp: Types.NamedType => tp.designator match { diff --git a/library/src/scala/tasty/reflect/TreeOps.scala b/library/src/scala/tasty/reflect/TreeOps.scala index 7358fb9d64c4..f035cccd5c91 100644 --- a/library/src/scala/tasty/reflect/TreeOps.scala +++ b/library/src/scala/tasty/reflect/TreeOps.scala @@ -2,7 +2,6 @@ package scala.tasty package reflect trait TreeOps extends Core { - // Decorators implicit def TreeDeco(tree: Tree): TreeAPI @@ -249,6 +248,7 @@ trait TreeOps extends Core { /** Scala term identifier */ val Ident: IdentModule abstract class IdentModule { + def apply(tmref: TermRef)(implicit ctx: Context): Ident def copy(original: Tree)(name: String)(implicit ctx: Context): Ident diff --git a/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala b/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala index 1063d4345e25..83f7d8974d43 100644 --- a/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala +++ b/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala @@ -55,6 +55,7 @@ trait TypeOrBoundsOps extends Core { def widen(implicit ctx: Context): Type def classSymbol(implicit ctx: Context): Option[ClassSymbol] def typeSymbol(implicit ctx: Context): Symbol + def memberType(member: Symbol)(implicit ctx: Context): Type } val IsType: IsTypeModule @@ -107,6 +108,7 @@ trait TypeOrBoundsOps extends Core { val TermRef: TermRefModule abstract class TermRefModule { + def apply(qual: TypeOrBounds, name: String)(implicit ctx: Context): TermRef def unapply(typeOrBounds: TypeOrBounds)(implicit ctx: Context): Option[(String, TypeOrBounds /* Type | NoPrefix */)] } diff --git a/tests/run-with-compiler/i5941/macro_1.scala b/tests/run-with-compiler/i5941/macro_1.scala index 7299a44a44a5..059749c1ba85 100644 --- a/tests/run-with-compiler/i5941/macro_1.scala +++ b/tests/run-with-compiler/i5941/macro_1.scala @@ -1,4 +1,4 @@ -abstract class Lens[S, T] { +trait Lens[S, T] { def get(s: S): T def set(t: T, s: S) :S } @@ -19,15 +19,15 @@ object Lens { // obj.copy(a = obj.a.copy(b = a.b.copy(c = v))) - def setterBody(obj: Term, value: Term, fields: List[String]): Term = { + def setterBody(obj: Term, value: Term, parts: List[String]): Term = { // o.copy(field = value) def helper(obj: Term, value: Term, field: String): Term = Term.Select.overloaded(obj, "copy", Nil, Term.NamedArg(field, value) :: Nil) - fields match { + parts match { case field :: Nil => helper(obj, value, field) - case field :: fields => - helper(obj, setterBody(Term.Select.unique(obj, field), value, fields), field) + case field :: parts => + helper(obj, setterBody(Term.Select.unique(obj, field), value, parts), field) } } @@ -56,9 +56,9 @@ object Lens { // exception: getter.unseal.underlyingArgument getter.unseal match { - case Function(param :: Nil, Path(o, fields)) if o.symbol == param.symbol => + case Function(param :: Nil, Path(o, parts)) if o.symbol == param.symbol => '{ - val setter = (t: T) => (s: S) => ~setterBody(('(s)).unseal, ('(t)).unseal, fields).seal[S] + val setter = (t: T) => (s: S) => ~setterBody(('(s)).unseal, ('(t)).unseal, parts).seal[S] apply(~getter)(setter) } case _ => @@ -79,4 +79,93 @@ object GenLens { class MkGenLens[S] { inline def apply[T](get: => (S => T)): Lens[S, T] = ~Lens.impl('(get)) } +} + +trait Iso[S, A] { + def from(a: A): S + def to(s: S): A +} + +object Iso { + def apply[S, A](_from: A => S)(_to: S => A): Iso[S, A] = new Iso { + def from(a: A): S = _from(a) + def to(s: S): A = _to(s) + } + + def impl[S: Type, A: Type](implicit refl: Reflection): Expr[Iso[S, A]] = { + import refl._ + import util._ + import quoted.Toolbox.Default._ + + val tpS = typeOf[S] + val tpA = typeOf[A] + + // 1. S must be a case class + // 2. A must be a tuple + // 3. The parameters of S must match A + if (tpS.classSymbol.flatMap(cls => if (cls.flags.is(Flags.Case)) Some(true) else None).isEmpty) + throw new QuoteError("Only support generation for case classes") + + val cls = tpS.classSymbol.get + + val companion = tpS match { + case Type.SymRef(sym, prefix) => Type.TermRef(prefix, sym.name) + case Type.TypeRef(name, prefix) => Type.TermRef(prefix, name) + } + + if (cls.caseFields.size != 1) + throw new QuoteError("Use GenIso.fields for case classes more than one parameter") + + val fieldTp = tpS.memberType(cls.caseFields.head) + if (!(fieldTp =:= tpA)) + throw new QuoteError(s"The type of case class field $fieldTp does not match $tpA") + + '{ + // (p: S) => p._1 + val to = (p: S) => ~{ Term.Select.unique(('(p)).unseal, "_1").seal[A] } + // (p: A) => S(p) + val from = (p: A) => ~{ Term.Select.overloaded(Term.Ident(companion), "apply", Nil, ('(p)).unseal :: Nil).seal[S] } + apply(from)(to) + } + } +} + +object GenIso { + /** + * GenIso[Person, String] ~~> + * + * Iso[Person, String] + * { p => p._1 } + * { p => Person(p) } + */ + inline def apply[S, A]: Iso[S, A] = ~Iso.impl[S, A] + + inline def fields[S, A]: Iso[S, A] = ??? + inline def unit[S]: Iso[S, Unit] = ??? +} + +trait Prism[S, A] { + def getOption(s: S): Option[A] + def apply(a: A): S +} + +object Prism { + def apply[S, A](getOpt: S => Option[A])(app: A => S): Prism[S, A] = new Prism { + def getOption(s: S): Option[A] = getOpt(s) + def apply(a: A): S = app(a) + } + + def impl[S: Type, A: Type](implicit refl: Reflection): Expr[Prism[S, A]] = ??? +} + +object GenPrism { + /** + * GenPrism[Json, JStr] ~~> + * + * Prism[Json, JStr]{ + * case JStr(v) => Some(v) + * case _ => None + * }(jstr => jstr) + */ + inline def apply[S, A]: Prism[S, A] = ~Prism.impl[S, A] } \ No newline at end of file diff --git a/tests/run-with-compiler/i5941/usage_2.scala b/tests/run-with-compiler/i5941/usage_2.scala index 14d06e858d82..c7b4326f8d8c 100644 --- a/tests/run-with-compiler/i5941/usage_2.scala +++ b/tests/run-with-compiler/i5941/usage_2.scala @@ -1,7 +1,12 @@ case class Address(streetNumber: Int, streetName: String) - case class Employee(name: String, addr: Address) +sealed trait Json +case object JNull extends Json +case class JStr(v: String) extends Json +case class JNum(v: Double) extends Json +case class JObj(v: Map[String, Json]) extends Json + object Test { def main(args: Array[String]): Unit = { val len = GenLens[Address](_.streetNumber) @@ -10,11 +15,20 @@ object Test { val addr2 = len.set(5, address) assert(len.get(addr2) == 5) + // a.b.c val len2 = GenLens[Employee](_.addr.streetNumber) val employee = Employee("Bob", Address(10, "High Street")) assert(len2.get(employee) == 10) val employee2 = len2.set(5, employee) assert(employee2.name == "Bob") assert(len2.get(employee2) == 5) + + // prism + // val jStr: Prism[Json, JStr] = GenPrism[Json, JStr] + // assert(jStr.getOption(JNum(4.5)) == None) + // assert(jStr.getOption(JStr("hello")) == Some(JStr("hello"))) + // assert(jStr(JStr("world")) == JStr("world")) + + assert(GenIso[JStr, String].to(JStr("Hello")) == "Hello") } } \ No newline at end of file From 032a192d3d006215d48ef75cc47c85ad8cbbf479 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Thu, 21 Feb 2019 18:18:40 +0100 Subject: [PATCH 07/13] Implement GenIso.unit --- .../tastyreflect/TypeOrBoundsOpsImpl.scala | 2 + .../scala/tasty/reflect/TypeOrBoundsOps.scala | 1 + tests/run-with-compiler/i5941/macro_1.scala | 37 ++++++++++++++++++- tests/run-with-compiler/i5941/usage_2.scala | 2 + 4 files changed, 41 insertions(+), 1 deletion(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala index cf3689585e56..6dd3e134aa79 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TypeOrBoundsOpsImpl.scala @@ -25,6 +25,8 @@ trait TypeOrBoundsOpsImpl extends scala.tasty.reflect.TypeOrBoundsOps with CoreI def typeSymbol(implicit ctx: Context): Symbol = tpe.typeSymbol + def isSingleton(implicit ctx: Context): Boolean = tpe.isSingleton + def memberType(member: Symbol)(implicit ctx: Context): Type = member.info.asSeenFrom(tpe, member.owner) } diff --git a/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala b/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala index 83f7d8974d43..9e3beb602f37 100644 --- a/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala +++ b/library/src/scala/tasty/reflect/TypeOrBoundsOps.scala @@ -55,6 +55,7 @@ trait TypeOrBoundsOps extends Core { def widen(implicit ctx: Context): Type def classSymbol(implicit ctx: Context): Option[ClassSymbol] def typeSymbol(implicit ctx: Context): Symbol + def isSingleton(implicit ctx: Context): Boolean def memberType(member: Symbol)(implicit ctx: Context): Type } diff --git a/tests/run-with-compiler/i5941/macro_1.scala b/tests/run-with-compiler/i5941/macro_1.scala index 059749c1ba85..5dc90d08021a 100644 --- a/tests/run-with-compiler/i5941/macro_1.scala +++ b/tests/run-with-compiler/i5941/macro_1.scala @@ -128,6 +128,41 @@ object Iso { apply(from)(to) } } + + def implUnit[S: Type](implicit refl: Reflection): Expr[Iso[S, Unit]] = { + import refl._ + import util._ + import quoted.Toolbox.Default._ + + val tpS = typeOf[S] + + if (tpS.isSingleton) { + val ident = Term.Ident(tpS.asInstanceOf[TermRef]).seal[S] + '{ + Iso[S, Unit](Function.const(~ident))(Function.const(())) + } + } + else if (tpS.classSymbol.flatMap(cls => if (cls.flags.is(Flags.Case)) Some(true) else None).nonEmpty) { + val cls = tpS.classSymbol.get + + if (cls.caseFields.size != 0) + throw new QuoteError("Use GenIso.fields for case classes more than one parameter") + + val companion = tpS match { + case Type.SymRef(sym, prefix) => Type.TermRef(prefix, sym.name) + case Type.TypeRef(name, prefix) => Type.TermRef(prefix, name) + } + + val obj = Term.Select.overloaded(Term.Ident(companion), "apply", Nil, Nil).seal[S] + + '{ + Iso[S, Unit](Function.const(~obj))(Function.const(())) + } + } + else { + throw new QuoteError("Only support generation for case classes or singleton types") + } + } } object GenIso { @@ -141,7 +176,7 @@ object GenIso { inline def apply[S, A]: Iso[S, A] = ~Iso.impl[S, A] inline def fields[S, A]: Iso[S, A] = ??? - inline def unit[S]: Iso[S, Unit] = ??? + inline def unit[S]: Iso[S, Unit] = ~Iso.implUnit[S] } trait Prism[S, A] { diff --git a/tests/run-with-compiler/i5941/usage_2.scala b/tests/run-with-compiler/i5941/usage_2.scala index c7b4326f8d8c..46d2d4f18fba 100644 --- a/tests/run-with-compiler/i5941/usage_2.scala +++ b/tests/run-with-compiler/i5941/usage_2.scala @@ -30,5 +30,7 @@ object Test { // assert(jStr(JStr("world")) == JStr("world")) assert(GenIso[JStr, String].to(JStr("Hello")) == "Hello") + assert(GenIso.unit[JNull.type].to(JNull) == (())) + assert(GenIso.unit[JNull.type].from(()) == JNull) } } \ No newline at end of file From 49c0b1d44d9e12d2278bfbcbe49cd368e779d485 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Thu, 21 Feb 2019 18:49:07 +0100 Subject: [PATCH 08/13] Use literal type 1 for unit --- tests/run-with-compiler/i5941/macro_1.scala | 8 ++++---- tests/run-with-compiler/i5941/usage_2.scala | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/run-with-compiler/i5941/macro_1.scala b/tests/run-with-compiler/i5941/macro_1.scala index 5dc90d08021a..0d581dbcf762 100644 --- a/tests/run-with-compiler/i5941/macro_1.scala +++ b/tests/run-with-compiler/i5941/macro_1.scala @@ -129,7 +129,7 @@ object Iso { } } - def implUnit[S: Type](implicit refl: Reflection): Expr[Iso[S, Unit]] = { + def implUnit[S: Type](implicit refl: Reflection): Expr[Iso[S, 1]] = { import refl._ import util._ import quoted.Toolbox.Default._ @@ -139,7 +139,7 @@ object Iso { if (tpS.isSingleton) { val ident = Term.Ident(tpS.asInstanceOf[TermRef]).seal[S] '{ - Iso[S, Unit](Function.const(~ident))(Function.const(())) + Iso[S, 1](Function.const(~ident))(Function.const(1)) } } else if (tpS.classSymbol.flatMap(cls => if (cls.flags.is(Flags.Case)) Some(true) else None).nonEmpty) { @@ -156,7 +156,7 @@ object Iso { val obj = Term.Select.overloaded(Term.Ident(companion), "apply", Nil, Nil).seal[S] '{ - Iso[S, Unit](Function.const(~obj))(Function.const(())) + Iso[S, 1](Function.const(~obj))(Function.const(1)) } } else { @@ -176,7 +176,7 @@ object GenIso { inline def apply[S, A]: Iso[S, A] = ~Iso.impl[S, A] inline def fields[S, A]: Iso[S, A] = ??? - inline def unit[S]: Iso[S, Unit] = ~Iso.implUnit[S] + inline def unit[S]: Iso[S, 1] = ~Iso.implUnit[S] } trait Prism[S, A] { diff --git a/tests/run-with-compiler/i5941/usage_2.scala b/tests/run-with-compiler/i5941/usage_2.scala index 46d2d4f18fba..2c15137868ee 100644 --- a/tests/run-with-compiler/i5941/usage_2.scala +++ b/tests/run-with-compiler/i5941/usage_2.scala @@ -30,7 +30,7 @@ object Test { // assert(jStr(JStr("world")) == JStr("world")) assert(GenIso[JStr, String].to(JStr("Hello")) == "Hello") - assert(GenIso.unit[JNull.type].to(JNull) == (())) - assert(GenIso.unit[JNull.type].from(()) == JNull) + assert(GenIso.unit[JNull.type].to(JNull) == 1) + assert(GenIso.unit[JNull.type].from(1) == JNull) } } \ No newline at end of file From fbf2c522adb7d5305c88563516f36c1ed07e30ec Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Thu, 21 Feb 2019 22:17:12 +0100 Subject: [PATCH 09/13] GenIso.fields blocked by whitebox macros --- tests/run-with-compiler/i5941/macro_1.scala | 7 ++++++- tests/run-with-compiler/i5941/usage_2.scala | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/run-with-compiler/i5941/macro_1.scala b/tests/run-with-compiler/i5941/macro_1.scala index 0d581dbcf762..e5835fdb0c96 100644 --- a/tests/run-with-compiler/i5941/macro_1.scala +++ b/tests/run-with-compiler/i5941/macro_1.scala @@ -163,6 +163,9 @@ object Iso { throw new QuoteError("Only support generation for case classes or singleton types") } } + + // TODO: require whitebox macro + def implFields[S: Type](implicit refl: Reflection): Expr[Iso[S, Any]] = ??? } object GenIso { @@ -175,7 +178,9 @@ object GenIso { */ inline def apply[S, A]: Iso[S, A] = ~Iso.impl[S, A] - inline def fields[S, A]: Iso[S, A] = ??? + // TODO: require whitebox macro + inline def fields[S]: Iso[S, Any] = ~Iso.implFields[S] + inline def unit[S]: Iso[S, 1] = ~Iso.implUnit[S] } diff --git a/tests/run-with-compiler/i5941/usage_2.scala b/tests/run-with-compiler/i5941/usage_2.scala index 2c15137868ee..6622745197a3 100644 --- a/tests/run-with-compiler/i5941/usage_2.scala +++ b/tests/run-with-compiler/i5941/usage_2.scala @@ -32,5 +32,8 @@ object Test { assert(GenIso[JStr, String].to(JStr("Hello")) == "Hello") assert(GenIso.unit[JNull.type].to(JNull) == 1) assert(GenIso.unit[JNull.type].from(1) == JNull) + + // TODO: require whitebox macros + // assert(GenIso.fields[Address].from((0, "a")) == Address(0, "a")) } } \ No newline at end of file From 5ace2a5b7dbb0622607bdbbc138e9dff3d9895a8 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Fri, 22 Feb 2019 14:22:26 +0100 Subject: [PATCH 10/13] Implement GenPrism macro --- tests/run-with-compiler/i5941/macro_1.scala | 26 +++++++++++++++++---- tests/run-with-compiler/i5941/usage_2.scala | 5 ++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/tests/run-with-compiler/i5941/macro_1.scala b/tests/run-with-compiler/i5941/macro_1.scala index e5835fdb0c96..a3022f57f995 100644 --- a/tests/run-with-compiler/i5941/macro_1.scala +++ b/tests/run-with-compiler/i5941/macro_1.scala @@ -184,9 +184,14 @@ object GenIso { inline def unit[S]: Iso[S, 1] = ~Iso.implUnit[S] } -trait Prism[S, A] { +trait Prism[S, A] { outer => def getOption(s: S): Option[A] def apply(a: A): S + + def composeIso[B](iso: Iso[A, B]): Prism[S, B] = new Prism { + def getOption(s: S): Option[B] = outer.getOption(s).map(a => iso.to(a)) + def apply(b: B): S = outer(iso.from(b)) + } } object Prism { @@ -195,7 +200,20 @@ object Prism { def apply(a: A): S = app(a) } - def impl[S: Type, A: Type](implicit refl: Reflection): Expr[Prism[S, A]] = ??? + def impl[S: Type, A <: S : Type](implicit refl: Reflection): Expr[Prism[S, A]] = { + import refl._ + import util._ + import quoted.Toolbox.Default._ + + val tpS = typeOf[S] + val tpA = typeOf[A] + + '{ + val get = (p: S) => if (p.isInstanceOf[A]) Some(p.asInstanceOf[A]) else None + val app = (p: A) => p + apply(get)(app) + } + } } object GenPrism { @@ -204,8 +222,8 @@ object GenPrism { * * Prism[Json, JStr]{ * case JStr(v) => Some(v) - * case _ => None + * case _ => None * }(jstr => jstr) */ - inline def apply[S, A]: Prism[S, A] = ~Prism.impl[S, A] + inline def apply[S, A <: S]: Prism[S, A] = ~Prism.impl[S, A] } \ No newline at end of file diff --git a/tests/run-with-compiler/i5941/usage_2.scala b/tests/run-with-compiler/i5941/usage_2.scala index 6622745197a3..2d3fed87a511 100644 --- a/tests/run-with-compiler/i5941/usage_2.scala +++ b/tests/run-with-compiler/i5941/usage_2.scala @@ -35,5 +35,10 @@ object Test { // TODO: require whitebox macros // assert(GenIso.fields[Address].from((0, "a")) == Address(0, "a")) + + val jNum: Prism[Json, Double] = GenPrism[Json, JNum] composeIso GenIso[JNum, Double] + assert(jNum(3.5) == JNum(3.5)) + assert(jNum.getOption(JNum(3.5)) == Some(3.5)) + assert(jNum.getOption(JNull) == None) } } \ No newline at end of file From d02d60459c0ff72abc03ea602d0e266eabae50a6 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Fri, 22 Feb 2019 14:32:19 +0100 Subject: [PATCH 11/13] test GenIso with inner classes --- .../tools/dotc/tastyreflect/TreeOpsImpl.scala | 4 ++-- library/src/scala/tasty/reflect/TreeOps.scala | 2 +- tests/run-with-compiler/i5941/usage_2.scala | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 3 deletions(-) diff --git a/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala b/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala index 1390caf5df24..5f86ea0dd0f1 100644 --- a/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala +++ b/compiler/src/dotty/tools/dotc/tastyreflect/TreeOpsImpl.scala @@ -383,8 +383,8 @@ trait TreeOpsImpl extends scala.tasty.reflect.TreeOps with RootPositionImpl with } object Ident extends IdentModule { - def apply(tmref: TermRef)(implicit ctx: Context): Ident = - withDefaultPos(implicit ctx => tpd.Ident(tmref)) + def apply(tmref: TermRef)(implicit ctx: Context): Term = + withDefaultPos(implicit ctx => tpd.ref(tmref).asInstanceOf[Term]) def copy(original: Tree)(name: String)(implicit ctx: Context): Ident = tpd.cpy.Ident(original)(name.toTermName) diff --git a/library/src/scala/tasty/reflect/TreeOps.scala b/library/src/scala/tasty/reflect/TreeOps.scala index f035cccd5c91..89f12ae03ba4 100644 --- a/library/src/scala/tasty/reflect/TreeOps.scala +++ b/library/src/scala/tasty/reflect/TreeOps.scala @@ -248,7 +248,7 @@ trait TreeOps extends Core { /** Scala term identifier */ val Ident: IdentModule abstract class IdentModule { - def apply(tmref: TermRef)(implicit ctx: Context): Ident + def apply(tmref: TermRef)(implicit ctx: Context): Term def copy(original: Tree)(name: String)(implicit ctx: Context): Ident diff --git a/tests/run-with-compiler/i5941/usage_2.scala b/tests/run-with-compiler/i5941/usage_2.scala index 2d3fed87a511..1fefad1959c2 100644 --- a/tests/run-with-compiler/i5941/usage_2.scala +++ b/tests/run-with-compiler/i5941/usage_2.scala @@ -40,5 +40,19 @@ object Test { assert(jNum(3.5) == JNum(3.5)) assert(jNum.getOption(JNum(3.5)) == Some(3.5)) assert(jNum.getOption(JNull) == None) + + // inner classes + val inner = new Inner + assert(GenIso[inner.JStr, String].to(inner.JStr("Hello")) == "Hello") + assert(GenIso.unit[inner.JNull.type].to(inner.JNull) == 1) + assert(GenIso.unit[inner.JNull.type].from(1) == inner.JNull) } +} + +class Inner { + sealed trait Json + case object JNull extends Json + case class JStr(v: String) extends Json + case class JNum(v: Double) extends Json + case class JObj(v: Map[String, Json]) extends Json } \ No newline at end of file From 4e58ba12edd8e8885258411132a455eec6838e13 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Mon, 25 Feb 2019 13:13:32 +0100 Subject: [PATCH 12/13] Cleanup code --- tests/run-with-compiler/i5941/macro_1.scala | 4 ---- tests/run-with-compiler/i5941/usage_2.scala | 8 ++++---- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/tests/run-with-compiler/i5941/macro_1.scala b/tests/run-with-compiler/i5941/macro_1.scala index a3022f57f995..c292a0941552 100644 --- a/tests/run-with-compiler/i5941/macro_1.scala +++ b/tests/run-with-compiler/i5941/macro_1.scala @@ -203,10 +203,6 @@ object Prism { def impl[S: Type, A <: S : Type](implicit refl: Reflection): Expr[Prism[S, A]] = { import refl._ import util._ - import quoted.Toolbox.Default._ - - val tpS = typeOf[S] - val tpA = typeOf[A] '{ val get = (p: S) => if (p.isInstanceOf[A]) Some(p.asInstanceOf[A]) else None diff --git a/tests/run-with-compiler/i5941/usage_2.scala b/tests/run-with-compiler/i5941/usage_2.scala index 1fefad1959c2..6cad3fc0cec5 100644 --- a/tests/run-with-compiler/i5941/usage_2.scala +++ b/tests/run-with-compiler/i5941/usage_2.scala @@ -24,10 +24,10 @@ object Test { assert(len2.get(employee2) == 5) // prism - // val jStr: Prism[Json, JStr] = GenPrism[Json, JStr] - // assert(jStr.getOption(JNum(4.5)) == None) - // assert(jStr.getOption(JStr("hello")) == Some(JStr("hello"))) - // assert(jStr(JStr("world")) == JStr("world")) + val jStr: Prism[Json, JStr] = GenPrism[Json, JStr] + assert(jStr.getOption(JNum(4.5)) == None) + assert(jStr.getOption(JStr("hello")) == Some(JStr("hello"))) + assert(jStr(JStr("world")) == JStr("world")) assert(GenIso[JStr, String].to(JStr("Hello")) == "Hello") assert(GenIso.unit[JNull.type].to(JNull) == 1) From 7538a32aaf633df4fde1cff66cea00615ba17782 Mon Sep 17 00:00:00 2001 From: Liu Fengyun Date: Mon, 25 Feb 2019 14:11:58 +0100 Subject: [PATCH 13/13] Fix syntax change for Splice after rebase --- tests/neg-with-compiler/i5941/macro_1.scala | 6 +++--- tests/run-with-compiler/i5941/macro_1.scala | 22 ++++++++++----------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/tests/neg-with-compiler/i5941/macro_1.scala b/tests/neg-with-compiler/i5941/macro_1.scala index 678392eecb7f..4791335569a5 100644 --- a/tests/neg-with-compiler/i5941/macro_1.scala +++ b/tests/neg-with-compiler/i5941/macro_1.scala @@ -31,8 +31,8 @@ object Lens { ) ) if o.symbol == param.symbol => '{ - val setter = (t: T) => (s: S) => ~setterBody('(s), '(t), field) - apply(~getter)(setter) + val setter = (t: T) => (s: S) => ${ setterBody('s, 't, field) } + apply($getter)(setter) } case _ => throw new QuoteError("Unsupported syntax. Example: `GenLens[Address](_.streetNumber)`") @@ -50,6 +50,6 @@ object GenLens { def apply[S] = new MkGenLens[S] class MkGenLens[S] { - inline def apply[T](get: => (S => T)): Lens[S, T] = ~Lens.impl('(get)) + inline def apply[T](get: => (S => T)): Lens[S, T] = ${ Lens.impl('get) } } } \ No newline at end of file diff --git a/tests/run-with-compiler/i5941/macro_1.scala b/tests/run-with-compiler/i5941/macro_1.scala index c292a0941552..6eed35a2a69c 100644 --- a/tests/run-with-compiler/i5941/macro_1.scala +++ b/tests/run-with-compiler/i5941/macro_1.scala @@ -58,8 +58,8 @@ object Lens { getter.unseal match { case Function(param :: Nil, Path(o, parts)) if o.symbol == param.symbol => '{ - val setter = (t: T) => (s: S) => ~setterBody(('(s)).unseal, ('(t)).unseal, parts).seal[S] - apply(~getter)(setter) + val setter = (t: T) => (s: S) => ${ setterBody(('s).unseal, ('t).unseal, parts).seal[S] } + apply($getter)(setter) } case _ => throw new QuoteError("Unsupported syntax. Example: `GenLens[Address](_.streetNumber)`") @@ -77,7 +77,7 @@ object GenLens { def apply[S] = new MkGenLens[S] class MkGenLens[S] { - inline def apply[T](get: => (S => T)): Lens[S, T] = ~Lens.impl('(get)) + inline def apply[T](get: => (S => T)): Lens[S, T] = ${ Lens.impl('get) } } } @@ -122,9 +122,9 @@ object Iso { '{ // (p: S) => p._1 - val to = (p: S) => ~{ Term.Select.unique(('(p)).unseal, "_1").seal[A] } + val to = (p: S) => ${ Term.Select.unique(('p).unseal, "_1").seal[A] } // (p: A) => S(p) - val from = (p: A) => ~{ Term.Select.overloaded(Term.Ident(companion), "apply", Nil, ('(p)).unseal :: Nil).seal[S] } + val from = (p: A) => ${ Term.Select.overloaded(Term.Ident(companion), "apply", Nil, ('p).unseal :: Nil).seal[S] } apply(from)(to) } } @@ -139,7 +139,7 @@ object Iso { if (tpS.isSingleton) { val ident = Term.Ident(tpS.asInstanceOf[TermRef]).seal[S] '{ - Iso[S, 1](Function.const(~ident))(Function.const(1)) + Iso[S, 1](Function.const($ident))(Function.const(1)) } } else if (tpS.classSymbol.flatMap(cls => if (cls.flags.is(Flags.Case)) Some(true) else None).nonEmpty) { @@ -156,7 +156,7 @@ object Iso { val obj = Term.Select.overloaded(Term.Ident(companion), "apply", Nil, Nil).seal[S] '{ - Iso[S, 1](Function.const(~obj))(Function.const(1)) + Iso[S, 1](Function.const($obj))(Function.const(1)) } } else { @@ -176,12 +176,12 @@ object GenIso { * { p => p._1 } * { p => Person(p) } */ - inline def apply[S, A]: Iso[S, A] = ~Iso.impl[S, A] + inline def apply[S, A]: Iso[S, A] = ${ Iso.impl[S, A] } // TODO: require whitebox macro - inline def fields[S]: Iso[S, Any] = ~Iso.implFields[S] + inline def fields[S]: Iso[S, Any] = ${ Iso.implFields[S] } - inline def unit[S]: Iso[S, 1] = ~Iso.implUnit[S] + inline def unit[S]: Iso[S, 1] = ${ Iso.implUnit[S] } } trait Prism[S, A] { outer => @@ -221,5 +221,5 @@ object GenPrism { * case _ => None * }(jstr => jstr) */ - inline def apply[S, A <: S]: Prism[S, A] = ~Prism.impl[S, A] + inline def apply[S, A <: S]: Prism[S, A] = ${ Prism.impl[S, A] } } \ No newline at end of file