为什么一元运算符启用()=> X隐式转换?

use*_*468 1 scala

我遇到一个问题,()=>X当我定义和使用一元运算符时发生隐式转换.以下是一个最小的例子:

class Gate {
  def unary_!(): Gate = this
}

class Foo {
  private def foo(f: () => Gate) = 1
  val gate = new Gate

  // Compiles, -Xprint:typer shows it becomes
  //   Foo.this.foo({
  //     (() => Foo.this.gate.unary_!())
  //   })
  foo(!gate)

  // Does not compile, we get
  //  error: type mismatch;
  //  found   : Gate
  //  required: () => Gate
  //  foo(gate)
  foo(gate)
}
Run Code Online (Sandbox Code Playgroud)

这种() => Gate转换发生在哪里unary_!?为什么它只发生?


编辑

谢谢你的回答!我提出这个问题,因为埃塔扩张(从X() => X块为我们定义的另一个隐式转换X,并希望发生的.删除不必要的括号从unary_!解决问题的我们,谢谢!

som*_*ytt 5

这只是eta-expansion,它将方法转换为函数.

http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#eta-expansion

触发器是期望的类型是函数.

该规范中的链接不明确,但扩展为6.26.5.

比较这两种形式:

scala> foo(gate.unary_!())
<console>:11: error: type mismatch;
 found   : Gate
 required: () => Gate
              foo(gate.unary_!())
                              ^

scala> foo(gate.unary_!)
res3: Int = 1
Run Code Online (Sandbox Code Playgroud)

第一种情况是该功能的应用.不打字检查.

第二种情况是什么?它不是隐式地添加parens以将其转换为应用程序,而是首先进行eta扩展以创建一个类型检查的函数.

有人建议首先添加parens(规范中的"空应用程序"),这样它的行为与第一种情况相同.

这是关于如何处理前缀op的规范措辞:

http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#prefix-operations

前缀操作op; e由前缀运算符op组成,前缀运算符op必须是标识符'+',' - ','!'之一 或'〜'.表达式op; e等同于后缀方法应用程序e.unary_op.

但是这个例子表明它是成员选择,但不是应用程序.

这是您的方法定义中没有parens的反例:

scala> class Gate { def unary_! : Gate = this }
defined class Gate

scala> def foo(f: () => Gate) = 1
foo: (f: () => Gate)Int

scala>   val gate = new Gate
gate: Gate = Gate@2db0f6b2

scala> foo(!gate)
<console>:11: error: type mismatch;
 found   : Gate
 required: () => Gate
              foo(!gate)
                  ^
Run Code Online (Sandbox Code Playgroud)

在那里,您首先得到简单的评估,即6.26.2中的第一次转换.

关于相关票证的更多示例.

在链接票证上的评论不仅表明改变了隐含的顺序,而且禁止在这种情况下进行eta扩展:

考虑到预期的Function0类型,我倾向于进一步讨论eta-expansion,并避免从一开始就使用相同形状的SAM类型触发它.

这太糟糕了,因为这会成为一个不错的小益智游戏.