question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Referring to outer class in implicit inline breaks Expr summoning mechanism

See original GitHub issue

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

Repo

https://github.com/deusaquilus/anyval_encoder_issue

Issue Analytics

  • State:open
  • Created 2 years ago
  • Comments:9 (9 by maintainers)

github_iconTop GitHub Comments

1reaction
deusaquiluscommented, Apr 25, 2021

I think I found a working workaround. If you make it return a function that takes the EncoderContext and pass the encoder context afterward it works. I.e. if you do this:

object MappedEncoderMaker:
  inline def apply[T]: EncoderContext => Encoder[T] = ${ applyImpl[T] }
  def applyImpl[T: Type](using Quotes): Expr[EncoderContext => Encoder[T]] =
    import quotes.reflect._
    println(s"===== Creating Instance for: ${Printer.TypeReprShortCode.show(TypeRepr.of[T])}")
    '{ (ctx: EncoderContext) => new Encoder[T] { def encode(m: T) = ctx.encode[T](m) } }

… and then call it like this:

class EncoderContext { self =>
  def encode[Cls](cls: Cls) = List(cls.toString)
  implicit inline def anyClsEncoder[Cls]: Encoder[Cls] =
    MappedEncoderMaker[Cls].apply(self)
}

Hope this info helps.

0reactions
deusaquiluscommented, Apr 25, 2021

Not sure if it helps but if you make def encode[Cls] then it works. Unfortunately not useful for me as a workaround because I need this method to be virtual.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Is "inline" implicit in C++ member functions defined in class ...
The C++ ISO standard says: A function defined within a class definition is an inline function. But, this doesn't mean the function will ......
Read more >
CanThrow Capabilities - Scala 3 - EPFL
The CanThrow Capability. In the effects as capabilities model, an effect is expressed as an (implicit) parameter of a certain type. For exceptions...
Read more >
Dissecting Razor, part 9: Inline Helpers - SLaks.Blog
An inline helper is created by writing @<tag>Content</tag> as an expression. This creates a lambda expression that takes a parameter called ...
Read more >
D78655 [CUDA][HIP] Let lambda be host device by default
Lambdas are also implicitly constexpr whereas a function need to ... possible to capture the mutable lambda by reference by another lambda.
Read more >
Semantics-Preserving Inlining for Metaprogramming
3.1 Members and Bridges. An inline method may refer in its body to the this reference of the current class or to any...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found