Pre-SIP: Assignment Operator Precedence Exceptions (!=, <=, >=)


#1

History

Date Version
Mar 10th 2019 Pre-SIP

Introduction/Motivation/Abstract

This SIP proposes to change the Scala language specification’s assignment operator language definition1, by modifying the 2nd exception from the operator is one of (<=), (>=), (!=) to the operator begins with one of (<=), (>=), (!=).

The aim is to support additional comparison operators that also begin with <=, >=, and !=, and also end in =.

Motivating Example

This was encountered when creating a TypeSafeEquals2 implementation with the desired === and !== operators. When used, it generates a compile exception if the types don’t match, avoiding an unintended comparison of unrelated types that would always fail. However, the existing language specification will generate a compile error when using !==:

scala> 1 !== -1 || true
<console>:15: error: value || is not a member of Int
       1 !== -1 || true
                ^

Since !== ends in =, its’ precedence is the same as an assignment operator, so 1 !== -1 || true gets evaluated as 1 !== (-1 || true) by the Scala compiler.

By modifying the exceptions definition to “begins with”, this should get compiled with the desired precedence of (1 !== -1) || true.

Counter-Example

This can be worked around by using other operators:

scala> 1 =!= -1 || true
res0: Boolean = true

Implementation

    def isOpAssignmentName: Boolean = name match {
      case raw.NE | raw.LE | raw.GE | EMPTY =>
        false
      case name: SimpleName =>
        name.length > 0 && name.last == '=' && isOperatorPart(name.head) &&
        (name.length < 3 || !(name.startsWith(raw.NE) || name.startsWith(raw.GE) || name.startsWith(raw.LE))
      case _ =>
        false
    }

Drawbacks

This is a piece of the language specification that may have never been changed and has unintended consequences which are unknown.

The scope of the change would affect any custom assignment operators that use the patterns:

  • !=[.*]=
  • >=[.*]=
  • <=[.*]=

If there are DSLs or other implemented assignment operators using the above patterns, then the scala code would not compile without modifying the first = character to another.

Alternatives

As described in the counter-example, there are other operators available, and is not core functionality that is currently “broken” or has no alternative.

References

  1. Scala Specification
  2. TypeSafeEquals

#2

Scala 3 shouldn’t change anything here, but what you’re suggesting sounds reasonable, I would encourage to write a SIP for it.


#5

Overall, I think it’s an interesting idea.

From the marketing perspective, it might be good to call this “Relational Operator Clause” instead of “Assignment Operator Precedence Exceptions”, so we can discuss what constitutes relational (or comparison) operators.

The current exception clause:

… that ends in an equals character “ = ”, with the exception of operators for which one of the following conditions holds:

  1. the operator also starts with an equals character, or
  2. the operator is one of (<=) , (>=) , (!=) .

is effectively defining the relational operators, and you’re proposing to expand that to the following:

… that ends in an equals character “ = ”, with the exception of relational operators for which the following condition holds:

  • the operator also starts with one of =, <= , >= , != .

Given Scala 3 is adding some sort of Eq typeclass with its multiversal equality, I am not sure about the committee’s appetite to accommodate DSL in this area.


#6

I am extremely wary of any change to the precedence rules of any language. This is a recipe for silent breakages in codebases that we know nothing about, for no real reason. And by no real reason, I mean that yes, sure, you can write a long post explaining why it should be the way you suggest, but I can always write another post explaining why it should be another way, and there is never any objective measure we can make. So when in doubt, apply the motto “If it ain’t broke, don’t fix it”.


#7

I like @eed3si9ns generalization of the topic, I think that makes sense. Regarding the “counter examples” argument @sjrd, I guess that’s the part I’m struggling with. Is there an argument/“another long post” on why “<==” SHOULD be an assignment operator? Or is this more closely to an oversight?

IIRC, was there some type of community repository of popular source code that I could use to find counter-examples? Could anyone point me in that direction?


#8

Whether such a post already exists or not is beside the point. The point is that it could exist. Also the fact that it does not exist says nothing at this point, because it already is considered an assignment operator, so someone who wants that to be the correct behavior is simply using it, not writing posts praising how the existing precedence is good.


#9

I think a better question will be why do we need === (and the rest) and not just use == (and the rest)?
I know there are issues with ==, but they can be resolved if we remove it from Any and implement it using extension methods. See the following proposal: