Earlier quoted context omitted.
The other "Missing feature" is null safe ".". Other languages have it, but I don't know what the technical name is... basically: Integer val = some.other.chain.of.objects.value; In Java, this code has a huge potential for NullPointerExceptions. Instead a new operator (yes, I know) like: Integer val = some.?other.?chain.?of.?objects.?value; If any of the intermediate objects are null, the whole assignment becomes null…
This is solved in a more general way in Java 8 via monadic optionals. The basic idea is to wrap your nullable objects in an Optional , which then both forces you to deal with the possibility of the value being absent and gives you good tools for doing so. Your example, with Optional, looks like this: T obj = ...; Optional.of(obj).map(T::some).map(U::other).map(V::methods).getOrElse(null); Not quite as clean syntactic…
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 m1 = Optional.lift("hello");
Monad 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> into just an Optional. So, in the following contrived example... // Returns a UTF8 string value from a database, may fail
public Optional getUtf8Name() { ... }
// Returns the length of a String if it contains exclusively ascii characters
public Optional getAsciiLength(String str) { ... }
... you can compare the differences between it and the Functor's map: Optional asciiLength = getUtf8Name().bind(getAsciiLength);
Optional 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 implements Functor {
/**
* Implements Functor's map method.
*/
@Override
public Monad map(Function fn) {
return this.bind( innerValue -> this.lift(fn.apply(innerValue)) );
}
}