I think that Java suffers from NIH syndrome. Nothing that other languages do is good enough for Java until it has been debated for years and reimplemented using new patterns and unknown terminology.
Optionals that throw NPEs, Streams that operate on more than 1 item when called with .limit(1). Futures that don't cancel. Lambdas that don't play nicely with exceptions. Non-extendable streams.
It's not a coincidence that many languages that PL fans think do things "right" end up being far less popular than languages that PL fans think do things "wrong". Developers have different and conflicting preferences, and they are not distributed evenly. For example, the sweet spot for mainstream, super-popular languages over more/less compile-time checking in some areas vs. language complexity is probably not the sweet spot preferred by most developers. It's a little like the VHS vs. Betamax debate.
Improving a super-popular language in some small way that doesn't harm its popularity can have a far bigger impact on software quality than features that complicate the language to a point where it will not be taught as a first language in a lot of schools and will thus never be super-popular. Designing language features for industry to maximise their impact requires far more complex considerations than just PL theory.
I do like the Java platform in general, but this sort of argument from hypothetical future Java fixes strikes my as incredibly disingenuous. People aren't programming in a hypothetical future Java.
I can literally pose no argument against your imagined solution to this problem without very specific details. (Implication being, it's a straw man at best.)
And saying saying stuff like (in an adjacent thread):
> You're assuming that deconstructing and exhaustive patterns will continue to be restricted to ADTs only. That may or may not be the case.
Is just complete fiction. (EDIT: Are you going to solve the halting problem, or are you going to provide details before just promising "it'll be ok"? I assume the Halting Problem is out of the question, but what's the plan, then? Details, pls)
Either point to extant JEPs or explain in detail what the exact plan is, please.
Having said that, we have a few experiments with type-safe "checked exception transparency" for lambdas (and methods in general), but as always we like to sit on them for a few years because the cost of a bad feature may be much higher than the cost of a missing one. We only publicly discuss specific solutions once there's something to be gained by such a discussion and once we've decided that the problem merits a solution in the short term.
> Is just complete fiction.
I was responding to a statement about deprecating Optional in favour of an ADT, and hinted at this design, which is under exploration (https://openjdk.org/projects/amber/design-notes/patterns/pat...):
case Optional.empty() -> ...
case Optional.of(var x) -> ...
It's not complete fiction, this is actual stuff we're working on, but not everything we explore will end up in the language.You are not wrong about cancelabel futures, though.
public sealed interface Maybe<T> {
<T2> Maybe<T2> map(Function<T, T2> mapper);
<T2> Maybe<T2> flatMap(Function<T, Maybe<T2>> mapper);
T elseGet(T fallback);
record Just<T>(T value) implements Maybe<T> {
public <T2> Maybe<T2> map(Function<T, T2> mapper) {
return new Just<T2>(mapper.apply(value));
}
public <T2> Maybe<T2> flatMap(Function<T, Maybe<T2>> mapper) {
return mapper.apply(value);
}
public T elseGet(T fallback) {
return value;
}
}
record Empty<T>() implements Maybe<T> {
public <T2> Maybe<T2> map(Function<T, T2> mapper) {
return new Empty<>();
}
public <T2> Maybe<T2> flatMap(Function<T, Maybe<T2>> mapper) {
return new Empty<>();
}
public T elseGet(T fallback) {
return fallback;
}
}
}
public void demo() {
Maybe<String> foo = new Maybe.Empty<String>();
System.out.println(
switch (foo) {
case Maybe.Just(String val) -> "Hello " + val;
case Maybe.Empty() -> "Fine, leave me hanging";
}
);
}; Maybe<String> foo = null;
foo.map(s -> "bar");
explicit pattern matching is usually discouraged anyway though, and you can do this now with Optional Optional<String> foo = Optional.empty();
String message = foo.map(s -> "Hello " + s).orElse("Fine, leave me hanging");
Other languages like Scala also have an Option.fold method for this specific case.The way Pattern matching works right now is just the beginning.
The best you will get is a warning and the JVM will deoptimize Optional back to a regular object, because somewhere someone set an Optional to null somewhere in a library. It doesn't even have to be as obvious as Optional<String> o = null;
Any cast to Optional can let a null pointer slip from an Object reference. List<Optional<String>> is allowed to contain null pointers and you are allowed to assign o = list.get(0) which generates an implicit cast.
There is no good solution for this. Due to the way generics are implemented.
However, unlike the binary choice of class/value type, project valhalla will provide a more granular approach, coming with incremental performance benefits and constraints.
In the current spec draft, the type system boils down to 4 categories:
- Fully mutable, polymorph classes
- Immutable, monomorphic classes
- Immutable, monomorphic classes + null-hostile
- Immutable, monomorphic classes + null-hostile + multithreading-unsafe
Another story for nullability is being investigated as well which would allow for full performance gain as well as full backwards compatibility.