我遇到一个问题,即在定义和使用一元运算符时会发生隐式()=>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_!
删除不必要的括号解决了我们的问题。谢谢!
答案 0 :(得分: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
第一种情况是该功能的应用。不打字检查。
第二种情况是什么?它不是隐式地添加parens以将其转换为应用程序,而是首先进行eta扩展以创建一个类型检查的函数。
有人建议首先添加parens(规范中的“空应用程序”),这样它的行为与第一种情况相同。
这是关于如何处理前缀op的规范措辞:
http://www.scala-lang.org/files/archive/spec/2.11/06-expressions.html#prefix-operations
前缀操作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扩展:
我倾向于更进一步,弃用eta-expansion给出一个 期望的Function0类型,并避免使用SAM类型触发它 从一开始就是相同的形状。
这太糟糕了,因为这会成为一个不错的小益智游戏。