Два разных использования неявных параметров в Scala? - PullRequest
0 голосов
/ 26 декабря 2018

(Я довольно новичок в Scala, надеюсь, это не глупый вопрос.)

Из того, что я вижу, объявление параметра функции implicit имеет two (связанный, но совершенно другой) использует:

  1. Это делает явной передачу соответствующего аргумента при вызове данной функции необязательным, когда компилятор может найти уникальное подходящее значение дляpass (в области вызова).

  2. Это делает сам параметр подходящим значением для передачи другим функциям с неявными параметрами (при вызове их из заданногофункция).

В коде:

def someFunction(implicit someParameter: SomeClass) = {  // Note `implicit`
  ...
  // Note no argument supplied in following call;
  // possible thanks to the combination of
  // `implicit` in `someOtherFunction` (1) and
  // `implicit` in line 1 above (2)
  someOtherFunction
  ...
}

def someOtherFunction(implicit someOtherParameter: SomeClass) = {
  ...
}

implicit val someValue = new SomeClass(...)
// Note no argument supplied in following call;
// possible thanks to `implicit` (1)
someFunction

Это кажется несколько странным, не так ли?Удаление implicit из строки 1 приведет к тому, что оба вызова (на someFunction из другого места и на someOtherFunction из someFunction) не будут скомпилированы.

Что за этим стоит?( Редактировать: Я имею в виду, каково официальное обоснование, в случае, если что-либо можно найти в каком-то официальном ресурсе Scala.)

И есть ли способ добиться этогобез другого (то есть, чтобы неявно передавать аргумент функции, не позволяя неявно использовать его внутри этой функции при вызове других функций, и / или неявно использовать неявный параметр при вызове других функций)?( Edit: Я немного изменил вопрос. Также, чтобы уточнить, я имею в виду, есть ли языковая конструкция, позволяющая это - не достижение эффекта с помощью ручного затенения или подобного.)

Ответы [ 2 ]

0 голосов
/ 26 декабря 2018

Объяснение простое:

  • То, что было передано как явное, остается явным
  • То, что было помечено как неявное, остается неявным

Я не думаю, что любую другую комбинацию (например, implicit -> явная, не говоря уже о явной -> implicit) было бы легче понять.Я думаю, что основная идея заключалась в том, что можно установить некоторый общий неявный контекст, а затем определить целую кучу методов, которые ожидают одинаковых implicit переменных, описывающих установленный контекст.

Вот как можно перейти отнеявное явное и обратно:

  • неявное -> неявное (по умолчанию)

    def foo(implicit x: Int): Unit = {
      bar
    }
    
    def bar(implicit x: Int): Unit = {}
    
  • явное -> неявное:

    def foo(x: Int): Unit = {
      implicit val implicitX = x
      bar
    }
    
    def bar(implicit x: Int): Unit = {}
    
  • Неявный -> явный: я бы просто использовал решение Алексея Романова, но можно было бы представить, что если бы у нас был Predef следующий метод:

    def shadowing[A](f: Unit => A): A = f(())
    

    , то мыможно написать что-то вроде этого:

    def foo(implicit x: Int): Unit = {
      val explicitX = x
      shadowing { x =>
        // bar         // doesn't compile
        bar(explicitX) // ok
      }
    }
    
    def bar(implicit x: Int): Unit = {}
    

    По сути, это то же самое, что и решение Алексея Романова: мы вводим фиктивную переменную, которая скрывает неявный аргумент, а затем пишем тело метода в области видимости, где толькофиктивная переменная видна.Единственное отличие состоит в том, что () -значение передается внутри реализации shadowing, поэтому нам не нужно явно присваивать 0.Это не делает код намного короче, но, возможно, он выражает намерение немного яснее.

0 голосов
/ 26 декабря 2018

На первый вопрос

В чем причина этого?

ответы, скорее всего, основаны на мнении.

И есть ли способ достичь одного без другого?

Да, хотя это немного сложнее, чем я думал изначально, если вы действительно хотите использовать параметр:

def someFunction(implicit someParameter: SomeClass) = {
  val _someParameter = someParameter // rename to make it accessible in the inner block

  { 
    val someParameter = 0 // shadow someParameter by a non-implicit
    someOtherFunction // doesn't compile
    someOtherFunction(_someParameter) // passed explicitly
  }
}
...