Registering functional programming with Java
I am new to functional programming and I am trying to use Lambda functions in Java to try and do FP. I know Java is not a good choice for learning Functional, but in my office I am limited to using Java and would like to apply some of these principles there.
I created an optional monad type thing in Java that looks something like this:
public abstract class Optional<T> implements Monad<T> {
//unit function
public static <T> Optional<T> of(T value) {
return value != null ? new Present<T>(value) : Absent.<T>instance();
}
@Override
public <V> Monad<V> flatMap(Function<T, Monad<V>> function) {
return isPresent() ? function.apply(get()) : Absent.<V>instance();
}
}
I use this to avoid nested checks null
in my code, a typical use case where I use this when I need something like firstNonNull
.
Using:
String value = Optional.<String>of(null)
.or(Optional.<String>of(null)) //call it some reference
.or(Optional.of("Hello")) //other reference
.flatMap(s -> {
return Optional.of(s.toLowerCase());
})
.get();
It works like a charm. Now the question is how to combine the entry with this? What if I need to know which of these links was used? This is useful if there is semantics associated with these links and I need to register that this link was not found by trying another option.
Log:
some reference is not present and some other business case specific log
Can this be implemented in Java? I tried to read some possible solutions from the internet and found the Writer
Haskell monad but I got confused and couldn't follow.
EDIT
Link to gist
source to share
The clean solution for this is monoid composition.
Combination of monoids
A monoid is an associative binary operation with an identifier. The type Optional<A>
forms a monoid for any A
:
https://functionaljava.ci.cloudbees.com/job/master/javadoc/fj/Monoid.html#firstOptionMonoid--
In your case it Monoid<Optional<A>>
will be implemented using or
as sum
and Absent
how zero
, so it yourMonoid.sum(x, y)
should be the same as x.or(y)
.
Now you want to combine this with another monoid made up of your magazine. So let's say you are using simple String
as your log. Well, String
forms a monoid, where sum
is string concatenation and zero
is an empty string.
You want to combine the monoid String
with firstOptionMonoid
. For this, you need a tuple type. Functional Java has a tuple type calledP2
. Here's how you combine the two monoids (this really needs to be added to the class Monoid
, send a pull request!):
import fj.*;
import static fj.P.p;
import static fj.Monoid.*;
public final <A,B> Monoid<P2<A,B>> compose(Monoid<A> a, Monoid<B> b) {
return monoid(
x -> y -> p(a.sum(x._1, y._1), b.sum(x._2, y._2)),
p(a.zero, b.zero));
}
Composite monoid then (in FJ):
Monoid<P2<Option<A>, String>> m = compose(firstOptionMonoid<A>, stringMonoid)
Now, you don't always want to add a journal. You want it to depend on whether a value is present or not in Option
. To do this, you can write a specialized method:
public final P2<Option<A>, String> loggingOr(
P2<Option<A>, String> soFar,
Option<A> additional,
String msg) {
return soFar._1.isDefined ?
soFar :
m.sum(soFar, p(additional, msg))
}
I recommend looking more at monoids. They are a very versatile tool, and they are one of the few purely functional constructs that are actually enjoyable to use in Java. If you don't mind learning in Scala, I wrote a book called Functional Programming in Scala and the chapter on monoids is just available online for free.
Combination of monads
But now you are working with a composite type P2<Option<A>, String>
, not just with Option<A>
, and this type does not come with flatMap
. What you would really like (if Java can do it, but it can't) is to use a monad Writer<String, _>
with a monad type transformer OptionT
. Imagining for a moment that Java can have monadic transformers, the type P2<Option<A>, String>
would be equivalent to the type OptionT<Writer<String, _>, A>
where _
the partially applied type constructor indicates (obviously not valid Java).
The only Java solution is to combine these monads in first-order order:
import fj.data.Writer
public abstract class OptionWriter<W, A> {
abstract Writer<W, Option<A>> writer;
public <B> OptionWriter<W, B> flatMap(Function<A, OptionWriter<B>>) {
...
}
public static <M, T> OptionWriter<M, T> unit(t: T) {
return Writer.unit(Option.unit(t))
}
}
source to share
I would use varargs approach, it would be easier to track and shorten the entries.
public static <T> Optional<T> of(T... value) {
for(int i=0;i<values.length;i++) {
if (value[i] != null) {
// log that value[i] was chosen
return new Present<T>(value[i]);
}
}
// log all was null
return Absent.<T>instance();
}
In your example
String value = Optional.of(null, null, "Hello").get();
source to share
At this point, you have two implementations Optional
, Present
and Absent
. I suggest introducing another type LoggingAbsent
that behaves like Absent
but logs any operation that reacts inversely.
The key is deciding whether to return one of these types. For example. when converting a null to a value Optional
it would be helpful to return an instance LoggingAbsent
. But when called map
on, Optional
it would make sense that the implementation LoggingAbsent
registers the operation and returns the result unregistered Absent
, so that only the first operation in the chain reports a failure, and all subsequent operations back out silently.
You can also support explicit selection by providing an alternative factory method for logging and non-logging Optional
.
source to share