I intend to create the simple polymorphic function expression below using the Quotes.reflect API:
new PolyFunction {
def apply[X](a: X): X = a
}
What I have attempted is shown below with parts that I could not implement replaced by ???:
val name: String = "$anon"
val parents = List(TypeTree.of[Object], TypeTree.of[PolyFunction])
def decls(cls: Symbol): List[Symbol] =
List(
Symbol.newMethod(
cls,
"apply",
MethodType(List("a"))(
{ mt =>
???
},
mt => ???
)
)
)
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = parents.map(_.tpe), decls, selfType = None)
val applySym = cls.declaredMethod("apply").head
val applyDef = DefDef(applySym, argss => Some(???))
val clsDef = ClassDef(cls, parents, body = List(applyDef))
val closure = Block(List(clsDef),Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil))
closure.asExpr
The problem, mainly, is I am unable to declare the type parameter X, and its reference in the first value parameter and the functions return type. I have noticed some reflection functions are annotated as experimental.
Try
import scala.annotation.experimental
import scala.quoted.*
inline def newPoly: PolyFunction = ${newPolyImpl}
#experimental
def newPolyImpl(using Quotes): Expr[PolyFunction] = {
import quotes.reflect.*
val name: String = "$anon"
val parents = List(TypeTree.of[Object], TypeTree.of[PolyFunction])
def decls(cls: Symbol): List[Symbol] =
List(
Symbol.newMethod(
cls,
"apply",
PolyType(List("X"))(_ => List(TypeBounds.empty), polyType => {
val typeParam = polyType.param(0)
MethodType(List("a"))(_ => List(typeParam), _ => typeParam)
})
)
)
val cls = Symbol.newClass(Symbol.spliceOwner, name, parents = parents.map(_.tpe), decls, selfType = None)
val applySym = cls.declaredMethod("apply").head
// argss=List(List(TypeTree[TypeRef(NoPrefix,type X)]), List(Ident(a)))
val applyDef = DefDef(applySym, argss => Some(argss(1)(0).asInstanceOf[Term]))
val clsDef = ClassDef(cls, parents, body = List(applyDef))
val closure = Block(List(clsDef), Apply(Select(New(TypeIdent(cls)), cls.primaryConstructor), Nil))
closure.asExprOf[PolyFunction]
}
Usage:
newPoly
//scalac: {
// class $anon extends java.lang.Object with scala.PolyFunction {
// def apply[X](a: X): X = a
// }
// new $anon()
//}
Scala 3.2.1
Method Override with Scala 3 Macros
Scala3: Crafting Types Through Metaprogramming?
`tq` equivalent in Scala 3 macros
I am coding using scala3, leveraging programmatic structural types.
The structural types happen to mimic existing case classes:
their definition is pure boiler plate,
hence the temptation to craft them through meta-programming.
I understand how to craft a function implementation, typically via typeclass derivation.
But here we are trying to craft a (structural) type.
This was possible in scala2, via class macro annotation, but those are gone in scala3.
Is there a way ? If so how ?
Code below is the result I would like to obtain :
// Library part
trait View extends scala.Selectable :
def selectDynamic(key:String) =
println(s"$key is being looked up")
???
// DSL Definition part
case class SomeDefWithInt ( i : Int )
case class SomeDefWithString( s : String )
// Boiler-plate code
type ViewOf[M] = M match
case SomeDefWithInt => View { def i : Int }
case SomeDefWithString => View { def s : String }
// Mockup usage
class V extends View
val v = V()
v.asInstanceOf[ViewOf[SomeDefWithInt ]].i
v.asInstanceOf[ViewOf[SomeDefWithString]].s
is it possible to create ViewOf[M] of an arbitrary case class M ?
Thank you !
Just in case, here is what I meant by hiding ViewOf inside a type class (type classes is an alternative to match types). Sadly, in Scala 3 this is wordy.
(version 1)
import scala.annotation.experimental
import scala.quoted.{Expr, Quotes, Type, quotes}
// Library part
trait View extends Selectable {
def applyDynamic(key: String)(args: Any*): Any = {
println(s"$key is being looked up with $args")
if (key == "i") 1
else if (key == "s") "a"
else ???
}
def selectDynamic(key: String): Any = {
println(s"$key is being looked up")
if (key == "i1") 2
else if (key == "s1") "b"
else ???
}
}
// type class
trait ViewOf[M <: Product] {
type Out <: View
}
object ViewOf {
transparent inline given mkViewOf[M <: Product]: ViewOf[M] = ${givenImpl[M]}
#experimental // because .newClass is #experimental
def givenImpl[M <: Product : Type](using Quotes): Expr[ViewOf[M]] = {
import quotes.reflect.*
extension (symb: Symbol) {
def setFlags(flags: Flags): Symbol = {
given dotty.tools.dotc.core.Contexts.Context =
quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
symb.asInstanceOf[dotty.tools.dotc.core.Symbols.Symbol]
.denot.setFlag(flags.asInstanceOf[dotty.tools.dotc.core.Flags.FlagSet])
symb
}
}
def newType(cls: Symbol, name: String, tpe: TypeRepr, flags: Flags = Flags.EmptyFlags, privateWithin: Symbol = Symbol.noSymbol): Symbol = {
given dotty.tools.dotc.core.Contexts.Context =
quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
import dotty.tools.dotc.core.Decorators.toTypeName
dotty.tools.dotc.core.Symbols.newSymbol(
cls.asInstanceOf[dotty.tools.dotc.core.Symbols.Symbol],
name.toTypeName,
flags.asInstanceOf[dotty.tools.dotc.core.Flags.FlagSet],
tpe.asInstanceOf[dotty.tools.dotc.core.Types.Type],
privateWithin.asInstanceOf[dotty.tools.dotc.core.Symbols.Symbol]
).asInstanceOf[Symbol]
}
val M = TypeRepr.of[M]
val fields = M.typeSymbol.caseFields
val viewImplDecls = (cls: Symbol) =>
fields.flatMap(fieldSymb =>
Seq(
Symbol.newMethod(cls, fieldSymb.name, MethodType(Nil)(_ => Nil, _ => M.memberType(fieldSymb)), // vararg? MatchError: Inlined
Flags.Deferred, privateWithin = Symbol.noSymbol),
Symbol.newVal(cls, fieldSymb.name + "1", M.memberType(fieldSymb),
Flags.Deferred, privateWithin = Symbol.noSymbol)
)
)
val viewImplParents = List(TypeTree.of[AnyRef], TypeTree.of[View])
val viewImplCls = Symbol.newClass(Symbol.spliceOwner, "ViewImpl", viewImplParents.map(_.tpe), viewImplDecls, selfType = None)
.setFlags(Flags.Trait)
val methodDefs = fields.flatMap(fieldSymb => {
val methodSymb = viewImplCls.declaredMethod(fieldSymb.name).head
val valSymb = viewImplCls.fieldMember(fieldSymb.name + "1")
Seq(
DefDef(methodSymb, _ => None),
ValDef(valSymb, None)
)
})
val viewImplClsDef = ClassDef(viewImplCls, viewImplParents, body = methodDefs)
val viewOfImplDecls = (cls: Symbol) => List(newType(cls, "Out",
TypeBounds(viewImplCls.typeRef, viewImplCls.typeRef), Flags.Override))
val viewOfTypeTree = TypeTree.of[ViewOf[M]]
val viewOfImplParents = List(TypeTree.of[AnyRef], viewOfTypeTree)
val viewOfImplCls = Symbol.newClass(Symbol.spliceOwner, "ViewOfImpl", viewOfImplParents.map(_.tpe), viewOfImplDecls, selfType = None)
val outSymb = viewOfImplCls.declaredType("Out").head
val outTypeDef = TypeDef(outSymb)
val viewOfImplClsDef = ClassDef(viewOfImplCls, viewOfImplParents, body = List(outTypeDef))
val newViewOfImpl = Apply(Select(New(TypeIdent(viewOfImplCls)), viewOfImplCls.primaryConstructor), Nil)
val res = Block(List(viewImplClsDef, viewOfImplClsDef), newViewOfImpl).asExprOf[ViewOf[M]]
println(res.show + "=" + res.asTerm.show(using Printer.TreeStructure))
res
}
}
extension (v: View) {
def refine[M <: Product](using viewOf: ViewOf[M]): viewOf.Out = v.asInstanceOf[viewOf.Out]
}
// DSL Definition part
case class SomeDefWithInt ( i : Int )
case class SomeDefWithString( s : String )
// Mockup usage
class V extends View
val v = V()
println(v.refine[SomeDefWithInt].i())
// i is being looked up with ArraySeq()
// 1
println(v.refine[SomeDefWithString].s())
// s is being looked up with ArraySeq()
// a
println(v.refine[SomeDefWithInt].i1)
// i1 is being looked up
// 2
println(v.refine[SomeDefWithString].s1)
// s1 is being looked up
// b
//scalac: {
// trait ViewImpl extends java.lang.Object with Macros.View {
// def i(): scala.Int
// val i1: scala.Int
// }
// class ViewOfImpl extends java.lang.Object with Macros.ViewOf[App.SomeDefWithInt] {
// type Out // actually, type Out = ViewImpl
// }
// new ViewOfImpl()
//}=Block(List(ClassDef("ViewImpl", DefDef("<init>", Nil, Inferred(), None), List(Inferred(), Inferred()), None, List(DefDef("i", List(TermParamClause(Nil)), Inferred(), None), ValDef("i1", Inferred(), None))), ClassDef("ViewOfImpl", DefDef("<init>", Nil, Inferred(), None), List(Inferred(), Inferred()), None, List(TypeDef("Out", TypeBoundsTree(Inferred(), Inferred()))))), Apply(Select(New(Inferred()), "<init>"), Nil))
//scalac: {
// trait ViewImpl extends java.lang.Object with Macros.View {
// def s(): scala.Predef.String
// val s1: scala.Predef.String
// }
// class ViewOfImpl extends java.lang.Object with Macros.ViewOf[App.SomeDefWithString] {
// type Out // actually, type Out = ViewImpl
// }
// new ViewOfImpl()
//}=Block(List(ClassDef("ViewImpl", DefDef("<init>", Nil, Inferred(), None), List(Inferred(), Inferred()), None, List(DefDef("s", List(TermParamClause(Nil)), Inferred(), None), ValDef("s1", Inferred(), None))), ClassDef("ViewOfImpl", DefDef("<init>", Nil, Inferred(), None), List(Inferred(), Inferred()), None, List(TypeDef("Out", TypeBoundsTree(Inferred(), Inferred()))))), Apply(Select(New(Inferred()), "<init>"), Nil))
ViewOf[M] is meant is to be used by a DSL user, so no way to hide it within a derived type class.
Not sure I understood.
Method Override with Scala 3 Macros
`tq` equivalent in Scala 3 macros
How to generate a class in Dotty with macro?
How to splice multiple expressions in quote syntax of scala 3 macros?
How to access parameter list of case class in a dotty macro
https://github.com/lampepfl/dotty/discussions/14056
Another implementation of the type class (with a refinement type instead of trait type)
(version 2)
trait ViewOf[M <: Product] {
type Out <: View
}
object ViewOf {
transparent inline given mkViewOf[M <: Product]: ViewOf[M] = ${givenImpl[M]}
#experimental // because .newClass is #experimental
def givenImpl[M <: Product : Type](using Quotes): Expr[ViewOf[M]] = {
import quotes.reflect.*
def makeRefinement(parent: TypeRepr, names: List[String], infos: List[TypeRepr]): TypeRepr =
names.zip(infos).foldLeft(parent){ case (acc, (name, tpe)) => Refinement(acc, name, tpe) }
def newType(cls: Symbol, name: String, tpe: TypeRepr, flags: Flags = Flags.EmptyFlags, privateWithin: Symbol = Symbol.noSymbol): Symbol = {
given dotty.tools.dotc.core.Contexts.Context =
quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
import dotty.tools.dotc.core.Decorators.toTypeName
dotty.tools.dotc.core.Symbols.newSymbol(
cls.asInstanceOf[dotty.tools.dotc.core.Symbols.Symbol],
name.toTypeName,
flags.asInstanceOf[dotty.tools.dotc.core.Flags.FlagSet],
tpe.asInstanceOf[dotty.tools.dotc.core.Types.Type],
privateWithin.asInstanceOf[dotty.tools.dotc.core.Symbols.Symbol]
).asInstanceOf[Symbol]
}
val M = TypeRepr.of[M]
val fields = M.typeSymbol.caseFields
val fieldNames = fields.flatMap(fieldSymb => Seq(fieldSymb.name, fieldSymb.name + "1"))
val fieldMethodTypes = fields.flatMap(fieldSymb => Seq(
MethodType(List("args"))(_ => List(AnnotatedType(TypeRepr.of[Any], '{new scala.annotation.internal.Repeated()}.asTerm)), _ => M.memberType(fieldSymb)),
ByNameType(M.memberType(fieldSymb)))
)
val refinement = makeRefinement(TypeRepr.of[View], fieldNames, fieldMethodTypes)
val viewOfImplDecls = (cls: Symbol) => List(newType(cls, "Out",
TypeBounds(refinement, refinement), Flags.Override))
val viewOfTypeTree = TypeTree.of[ViewOf[M]]
val viewOfImplParents = List(TypeTree.of[AnyRef], viewOfTypeTree)
val viewOfImplCls = Symbol.newClass(Symbol.spliceOwner, "ViewOfImpl", viewOfImplParents.map(_.tpe), viewOfImplDecls, selfType = None)
val outSymb = viewOfImplCls.declaredType("Out").head
val outTypeDef = TypeDef(outSymb)
val viewOfImplClsDef = ClassDef(viewOfImplCls, viewOfImplParents, body = List(outTypeDef))
val newViewOfImpl = Apply(Select(New(TypeIdent(viewOfImplCls)), viewOfImplCls.primaryConstructor), Nil)
val res = Block(List(viewOfImplClsDef), newViewOfImpl).asExprOf[ViewOf[M]]
println(res.show + "=" + res.asTerm.show(using Printer.TreeStructure))
res
}
}
println(v.refine[SomeDefWithInt].i(10, "x", true))
//i is being looked up with ArraySeq((10,x,true))
//1
println(v.refine[SomeDefWithString].s(20, "y", 30L))
//s is being looked up with ArraySeq((20,y,30))
//a
println(v.refine[SomeDefWithInt].i1)
//i1 is being looked up
//2
println(v.refine[SomeDefWithString].s1)
//s1 is being looked up
//b
//scalac: {
// class ViewOfImpl extends java.lang.Object with Macros.ViewOf[App.SomeDefWithInt] {
// type Out // actually, type Out = View {def i(args: Any*): Int; def i1: Int}
// }
// new ViewOfImpl()
//}=Block(List(ClassDef("ViewOfImpl", DefDef("<init>", Nil, Inferred(), None), List(Inferred(), Inferred()), None, List(TypeDef("Out", TypeBoundsTree(Inferred(), Inferred()))))), Apply(Select(New(Inferred()), "<init>"), Nil))
Also we can use Mirror instead of reflection
(version 3)
trait ViewOf[M <: Product] {
type Out <: View
}
object ViewOf {
transparent inline given mkViewOf[M <: Product]: ViewOf[M] = ${givenImpl[M]}
def givenImpl[M <: Product : Type](using Quotes): Expr[ViewOf[M]] = {
import quotes.reflect.*
def makeRefinement(parent: TypeRepr, namesAndTypes: List[(String, TypeRepr)]): TypeRepr =
namesAndTypes.foldLeft(parent) { case (acc, (name, tpe)) => Refinement(acc, name, tpe) }
def mkNamesAndTypes[mels: Type, mets: Type]: List[(String, TypeRepr)] =
(Type.of[mels], Type.of[mets]) match {
case ('[EmptyTuple], '[EmptyTuple]) => Nil
case ('[mel *: melTail], '[met *: metTail] ) => {
val name = Type.valueOfConstant[mel].get.toString
val name1 = name + "1"
//scala.MatchError: Inlined(Ident(Macros$),List(),Apply(Select(New(Select(Select(Select(Ident(scala),annotation),internal),Repeated)),<init>),List())) (of class dotty.tools.dotc.ast.Trees$Inlined)
//val methodType = MethodType(List("args"))(_ => List(AnnotatedType(TypeRepr.of[Any], '{new scala.annotation.internal.Repeated()}.asTerm)), _ => TypeRepr.of[met])
val methodType = MethodType(Nil)(_ => Nil, _ => TypeRepr.of[met])
val methodType1 = ByNameType(TypeRepr.of[met])
(name, methodType) :: (name1, methodType1) :: mkNamesAndTypes[melTail, metTail]
}
}
val namesAndTypes = Expr.summon[Mirror.ProductOf[M]].get match {
case '{ $m: Mirror.ProductOf[M] { type MirroredElemLabels = mels; type MirroredElemTypes = mets } } =>
mkNamesAndTypes[mels, mets]
}
val res = makeRefinement(TypeRepr.of[View], namesAndTypes).asType match {
case '[tpe] =>
'{
new ViewOf[M] {
type Out = tpe
}
}
}
println(res.show)
res
}
}
Unfortunately, this doesn't work because of an extra type ascription (Expr looses type refinement)
//scalac: {
// final class $anon() extends Macros.ViewOf[App.SomeDefWithInt] {
// type Out = Macros.View {
// def i(): scala.Int
// def i1: scala.Int
// }
// }
//
// (new $anon(): Macros.ViewOf[App.SomeDefWithInt]) // <--- HERE!!!
//}
https://github.com/lampepfl/dotty/issues/15566 (for structural refinements i.e. defs, their loss seems to be expected behavior, but type refinement loss can be a bug)
So, at least once we have to use low-level newClass to avoid type ascription
(version 4)
trait ViewOf[M <: Product] {
type Out <: View
}
object ViewOf {
transparent inline given mkViewOf[M <: Product]: ViewOf[M] = ${givenImpl[M]}
#experimental // because .newClass is #experimental
def givenImpl[M <: Product : Type](using Quotes): Expr[ViewOf[M]] = {
import quotes.reflect.*
def makeRefinement(parent: TypeRepr, namesAndTypes: List[(String, TypeRepr)]): TypeRepr =
namesAndTypes.foldLeft(parent) { case (acc, (name, tpe)) => Refinement(acc, name, tpe) }
def mkNamesAndTypes[mels: Type, mets: Type]: List[(String, TypeRepr)] =
(Type.of[mels], Type.of[mets]) match {
case ('[EmptyTuple], '[EmptyTuple]) => Nil
case ('[mel *: melTail], '[met *: metTail] ) => {
val name = Type.valueOfConstant[mel].get.toString
val name1 = name + "1"
val methodType = MethodType(List("args"))(_ => List(AnnotatedType(TypeRepr.of[Any], '{new scala.annotation.internal.Repeated()}.asTerm)), _ => TypeRepr.of[met])
val methodType1 = ByNameType(TypeRepr.of[met])
(name, methodType) :: (name1, methodType1) :: mkNamesAndTypes[melTail, metTail]
}
}
val namesAndTypes = Expr.summon[Mirror.ProductOf[M]].get match {
case '{ $m: Mirror.ProductOf[M] { type MirroredElemLabels = mels; type MirroredElemTypes = mets } } =>
mkNamesAndTypes[mels, mets]
}
val refinement = makeRefinement(TypeRepr.of[View], namesAndTypes)
def newType(cls: Symbol, name: String, tpe: TypeRepr, flags: Flags = Flags.EmptyFlags, privateWithin: Symbol = Symbol.noSymbol): Symbol = {
given dotty.tools.dotc.core.Contexts.Context =
quotes.asInstanceOf[scala.quoted.runtime.impl.QuotesImpl].ctx
import dotty.tools.dotc.core.Decorators.toTypeName
dotty.tools.dotc.core.Symbols.newSymbol(
cls.asInstanceOf[dotty.tools.dotc.core.Symbols.Symbol],
name.toTypeName,
flags.asInstanceOf[dotty.tools.dotc.core.Flags.FlagSet],
tpe.asInstanceOf[dotty.tools.dotc.core.Types.Type],
privateWithin.asInstanceOf[dotty.tools.dotc.core.Symbols.Symbol]
).asInstanceOf[Symbol]
}
val viewOfImplDecls = (cls: Symbol) => List(newType(cls, "Out",
TypeBounds(refinement, refinement),
Flags.Override))
val viewOfTypeTree = TypeTree.of[ViewOf[M]]
val viewOfImplParents = List(TypeTree.of[AnyRef], viewOfTypeTree)
val viewOfImplCls = Symbol.newClass(Symbol.spliceOwner, "ViewOfImpl", viewOfImplParents.map(_.tpe), viewOfImplDecls, selfType = None)
val outSymb = viewOfImplCls.declaredType("Out").head
val outTypeDef = TypeDef(outSymb)
val viewOfImplClsDef = ClassDef(viewOfImplCls, viewOfImplParents, body = List(outTypeDef))
// this would be an extra type ascription to be avoided
// val newViewOfImpl = Typed(Apply(Select(New(TypeIdent(viewOfImplCls)), viewOfImplCls.primaryConstructor), Nil), TypeTree.of[ViewOf[M]])
val newViewOfImpl = Apply(Select(New(TypeIdent(viewOfImplCls)), viewOfImplCls.primaryConstructor), Nil)
val res = Block(List(viewOfImplClsDef), newViewOfImpl).asExprOf[ViewOf[M]]
println(res.show + "=" + res.asTerm.show(using Printer.TreeStructure))
res
}
}
Inspired by travisbrown, I'm trying to use a macro to create some "smart constructors".
Given
package mypkg
sealed trait Hello[A]
case class Ohayo[A,B](a: (A,B)) extends Hello[A]
and
val smartConstructors = FreeMacros.liftConstructors[Hello]
The macro should find all the subclasses of Hello, look at their constructors, and extract a few elements to populate this tree for the "smart constructor":
q"""
def $methodName[..$typeParams](...$paramLists): $baseType =
$companionSymbol[..$typeArgs](...$argLists)
"""
I hoped to get:
val smartConstructors = new {
def ohayo[A, B](a: (A, B)): Hello[A] = Ohayo[A, B](a)
}
but instead get:
error: type mismatch;
found : (A(in class Ohayo), B(in class Ohayo))
required: ((some other)A(in class Ohayo), (some other)B(in class Ohayo))
val liftedConstructors = FreeMacros.liftConstructors[Hello]
At a glance, the tree looks ok to me:
scala> q" new { ..$wellTyped }"
res1: u.Tree =
{
final class $anon extends scala.AnyRef {
def <init>() = {
super.<init>();
()
};
def ohayo[A, B](a: (A, B)): net.arya.constructors.Hello[A] = Ohayo[A, B](a)
};
new $anon()
}
but I guess it invisibly isn't. If I naively try to freshen up the typeParams with info.typeParams.map(p => TypeName(p.name.toString)), I get "can't splice A as type parameter" when I do the quasiquoting.
Where am I going wrong? Thanks for taking a look.
-Arya
import scala.language.experimental.macros
import scala.reflect.api.Universe
import scala.reflect.macros.whitebox
class FreeMacros(val c: whitebox.Context) {
import c.universe._
import FreeMacros._
def liftedImpl[F[_]](implicit t: c.WeakTypeTag[F[_]]): Tree = {
val atc = t.tpe
val childSymbols: Set[ClassSymbol] = subCaseClassSymbols(c.universe)(atc.typeSymbol.asClass)
val wellTyped = childSymbols.map(ctorsForSymbol(c.universe)(atc)).unzip
q"new { ..${wellTyped} }"
}
}
object FreeMacros {
def liftConstructors[F[_]]: Any = macro FreeMacros.liftedImpl[F]
def smartName(name: String): String = (
name.toList match {
case h :: t => h.toLower :: t
case Nil => Nil
}
).mkString
def subCaseClassSymbols(u: Universe)(root: u.ClassSymbol): Set[u.ClassSymbol] = {
val subclasses = root.knownDirectSubclasses
val cast = subclasses.map(_.asInstanceOf[u.ClassSymbol])
val partitioned = mapped.partition(_.isCaseClass)
partitioned match {
case (caseClasses, regularClasses) => caseClasses ++ regularClasses.flatMap(r => subCaseClassSymbols(u)(r))
}
}
def ctorsForSymbol(u: Universe)(atc: u.Type)(caseClass: u.ClassSymbol): (u.DefDef, u.DefDef) = {
import u._
import internal._
// these didn't help
// def clearTypeSymbol(s: Symbol): TypeSymbol = internal.newTypeSymbol(NoSymbol, s.name.toTypeName, s.pos, if(s.isImplicit)Flag.IMPLICIT else NoFlags)
// def clearTypeSymbol2(s: Symbol): TypeSymbol = internal.newTypeSymbol(NoSymbol, s.name.toTypeName, NoPosition, if(s.isImplicit)Flag.IMPLICIT else NoFlags)
// def clearTypeDef(d: TypeDef): TypeDef = internal.typeDef(clearTypeSymbol(d.symbol))
val companionSymbol: Symbol = caseClass.companion
val info: Type = caseClass.info
val primaryCtor: Symbol = caseClass.primaryConstructor
val method = primaryCtor.asMethod
val typeParams = info.typeParams.map(internal.typeDef(_))
// val typeParams = info.typeParams.map(s => typeDef(newTypeSymbol(NoSymbol, s.name.toTypeName, NoPosition, NoFlags)))
// val typeParams = info.typeParams.map(s => internal.typeDef(clearTypeSymbol2(s)))
val typeArgs = info.typeParams.map(_.name)
val paramLists = method.paramLists.map(_.map(internal.valDef(_)))
val argLists = method.paramLists.map(_.map(_.asTerm.name))
val baseType = info.baseType(atc.typeSymbol)
val List(returnType) = baseType.typeArgs
val methodName = TermName(smartName(caseClass.name.toString))
val wellTyped =
q"""
def $methodName[..$typeParams](...$paramLists): $baseType =
$companionSymbol[..$typeArgs](...$argLists)
"""
wellTyped
}
}
P.S. I have been experimenting with toolbox.untypecheck / typecheck per this article but haven't found a working combination.
you need using
clas.typeArgs.map(_.toString).map(name => {
TypeDef(Modifiers(Flag.PARAM),TypeName(name), List(),TypeBoundsTree(EmptyTree, EmptyTree))
}
replace
info.typeParams.map(p => TypeName(p.name.toString))
it si my code
object GetSealedSubClass {
def ol3[T]: Any = macro GetSealedSubClassImpl.ol3[T]
}
class GetSealedSubClassImpl(val c: Context) {
import c.universe._
def showInfo(s: String) =
c.info(c.enclosingPosition, s.split("\n").mkString("\n |---macro info---\n |", "\n |", ""), true)
def ol3[T: c.WeakTypeTag]: c.universe.Tree = {
//get all sub class
val subClass = c.weakTypeOf[T]
.typeSymbol.asClass.knownDirectSubclasses
.map(e => e.asClass.toType)
//check type params must ia s sealed class
if (subClass.size < 1)
c.abort(c.enclosingPosition, s"${c.weakTypeOf[T]} is not a sealed class")
// get sub class constructor params
val subConstructorParams = subClass.map { e =>
//get constructor
e.members.filter(_.isConstructor)
//if the class has many Constructor then you need filter the main Constructor
.head.map(s => s.asMethod)
//get function param list
}.map(_.asMethod.paramLists.head)
.map(_.map(e => q"""${e.name.toTermName}:${e.info} """))
val outfunc = subClass zip subConstructorParams map {
case (clas, parm) =>
q"def smartConstructors[..${
clas.typeArgs.map(_.toString).map(name => {
TypeDef(Modifiers(Flag.PARAM), TypeName(name), List(), TypeBoundsTree(EmptyTree, EmptyTree))
})
}](..${parm})=${clas.typeSymbol.name.toTermName} (..${parm})"
}
val outClass =
q"""
object Term{
..${outfunc}
}
"""
showInfo(show(outClass))
q"""{
$outClass
Term
}
"""
}
}
using like this
sealed trait Hello[A]
case class Ohayo[A, B](a: (A, B)) extends Hello[A]
object GetSealed extends App {
val a = GetSealedSubClass.ol3[Hello[_]]
val b=a.asInstanceOf[ {def smartConstructors[A, B](a: (A, B)): Ohayo[A, B]}].smartConstructors(1, 2).a
println(b)
}