为什么运算符关联性对于 Scala 3 扩展方法和常规方法的工作方式不同?
Why does operator associativity work differently for Scala 3 extension methods and regular methods?
当我手动将 Scala 2 代码转换为 Scala 3 时,我的 DSL 的运算符优先级发生了变化,我花了很长时间进行调试和修复。似乎 :
的处理方式不同:
extension (i1: Int) def ~>:(i2: Int) = i1 < i2
extension (i1: Int) def ~>(i2: Int) = i1 < i2
class Wrap(val i: Int):
def ~>:(w: Wrap) = i ~>: w.i
def ~>(w: Wrap) = i ~> w.i
// `Wrap` preserves `~>`
println(1 ~> 2) // true
println(Wrap(1) ~> Wrap(2)) // true
// `Wrap` does not preserve `~>:`
println(1 ~>: 2) // true
println(Wrap(1) ~>: Wrap(2)) // false
我的心智模型是:
- 对于以
:
结尾的方法,接收者是右边的东西
- 扩展方法只是方法:就好像方法被添加到 class
我的心智模型似乎是错误的。解释正在发生的事情的正确方法是什么?
链接会有所帮助,我检查了 Scala 3 文档,但没有找到有关自定义运算符如何关联的任何信息。
更新
我尝试在 def
之前添加 infix
关键字,但它不会改变此示例中打印的内容。
您的心智模型需要一些调整。
回想一下,中缀 x op y
去糖化为 x.op(y)
,除非 op 以冒号结尾,否则它是 y.op:(x)
。无论 op()
方法是实例参数的本机方法还是添加的扩展,这都适用,在 Scala-2 中,它由中间隐式处理 class.
implicit class IntermediateClass(instance: Int) {
def op(arg: Int) = ???
}
另一方面,Scala-3 extension
只是一个接收两个柯里化参数的方法。所以中缀调用 leftOfOp op rightOfOp
将始终这样处理:
extension (leftOfOp: Int)
def op(rightOfOp: Int) = ???
op
后面有没有:
也是一样的。但是,虽然定义站点的代码以这种方式保持一致,但调用站点的关联性正如您所期望的那样。
extension (left: String)
def @:(right: String):String = s"$left.@:($right)"
def @@(right: String):String = s"$left.@@($right)"
"TOP" @: "MID" @: "END" //"TOP.@:(MID.@:(END))"
"top" @@ "mid" @@ "end" //"top.@@(mid).@@(end)"
当我手动将 Scala 2 代码转换为 Scala 3 时,我的 DSL 的运算符优先级发生了变化,我花了很长时间进行调试和修复。似乎 :
的处理方式不同:
extension (i1: Int) def ~>:(i2: Int) = i1 < i2
extension (i1: Int) def ~>(i2: Int) = i1 < i2
class Wrap(val i: Int):
def ~>:(w: Wrap) = i ~>: w.i
def ~>(w: Wrap) = i ~> w.i
// `Wrap` preserves `~>`
println(1 ~> 2) // true
println(Wrap(1) ~> Wrap(2)) // true
// `Wrap` does not preserve `~>:`
println(1 ~>: 2) // true
println(Wrap(1) ~>: Wrap(2)) // false
我的心智模型是:
- 对于以
:
结尾的方法,接收者是右边的东西 - 扩展方法只是方法:就好像方法被添加到 class
我的心智模型似乎是错误的。解释正在发生的事情的正确方法是什么?
链接会有所帮助,我检查了 Scala 3 文档,但没有找到有关自定义运算符如何关联的任何信息。
更新
我尝试在 def
之前添加 infix
关键字,但它不会改变此示例中打印的内容。
您的心智模型需要一些调整。
回想一下,中缀 x op y
去糖化为 x.op(y)
,除非 op 以冒号结尾,否则它是 y.op:(x)
。无论 op()
方法是实例参数的本机方法还是添加的扩展,这都适用,在 Scala-2 中,它由中间隐式处理 class.
implicit class IntermediateClass(instance: Int) {
def op(arg: Int) = ???
}
另一方面,Scala-3 extension
只是一个接收两个柯里化参数的方法。所以中缀调用 leftOfOp op rightOfOp
将始终这样处理:
extension (leftOfOp: Int)
def op(rightOfOp: Int) = ???
op
后面有没有:
也是一样的。但是,虽然定义站点的代码以这种方式保持一致,但调用站点的关联性正如您所期望的那样。
extension (left: String)
def @:(right: String):String = s"$left.@:($right)"
def @@(right: String):String = s"$left.@@($right)"
"TOP" @: "MID" @: "END" //"TOP.@:(MID.@:(END))"
"top" @@ "mid" @@ "end" //"top.@@(mid).@@(end)"