Scala中隐式参数的两种不同用法?

时间:2018-12-26 11:23:47

标签: scala implicit

(我对Scala还是陌生的,希望这不是一个愚蠢的问题。)

据我所知,为函数implicit声明参数具有两个(相关但完全不同)的用法:

  1. 当编译器可以找到唯一合适的值(在调用范围内)传递时,它使在调用给定函数时显式传递一个相应的参数 是可选的。

  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

这似乎有些奇怪,不是吗?从第1行中删除implicit会使两个调用(从其他位置调用someFunction,从someOtherFunction内部调用someFunction)都无法编译。

这背后的原理是什么? (编辑:我的意思是官方的基本原理,以防在官方的Scala资源中找到任何内容。)

还有没有其他方法可以实现一个方法(即允许隐式地将参数传递给函数,而在调用其他函数时不允许在函数内隐式使用参数,和/或使用非隐式)调用其他函数时隐式参数)? (编辑:我稍微改变了这个问题。此外,为了澄清,我的意思是说是否存在一种允许这种语言的语言结构-通过手动阴影或类似方法达到效果)

2 个答案:

答案 0 :(得分:3)

第一个问题

  

这背后的原理是什么?

答案可能基于观点。

  

有没有其他方法可以达成目标?

是的,虽然要真正使用该参数,但比我最初想的要复杂一些:

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

答案 1 :(得分:3)

理由很简单:

  • 传递为显式的内容,保持显性
  • 已标记为隐式的内容,保持隐式

我认为任何其他组合(例如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 = {}
    
  • 隐式->显式:我只会使用Alexey Romanov的解决方案,但是可以想象,如果我们在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 = {}
    

    从本质上讲,它与Alexey Romanov的解决方案相同:我们引入了一个隐藏隐式参数的虚拟变量,然后在仅该虚拟变量可见的范围内编写方法的主体。唯一的区别是在()实现内部传递了一个shadowing值,因此我们不必显式地分配一个0。它并不会使代码短很多,但是也许表达的意图更加清晰。