【问题标题】:Two different uses of implicit parameters in Scala?Scala 中隐式参数的两种不同用法?
【发布时间】:2019-01-01 06:58:26
【问题描述】:

(我对 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,从 someFunction 内到 someOtherFunction)都无法编译。

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

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

【问题讨论】:

  • 我认为第二个澄清的问题已经得到回答,因为需要大量的代码扭曲来演示所请求的转换/限制。
  • “另外,澄清一下,我的意思是是否有语言结构允许这样做”不,没有。

标签: scala implicit


【解决方案1】:

第一个问题

这背后的原理是什么?

答案可能基于意见。

有没有一种方法可以实现一个而没有另一个?

是的,但如果您想实际使用该参数,这比我最初想象的要复杂一些:

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

【讨论】:

  • 谢谢,@Alexey。这是有道理的,回答了我的问题,但并没有完全回答我的意思要问的问题:) 编辑了问题以澄清。
【解决方案2】:

理由很简单:

  • 已作为显式传递的内容将保持显式状态
  • 已标记为隐式的内容将保持隐式

我不认为任何其他组合(例如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 的解决方案相同:我们引入一个隐藏隐式参数的虚拟变量,然后在只有虚拟变量可见的范围内编写方法体。唯一的区别是()-value 在shadowing 实现内部传递,因此我们不必显式分配0。它并没有使代码更短,但它可能更清楚地表达了意图。

【讨论】:

  • 谢谢,@Andrey。这是有道理的,回答了我的问题,但并没有完全回答我的意思要问的问题:) 编辑了问题以澄清。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-07-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多