2

当我手动将 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

我的心智模型是:

  • 对于以 结尾的方法:,接收者是右边的东西
  • 扩展方法只是方法:就好像方法被添加到类中一样

我的心理模型似乎是错误的。解释正在发生的事情的正确方法是什么?

链接会有所帮助,我检查了 Scala 3 文档并没有找到任何关于自定义运算符如何关联的信息。

更新

我尝试infix在之前添加关键字def,但它不会更改此示例中打印的内容。

4

1 回答 1

1

你的心智模型只需要一些调整。

回想一下,中缀对x op y进行去糖x.op(y),除非操作以冒号结尾,否则就是y.op:(x). 无论op()方法是实例参数的本机方法还是添加的扩展,这都适用,在 Scala-2 中,由中间隐式类处理。

implicit class IntermediateClass(instance: Int) {
  def op(arg: Int) = ???
}

extension另一方面,Scala-3只是一个接收两个 curried 参数的方法。因此中缀调用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)"

更多细节可以在这里这里找到。

于 2021-05-26T04:19:59.597 回答