I have previously posted on Stack Overflow and created a bug on this topic.
As I mentioned in both of those places, I do understand how extensions are implemented and why that implementation impacts right-associative methods the way that it does. However, I believe that the implementation we have is confusing at best (violating POLA) and conflicts with several significant statements in the Scala 3 Book and the Reference Docs (I expand on this assertion in far more detail in my comment on the bug report linked above).
Specifically, the translation of extensions into methods with multiple parameter lists and no target (or “receiver” if you prefer) breaks expectations specifically with right-associative methods.
This design happens to not be a problem with left-associative methods since it all just aligns naturally, but for right-associative, developer expectations are broken. This can be seen in all of the posts, bugs, and questions about this issue (links here are just a sampling).
I’d add that it also makes reading and maintenance significantly more difficult. Prior to Scala 3’s extension
every sufficiently experienced Scala developer would have immediately known what this was and how to parse it mentally while reading:
a %: b
While we may not immediately know what the %:
operator does, we would know where to look for it. Namely on b
’s type.
In Scala 3, however, it is impossible know what is happening on that line of code without going and finding the implementation of %:
and determining if it is on b
’s type or on an extension of a
’s type.
Consider also type classes. In Scala 2 a right-associative method on a type class would behave exactly as you expect. To be completely honest, given the above ambiguity around handling of right-associative methods in the various places they can be defined, I actually have no idea what would happen in Scala 3 if I attempted to define a right-associative method in a type class. A type class is a class, so it seems as though it should behave like a right-associative method on any other class, but methods are added in an encapsulated extension
now, so maybe it will be inverse of what I expect with regard to target and operand at the call site?
While obviously, I could go write some code to test that out, the fact that it isn’t immediately obvious is exactly the problem I’m concerned about. While it seems to me that the community sentiment is opposed to right-associative methods (at least the way they have been prior to Scala 3), I cannot conceive of how breaking the model and adding significant confusion to the issue actually helps resolve anything except by perhaps abusing developers to the point that they drop the construct all-together.
I know that at this point Scala 3 is out there, being adopted, and changing this would be difficult. I’m certainly not opposed to taking on the challenge of championing a fix of this, but I acknowledge that it may be too big of an ask at this point. However, this seems like a significant enough wart in what I consider to otherwise be a truly delightful language, that it bears discussing in these terms (as opposed to just requesting the “how do I do this now” as in many of the other links I provided).
Am I alone in this concern or perhaps missing some significant context that would make this design make sense to me?