Почему ассоциативность операторов работает по-разному для методов расширения 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)"