1

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.

2
  • Why do you need reflection low-level api? Why can't you use quotation def myMacroImpl(using Quotes): Expr[PolyFunction] = '{ new PolyFunction { def apply[X](a: X): X = a } }? Because this is just an example and your actual use case is more complicated, yeah? Commented Jan 17, 2023 at 4:16
  • 1
    @DmytroMitin This is a proof of concept only example, In practice, my aim is to create a wrapper around every polymorphic function that I receive as input with an arbitrary number of type and value parameters, with the same type signature, and quote patterns are not good at matching arbitrary inputs. (or are they?...) Commented Jan 17, 2023 at 5:00

1 Answer 1

2

Try to use PolyType along with MethodType

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)
        }),
        Flags.Override,
        Symbol.noSymbol
      )
    )

  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, 3.7.0.

Method Override with Scala 3 Macros

Scala3: Crafting Types Through Metaprogramming?

`tq` equivalent in Scala 3 macros


For a higher-kinded X[_], a PolyType can be created as follows: for example

PolyType(List("X", "Y"))(_ => List(
  TypeBounds.upper(
    TypeLambda(List("_$1"), _ => List(TypeBounds.empty), _ => TypeRepr.of[Any])
  ),
  TypeBounds.empty,
), polyType => {
  val xTypeParam = polyType.param(0)
  val yTypeParam = polyType.param(1)
  val xyType = AppliedType(xTypeParam, List(yTypeParam))
  MethodType(List("a"))(_ => List(xyType), _ => xyType)
})

is for def apply[X[_], Y](a: X[Y]): X[Y] = a.

Sign up to request clarification or add additional context in comments.

2 Comments

Any idea how to create the PolyType for hkt X[_]?
@dev I added example to my answer.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.