- 
                Notifications
    
You must be signed in to change notification settings  - Fork 4
 
Functional Tropes
Javimmutable is not a functional programming library but its collections fit naturally into that style of programming. To further support that style the library provides some monadic classes:
- Maybe: A replacement for Java's 
Optionalclass that also supports null as a value. - NotNull: Another replacement for Java's 
Optionalclass and ensures it never contains null. - Result: Similar to 
Maybebut can contain either a value or an exception. - Computation: A deferred computation class that allows a computation to be composed in steps and evaluated later.
 
This page outlines what these classes are and what they do. For motivation on why these classes can be useful read a good book on functional programming.
Collections have a find() method that returns a Maybe rather than a value.  The Maybe either contains a value or is empty.  Methods are provided to map the value to make new Maybes as well as to safely extract the value or an alternative.
While Maybe is similar to Optional it has a number of advantages:
- It's 
Serializableso you can use it as a field inSerializableobjects. - It doesn't mind storing null so it won't turn itself into an empty if you map its value to null.
 - It has more options for mapping the value.
 
Though Maybe is fine with storing null it does provide two ways to eliminate null values.
- 
notNull()method returns an equivalentNotNullobject. - 
nullToEmpty()method returns an emptyMaybeif its value is null. 
Maybe has three static factory methods:
- 
Maybe.empty()returns an emptyMaybe. All emptyMaybevalues share a single common instance to reduce memory footprint. - 
Maybe.of(value)returns a fullMaybe. The resultingMaybeis always full even ifvalueisnull. - 
Maybe.cast(class, value)returns aMayberepresenting an attempt to cast thevalueto an instance of the given class. An emptyMaybeis returned if thevaluecannot be cast into the given class safely. Anullvalue will result in a fullMaybe. 
Suppose you have an IMap containing all environemnt variables.  You want to take the value of one of them (if it exists), parse it as an integer, and use it, if it exists, or a default, if it does not.  This example shows how all of this could be done:
IMap<String,String> envVars = IMaps.hashed(System.getenv());
int port = envVars.find("PORT").map(Integer::parseInt).get(80);Sometimes you really don't want null as a value.  In that case NotNull has you covered.  It always ensures that null values are mapped to empty NotNulls.  If you construct an instance using null as its value you'll get an empty NotNull.  Likewise if any map or flatMap function produces a null value the result will be an empty NotNull.
You can convert a NotNull into an equivalent Maybe by calling the maybe() method.
NotNull has three static factory methods:
- 
NotNull.empty()returns an emptyNotNull. All emptyNotNullvalues share a single common instance to reduce memory footprint. - 
NotNull.of(value)returns a fullNotNullifvalueis not null. Otherwise it returns an emptyNotNull. - 
NotNull.cast(class, value)returns aNotNullrepresenting an attempt to cast thevalueto an instance of the given class. An emptyNotNullis returned if thevaluecannot be cast into the given class safely. Anullvalue will also result in an emptyNotNull. 
Frequently we need to make multiple method calls to accomplish a task. If any of them can trigger an exception that should halt the computation it can be cumbersome to express that. Also there are advantages to capturing an exception into the return value of a method call rather than throw an exception that must be caught higher in the stack.
The Result class makes doing this a simple process.  Result is an object that holds either a computed value or an exception.  Return values have some advantages in that they can be passed around like any other value.  Also Result offers map and flatMap methods that make it easy to compose operations into a final result.
For example suppose you have three methods to call to compute a result.
public String callWebService(String host, int port) throws IOException;
public IList<Address> extractHouseAddresses(String webServiceResult) throws ParseException;
public IList<BigDecimal> lookupHouseValues(IList<Address> houseAddresses) throws DatabaseException;
Result<BigDecimal> totalValue = Result.attempt(() -> callWebService("some-host", 443))
                                      .map(resultJson -> extractHouseAddresses(resultJson))
                                      .map(addresses -> lookupHouseValues(addresses))
                                      .map(values -> values.reduce(BigDecimal.ZERO, BigDecimal::add));If any of the calls throws an exception the calculation will stop and the final Result will contain the exception.  If all of them succeed the final Result will contain the total value of all of the homes.  While this is a contrived example it still serves to demonstrate how the methods of Result can be applied.
A Computation holds a sequence of method calls that can be executed by calling its call() or compute() methods.  Computation implements Callable so it can be submitted to an ExecutorService or run on a Thread if desired.  Like Result they provide map and flatMap methods that make them easy to compose.
The call() method performs the computation and then either returns its computed value or throws any exception thrown during the process.  The compute() method performs the computation and returns a Result object containing the computed value or an exception if the computation failed.