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



当我定义并使用一元运算符时,我遇到了一个隐式()=>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)
}

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


编辑

谢谢你的回答!我提出这个问题是因为Eta扩展(从X() => X)阻止了我们为X定义的另一个隐式转换。从unary_!中删除不必要的括号为我们解决了问题。谢谢!

这只是eta展开,它将方法转化为函数。

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

触发器是期望的类型是一个函数。

规范中的链接不明确,但扩展版本为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

第一种情况是函数的应用程序。不进行类型检查。

第二种情况是什么?它不会隐式添加parens以将其转换为应用程序,而是会首先展开eta以生成一个函数,该函数将进行类型检查。

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

以下是如何处理前缀操作的规范措辞:

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

前缀运算op;e由前缀运算符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)
^

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

相关票证上的更多示例。

链接票证上的一条评论建议,不仅要更改隐含词的顺序,还要禁用这种情况下的eta扩展:

如果预期类型的Function0,并避免使用SAM类型的从一开始就是一样的形状。

这太糟糕了,因为这会让人有点困惑。

最新更新