Hi folks,

I was putting together a blog post on supporting Multiversal Equality
with Groovy. It's an optional feature in Scala 3:

https://docs.scala-lang.org/scala3/book/ca-multiversal-equality.html

Using the example (involving print and audio books) from the above
link, we can write an ad-hoc type checking extension something like
this:

beforeMethodCall { call ->
    if (call.methodAsString == 'equals') {
        lhsType = getType(call.objectExpression)
        rhsType = getType(call.arguments[0])
        if (lhsType != rhsType &&
                lhsType != classNodeFor(PrintedBook) &&
                rhsType != classNodeFor(AudioBook)) {
            addStaticTypeError("Invalid equality check: $lhsType.name
!= $rhsType.name", call)
            handled = true
        }
    }
}

This turns on strict equality checking except for the particular
ad-hoc exclusion listed for print  and audio books. So the following
would be true:

42.equals('foo') // STC error: int and String aren't compatible
aBook.equals(pBook) // STC error: due to the order we gave in the exclusion
pBook.equals(aBook) // compiles and returns true due to equals
definition (not shown)

The blog discusses how we could make it less ad-hoc if we wanted to by
introducing a CanEqual marker interface and so forth, but that's
off-topic for just now.

What I wanted to show is the same examples but using the '==' and '!='
operators, since that would be the typical Groovy style for this
scenario. Unfortunately, using the type checking extension DSL doesn't
currently work for binary operators. The swap from '==' to the
'equals' method call occurs well after type checking is finished. The
same would apply to operators eventually falling back to 'compareTo'.

You can still make it work by not using the DSL and writing your own
type checking extension, but that's significantly more work.

Our options seem to be:
(1) not trying to make this work
(2) modify operators to method call expressions earlier (might remove
some optimization steps)
(3) tweak StaticTypeCheckingVisitor#visitBinaryExpression to support
before/after method call hooks for known cases like equals/compareTo
with a pretend method call
(4) alter the TypeCheckingExtension interface with before/after binary
expression calls.

Note that while the multiversal equality example has some interest, it
isn't really what I am trying to fix here. It just shows a hole in the
type checking extension DSL support that I'd like to fix if we can
find a nice fix. If someone wanted to make it a type error to compare
'int's and 'float's (say), they would face the same issue but with
compareTo not equals.

Does anyone have strong opinions on this before I start having a play
and seeing what might work? In particular, a preference for option 3
or 4?

Cheers, Paul.

Reply via email to