Туннельный неявный параметр в тело функции вызова по имени

Рассмотрим следующий фрагмент кода:

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)
} 

который в настоящее время не компилируется со следующим сообщением:

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

Мой вопрос: можно ли сделать неявный параметр доступным для тела функции вызова по имени?

EDIT Я попытался заставить его работать с реализацией макроса, как предложил Алексей Романов

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)

}

Макрос отладки я вижу, что он правильно расширен в

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

К сожалению, он также не компилируется с той же ошибкой, что неявное не найдено.

Копаясь в проблеме, я нашел обсуждения здесь и jira выдает https://issues.scala-lang.org/browse/SI-5774

Итак, вопрос тот же: возможно ли в этом случае туннелировать неявно в функцию todo?


person llirik    schedule 20.09.2015    source источник
comment
Понятно, что я могу передать имплицит как параметр типа todo(parameter), но в этом случае весь смысл имплицита пропадает. Мой вопрос был больше о том, почему неявное определение, определенное в run, не туннелируется в тело запуска вызывающей стороны.   -  person llirik    schedule 20.09.2015


Ответы (2)


Проще говоря - нет. implicit требует, чтобы из кода было очевидно, что происходит. Если вы хотите, чтобы что-то неявно передавалось в функцию, она должна иметь параметр implicit, чего нет в вашей функции f.

Это отличный источник мудрости, связанный с implicit: http://docs.scala-lang.org/tutorials/FAQ/finding-implicits.html

person Rado Buransky    schedule 20.09.2015
comment
Можно определить f: implicit? => Unit? - person llirik; 20.09.2015
comment
Вы можете сделать это так: f: ()(implicit i: Int) => Unit. Но тогда вам придется вызывать f со скобками типа f() вместо вызова по имени. - person Rado Buransky; 20.09.2015
comment
Можете быть более конкретными? Если я перейду на f: ()(implicit i: Int) => Unit, как вы предложили, IDE жалуется, что это недопустимое определение функции - person llirik; 20.09.2015

Мой вопрос был больше о том, почему неявное определение в run не туннелируется в тело запуска вызывающей стороны.

Потому что это просто не то, как работает лексическая область видимости. Это не входит в область определения тела. У вас есть две хорошие альтернативы:

  1. def run(f: Int => Unit) = f(1)
    
    run { implicit i => 
      todo // can use i here
    }
    
  2. Сделайте run макросом. Это должно быть хотя бы приблизительно (адаптировано из SI-5778), к сожалению, я пока не могу проверить:

    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
      }"""
    }
    
person Alexey Romanov    schedule 20.09.2015
comment
Первый вариант - это именно то, что у меня есть сейчас, проблема сложна, если у меня теперь есть f: (Int, String) => Unit, я не могу использовать run { implicit i =>, чтобы сделать оба параметра доступными как неявные, мне понадобится run { (a,b) => implicit val aa = a; implicit bb = b ..., который в случае dsl становится шаблонным кодом. Как макросы могут помочь мне здесь? - person llirik; 20.09.2015
comment
Я до сих пор не понимаю, как создание run макроса решит неявную проблему не найденного в caller. Вы можете остановиться на этом? - person llirik; 20.09.2015
comment
Если вы сделаете аргумент c.Tree, расширение макроса произойдет до проверки типов, неявного поиска и т. д. Таким образом, вы просто вставляете нужные имплициты, и они будут доступны к тому моменту, когда они потребуются. Смотрите отредактированный ответ. - person Alexey Romanov; 20.09.2015
comment
К сожалению, это тоже не работает, с той же ошибкой. Я обнаружил, что проблема обсуждается на внутренних groups.google.com/forum/ #!topic/scala-internals/zvY_FR3hdJQ, но похоже, что это все еще не исправлено (теперь добавляются/находятся имплициты, определенные в макросах) - person llirik; 20.09.2015