Elvis operator for explicit nulls

In theory, an optimizer with whole program knowledge can do anything. In practice, my educated guess is that it would probably be fairly hard to do. Anything that requires coordinated changes of representation in different areas of the program is a challenge. The Scala.js linker doesn’t do this, and is unlikely to do it in the foreseeable future.

4 Likes

I think there is an intention to at some point have the scala compiler try unbox Options in pattern matching, as well as eliminate pairs of tupling and untupling, but I’m not sure when, if ever, it will exist. or if it exists now

2 Likes

My understanding is that we are waiting for Project Valhalla to deliver value types to the JVM.

1 Like

The optimizer can eliminate tupling and untupling.

-opt:box-unbox: Eliminate box-unbox pairs within the same method (also tuples, xRefs, value class instances). Enables unreachable-code.

Though in the small experiment I did it didn’t seem to work when pattern matching on the tuple. Which I would guess is the main (only?) use case for intra-method tupling and untupling.

2 Likes

I like Option[T] and also like optional chaining, how could a user space ?. operator be defined as shorthand for converting T | Null to Option[T], to avoid explicitly wrapping each nested call into an Option[T], and return a single default at the chain end.

nullishValueFromJavaInterop
   ?.methodMayReturnNull
   ?.otherMethodMayReturnNull
   .getOrElse(default)

There is joy in working the happy path where everything is defined and not null, and provide single fallback at the end, as you may have tried in other languages, the difference is they don’t work with an Option[T] but just allow chains to self short-circuit on the first null encounter.

2 Likes

This is a bad choice of operator because Rust uses it for early return and it’s absolutely magical there. Far cleaner than anything you can do with options for a wide variety of error-handling and data-absent cases.

It would be a shame to lose that parallel. Scala can absolutely do the same thing.

1 Like

Not only rust, but kotlin, typescript and some more, i don’t see what’s the problem with some “magic” for Option[T], i don’t believe in fallacies, like “X” language has it, but when many languages start to converge, maybe there is some principle or approach that’s worthy of consideration.

I’m not sure you understood my point.

There’s no problem with magic for Option[T]. It’s just if you have a choice between (1) Typescript and Kotlin’s ?. field/method access or null or (2) Rust’s return-the-disfavored-branch ? or (3) a new thing that is syntactic sugar for Option.apply, by far the most powerful and useful is (2).

The problem with ?. propagating nulls is that nulls infect everything. Now every call you have has to be ?., and if you end up with null at the end of it all, good luck figuring out where it came from!

The problem with ? meaning Option.apply is that it doesn’t match anything else. Furthermore, you also end up with all the awkwardness of using a monad (can’t do stuff directly, have to use map, etc.), and you still can’t tell what went wrong, and now Options infect everything.

The brilliance of Rust’s ? is that it does something far more useful. The usual case is that when you run into an error condition, the only thing you can do is propagate the error further up where it can be handled. Of course there are other cases, but this is the most common.

So, how do you do that? Just type ? and your function returns with the error value at that point. Nothing is infected–you needed to cover the possibility of error in the return type anyway (unless you were trying to use only exceptions for error handling). You can tell what went wrong. Because you use it with something that returns an error type, you can transform the error (e.g. to add extra contextual information) if you want to. The only time this is a problem is if you’re counting on doing a bunch of manual cleanup of resources or something…so don’t do it then.

Now, there’s no reason you have to use the same symbol in Scala as was established in Rust. You could use something else. But it’s best to save single-character symbols for something you want to use all the time. And you really should want to use Rust’s ? all the time. I think it’s best to leave ? free for that.

If you want to wrap X | Null as Option[X] (which is what Option.apply does) then I would suggest .opt or .o as alternative natural choices for a more convenient form.

7 Likes

These extensions for T | null provide the same chaining and default value provided by ?. and ??

/** Explicit nulls `T | null` chainable extensions */
extension [T](t: T | Null)
  inline def map[U](f: T => U): U | Null = 
    if t == null then null else f(t)
  inline def orElse[U >: T](u: => U): U = 
    if t == null then u else t

Example worksheet
image

With operators would look like the following, seems less idiomatic, also ?. feels like requires some compiler magic to generate shortcut-able chains of if/then/else expressions

input
   ?.reverse
   ?.toUpperCase
   ?? "default"
1 Like

Support arriving on 3.3.0


source: (19) Martin Odersky DIRECT STYLE SCALA Scalar Conference 2023 - YouTube

1 Like

The only thing that’s coming in 3.3.0 is the library additions of boundary and boundary.break. If you want to build something on top of that, it’s up to you.

3 Likes