Почему ассоциативность операторов работает по-разному для методов расширения Scala 3 и обычных методов?

Когда я вручную преобразовал код 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 и не нашел ничего о том, как связываются пользовательские операторы.

1 ответ

Ваша ментальная модель просто нуждается в доработке.

Напомним, что инфикс x op y обессахаривать x.op(y), кроме случаев, когда операция заканчивается двоеточием, тогда это y.op:(x). Это верно, если op() является собственным для параметра экземпляра или добавленного расширения, которое в Scala-2 обрабатывается промежуточным неявным классом.

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

Скала-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)"

Более подробную информацию можно найти здесь и здесь.

Другие вопросы по тегам