Description
Compiler version
RC3
Minimized code
Create a simple class that does something which we want an implicit instance of e.g. an Encoder.
class Encoder[T]:
def apply(element: T): List[String] = List(element.toString)
class EncoderContext { self =>
def encode[Cls](cls: Cls) = List(cls.toString)
implicit inline def anyClsEncoder[Cls]: Encoder[Cls] =
MappedEncoderMaker[Cls](self)
}
Then create the macro MappedEncoderMaker
in which we will access this EncoderContext.
I decided to also add a print statement to see if the macro is being invoked.
object MappedEncoderMaker:
inline def apply[T](inline ctx: EncoderContext): Encoder[T] = ${ applyImpl[T]('ctx) }
def applyImpl[T: Type](ctx: Expr[EncoderContext])(using Quotes): Expr[Encoder[T]] =
import quotes.reflect._
println(s"===== Creating Instance for: ${Printer.TypeReprShortCode.show(TypeRepr.of[T])}")
'{ new Encoder[T] { def encode(m: T) = $ctx.encode[T](m) } }
Creating a summoning macro that will attempt to summon the encoder created by our macro:
object SummonAndEncode {
inline def apply[Cls <: AnyVal](cls: Cls): Unit = ${ applyImpl[Cls]('cls) }
def applyImpl[Cls <: AnyVal: Type](cls: Expr[Cls])(using Quotes): Expr[Unit] = {
import quotes.reflect._
Expr.summon[Encoder[Cls]] match
case Some(value) => println(s"ENCODER FOUND")
case None => println("ENCODER NOT FOUND")
'{ () }
}
}
Finally, create a class that will import the context and attempt to summon the Encoder.
case class Wrap(value: String) extends AnyVal
def main(args: Array[String]): Unit = {
val ctx = new EncoderContext()
import ctx._
val w = new Wrap("stuff")
SummonAndEncode[Wrap](w)
}
Output
The result is that the summoning macro will not be able to find the Encoder. However, right before, I can see that the MappedEncoderMaker is actually working as intended.
[info] compiling 1 Scala source to....
===== Creating Instance for: Wrap
ENCODER NOT FOUND
Expectation
Then encoder for the Wrap
object should be successfully summoned.
Note that if you remove the self
from the MappedEncoderMaker[Cls](self)
call i.e:
class EncoderContext { self =>
def encode[Cls](cls: Cls) = List(cls.toString)
implicit inline def anyClsEncoder[Cls]: Encoder[Cls] =
MappedEncoderMaker[Cls](???) // <- Remove the 'self' reference here.
}
Then the encoder will be summoned:
===== Creating Instance for: Wrap
[info] compiling 1 Scala source to...
ENCODER FOUND