简体   繁体   中英

Tunnel implicit parameter to call-by-name function body

Consider following code snippet:

object Example {

    def run(f: => Unit): Unit = {
        implicit val i = 1

        f
    }

    def caller(): Unit =
        run {
            todo
        }

    def todo(implicit i: Int): Unit =
        println(i)
} 

which currently is not compiling with following message:

Error:(14, 13) could not find implicit value for parameter i: Int
            todo
        ^ 

My question is it possible to make implicit parameter available to call-by-name function body?

EDIT I tried make it working with macro implementation as suggested by Alexey Romanov

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

object Macros {

  def run(f: => Unit): Unit = macro runImpl

  def runImpl(c : Context)(f: c.Tree) = {
      import c.universe._
      q"""{
        implicit val i: Int = 3
        $f
      }"""
  }
}

object Example extends App {

    Macros.run {
       todo
    }

    def todo(implicit i: Int): Unit =
       println(i)

}

Debugging macro i can see that it is correctly expanded into

{
   implicit val i: Int = 3
   Example.this.todo
}

Unfortunately it does not compiles as well with same error that implicit is not found.

Digging into issue i found discussions here and jira issues https://issues.scala-lang.org/browse/SI-5774

So question is the same: Is it possible to tunnel implicit into todo function in this case?

Simply said - no. implicit requires that it is obvious from the code what is going on. If you want anything to be passed to a function implicitly, it must have implicit parameter which is not the case of your f function.

This is a great source of wisdom related to implicit : http://docs.scala-lang.org/tutorials/FAQ/finding-implicits.html

My question was more about why implicit defined in run is not tunneled into caller's run body

Because that's simply not how lexical scoping works. It isn't in scope where the body is defined. You have two good alternatives:

  1.  def run(f: Int => Unit) = f(1) run { implicit i => todo // can use i here } 
  2. Make run a macro. This should be at least an approximation (adapted from SI-5778 ), unfortunately I can't test it at the moment:

     object Macros { def run(f: => Unit) = macro runImpl def runImpl(c : Context)(f: c.Tree) = q"""{ implicit val i: Int = 1 // some other implicits $f }""" } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM