Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Finding implicit method definitions in macro context

I understand the basic concept of macros in Scala, but currently fail to do this (simple?) job:

  • Find all implicit defs/vals currently visible to the compiler to transform from a given type to another one.

What I'd expect to get is a List of Method objects or something similar. I already played around with enclosingImplicits but always get an empty list and do not really know where to look next.

What do I need to do to get the list I am looking for?

like image 330
lena_punkt Avatar asked Apr 23 '15 20:04

lena_punkt


People also ask

When to use implicit in Scala?

Scala implicit allows you to omit calling method or parameter directly. For example, you can write a function that converts int to/from string explicitly but you can ask the compiler to do the same thing for you, implicitly.

What is implicit object in Scala?

In Scala, objects and values are treated mostly the same. An implicit object can be thought of as a value which is found in the process of looking up an implicit of its type.

What is implicit class in Scala?

Scala 2.10 introduced a new feature called implicit classes. An implicit class is a class marked with the implicit keyword. This keyword makes the class's primary constructor available for implicit conversions when the class is in scope. Implicit classes were proposed in SIP-13.

What is Scala macros?

In Scala macros are a way to implement compile time reflection. One advantage that macros possess is that they are based on the same API which is also used for Scala runtime reflection and is provided in 'scala. reflect.


1 Answers

There can be only one implicit from type A to B in the context (or you get ambigious implicit), so if you want to find it:

import reflect.macros.Context, scala.language.experimental.macros

def fImpl(c: Context): c.Expr[Unit] = {
  import c.mirror._         
  println(c.inferImplicitValue(typeOf[Int]))
  c.universe.reify( () )
}
def f = macro fImpl

scala> f
<empty>

scala> implicit val a = 5
a: Int = 5

scala> f
$line24.$read.$iw.$iw.$iw.$iw.a

scala> implicit val b = 5
b: Int = 5

scala> f //result will be empty, but error printed to the log
error: ambiguous implicit values:
 both value a of type => Int
 and value b of type => Int
 match expected type Int
<empty>

To find implicit method:

def fImpl(c: Context): c.Expr[Unit] = {
      import c.mirror._       
      println(c.inferImplicitValue(typeOf[String => Int]))        
      c.universe.reify( () )
 }
def f = macro fImpl

scala> f
<empty>

scala> implicit def aaa(a: String) = 5
warning: there was one feature warning; re-run with -feature for details
aaa: (a: String)Int

scala> "A" : Int
res10: Int = 5

scala> f
{
  ((a: String) => $line47.$read.$iw.$iw.$iw.$iw.$iw.$iw.aaa(a))
}

If silent parameter is false (true by default), TypecheckException will be thrown in case of an inference error. So you could analize it to find list of ambigious implicits.

P.S. If type B is uncknown - there is no (documented) way to find all implicits using macroses: openImplicits/enclosingImplicits just looking for implicits being materialized in context of macro-expansion - not for all, that exists in the context. Compiler-plugin may help, but it's not so easy then.

If you really decide to try "compiler-plugin" way - the logic of finding implicits is implemented here. Here you can find compiler's Context (not same as macro's) and its implicitss field, which contains all implicits in the context (but it's not so trivial to obtain appropriate context).


And I shouldn't tell you but there is a tricky and unsafe hack to lift from macro Context to compiler's level and do what you want:

 scala>  def fImpl(c: Context): c.Expr[Unit] = {
 |       val cc = c.asInstanceOf[reflect.macros.contexts.Context]
 |       println(cc.callsiteTyper.context.implicitss.flatten)
 |       c.universe.reify( () )
 |  }
fImpl: (c: reflect.macros.Context)c.Expr[Unit]

scala> def f = macro fImpl

scala> f //I've defined aaaaaaaa etc. implicits while playing with that
List(aaaaaaaa: ?, lllllllllllllllllllllzzzz: ?, lllllllllllllllllllll: ?, lllllllllllllllllllll: ?, aaa: ?, aaa: ?, aaa: ?, aaa: ?, aaa: ?, aaa: ?, b: ?, a: ?, macros: ?, RuntimeClassTag:

Anyway, you have to analize a list of ImplicitInfo to obtain implicits you're looking for and it may not be trivial, as you can see from Analizer's sources, but at least it's possible to get approximate result, which may be fine for your needs. But again, it's better to do that very very very careful, as structures you working with are mutable and methods are not pure. And, as @Eugene Burmako noticed, this solution doesn't give you implicits from companion object.

like image 87
dk14 Avatar answered Oct 17 '22 01:10

dk14