New candidate JEP: 506: Scoped Values

Adam Gent dev at adamgent.com
Wed Apr 16 14:43:08 UTC 2025


On Tue, Apr 15, 2025, at 1:58 PM, Alan Bateman wrote:
> 
> 
> On 15/04/2025 17:17, Adam Gent wrote:
>> 
>> For those that do not want to go to reddit the gist is I think if we do not allow null in a Scoped Value it makes the API less bloated and easier to use with pattern matching. 
> Just to add to Andrew's comments.  If you think of a scoped value an implicit parameter then it would be surprising to allow an explicit parameter be null but not an implicit parameter. As it happens, the first usage of ScopedValue API was Subject which does bind to a null value. In any case, as Andrew said, once we have null-restricted types then a nullness marker can be used. The orElse(V) method doesn't allow null so it might become orElse(V!). There may be an argument to add an orNull() for cases where code wants to gracefully handle the case that it is called without a binding and not use a sentinel, that hasn't been ruled out.
> 
> -Alan

I promise I won't bring ScopedValue API up again but I kind of want to clarify largely the impetus of my concern was less of it containing null but the orElse method itself (hence the slightly unfair wording of "bloated").

I think I can largely accept ScopedValue taking null particularly with ThreadLocal being that way but I ask consideration that `orElse` be removed or possible replaced with `orNull`  or some sort of `map` function but removal I think is best. If that is still not possible given its "finalized" JEP state than maybe the documentation is adjusted if  `orElse(V!) -> V!` is the future. I will explain some more below.

I highly suspect that most people will use ScopedValue like ScopedValue<SomeObject!> implicitly till Valhalla e.g. not binding null.
 
There will be folks possibly like myself who use it like Optional eg:

ScopedValue<SomeObject /* ! */> scopedValue = ... 
// later
switch(scopeValue.orElse(null)) {
}


You see orElse on `Optional` which is what people are familiar with and what I think is confusing given it has the same name is known in static analysis world of `PolyNull` where if I pass in `null` I'm going to get `null` back regardless of the container generic type.

> . The orElse(V) method doesn't allow null so it might become orElse(V!). 

Thus unless we break folks code or the JDK adds PolyNull checking I assume `orElse(V?) -> V?` will always have to be the case particularly because the Javadoc literally says it: "other - the value to return if not bound, can be null". PolyNull is a complicated concept that I have not seen really possible in other type systems. Thus in things like JSpecify it is nullable in and nullable out for polynull like methods.

Perhaps you guys have figured out a solution to polynull that is in the works that I'm unaware of but I can't ever see how orElse can guarantee V! -> V! through types.

One option to fix orElse is to make it more like an orElseGet where a lambda is passed e.g.:

<T> T orElseGet( T fallback, Function<V, T> transform); // Here T can be nullable if you like.

This would fix the container typing being `V!` but at the point I think most would just do the isBound check.

Like I said earlier this will be my last post on the topic so apologies if this is distracting.


More information about the loom-dev mailing list