final List<Whatever> that
is the identity of the list.I also think Either and Optional are cancers. Type erasure takes some of the fun away from Either. Before you had Optional you had one problem, you might have a null. With Optional you have two problems, you can get a null AND you can get an empty.
I worked at a place that used Scala and the engineering manager would talk your ear off like an Amway scam person about how much better monads were for error handling than Exceptions. I started working on non-Scala stuff but once they put me on the Scala side I found out that error handling was just missing in most places even though they allegedly did code reviews. It was definitely a frying pan to fire situation relative to Exceptions.
(The time before that whenI coded in Scala I found that people would spend 3 days writing something concurrent with Actors that only used 2 out of 8 cores and was rife with error conditions that could be done in 20 minutes with an Executor in Java.)
In a a parallel universe where LISP was the dominant language, people would be writing essays about how great COBOL and Cold Fusion and PHP are, about what a genius Noam Chomsky was, how recursive descent parsers are a force multiplier, etc.
That's not really the fault of the Optional. That's Java adding null as value to every type. Java also allows lists of nulls, mappings to null etc. An optional is just a container that stores one item at most. I do agree that it makes optionals less useful in Java. In Haskell and OCaml they are used essentially instead of null.
var result = map(x,x=>x.y)
which is equivalent to var result = isNull(x) ? null : x.y
where isNull is static imported from Objects.An approach which is authentic to some other languages is to consistently used a list with 0 or 1 members instead of optionals.
Sounds like you were working with some folks that were just learning the language/idioms (which is admittedly a big hurdle).
I haven't used actors in a very long time. Simple future composition and reactive streams get you very far, at an absolute fraction of the code size and complexity of the equivalent vanilla Java approach (ie: not using Rx).
For me there was a lot of learning (that often just felt like grinding), and I can confidently say that I'm leagues more productive than trying to mimic this with low level vanilla Java primitives.
I'm sure this would also apply with go channels, async/await, etc. Raising the bar of abstraction in this case can be valuable. As compared to something really high leverage like reactive streams it's just going to take a ton of code and be far less flexible in a naive approach (in most cases).
I've seen this same pattern with others. Is it worth the investment of time to get there? Not sure, maybe Go is a better set of tradeoffs for most, but I wouldn't go back to straight executors, locks, and the rest of the Java 1.5 concurrency offerings (which is what I thought the OP was saying).