The sugar your parent proposed would still be nice and has precedent in other languages with option types. Swift has something very similar[0], and Rust is debating it[1]. Haskell's do-notation and Scala's for-comprehension are other solutions to the same general problem of conveniently using option types.
It may be a good thing to only support that sort of convenience for option types and not for nulls, in order to further encourage their use. Although using nullable types is already pretty darn convenient!
[0]: https://developer.apple.com/library/prerelease/ios/documenta... [1]: https://github.com/rust-lang/rfcs/pull/204
This is kind of a pedantic distinction, but the behavior you've described is actually Functor behavior; I think Monads allow for Functor-like behavior as part of their definition (not sure, I'm neither a category theorist nor an experienced Haskeller), but their use is something a little bit different. Monads allow you to do two things:
1) Lift regular values into monadic ones in a generic way. Let's say that we have two Monads - Optional and List. There should be a way such that we can take a non-monadic value (the part that will go inside) and turn it into a Monad. So, assuming Monad is a Java-like abstract class, the following should be possible:
Monad<String> m1 = Optional.lift("hello");
Monad<String> m2 = List.lift("hello");
2) Flat-map, typically called bind in this context. Given a monad and a function that takes a value and returns a monad, we should have some way of combining the resulting monads if we were to map this function over the first monad's internal value. So for instance, given an optional string, and a function that takes a string and returns an optional int, we should have some way of combining the Optional<Optional<Integer>> into just an Optional<Integer>. So, in the following contrived example... // Returns a UTF8 string value from a database, may fail
public Optional<String> getUtf8Name() { ... }
// Returns the length of a String if it contains exclusively ascii characters
public Optional<Integer> getAsciiLength(String str) { ... }
... you can compare the differences between it and the Functor's map: Optional<Integer> asciiLength = getUtf8Name().bind(getAsciiLength);
Optional<Integer> otherAsciiLength = getUtf8Name().map(getAsciiLength).getOrElse(Optional.absent());
As it turns out, you can implement the Functor's map method in a generic way using lift and bind. public abstract class Monad<A> implements Functor<A> {
/**
* Implements Functor's map method.
*/
@Override
public <B> Monad<B> map(Function<A, B> fn) {
return this.bind( innerValue -> this.lift(fn.apply(innerValue)) );
}
}
Re:Java 9 features announced
by Paradigma11 on Wednesday August 20, 2014 @07:14AM
(#8201857)
Monad<String> m1 = Optional.lift("hello");
Monad<String> m2 = List.lift("hello");
Shouldn't that be Option<String> m1 = Optional.lift("hello");
List<String> m2 = List.lift("hello");
?
T obj = ...;
Optional.of(obj).map(T::some).map(U::other).map(V::methods).getOrElse(null);
Not quite as clean syntactically, but much more general, as the same operations can be applied to other monadic objects.