I'm learning functional programming and their concept of Monads. I've found nothing more effective in learning than writing an implementation in a programming language I have experience with.
I came up with the following implementation in Java. Could someone please suggest improvements?
So, I have an interface, Bindable
:
public interface Bindable<T> {
<E> Bindable<E> bind(Function<T, Bindable<E>> function);
}
And I have the Maybe
implementation:
public class Maybe<T> implements Bindable<T> {
private final State<T> state;
public static <T> Maybe<T> just(T value) {
return new Maybe<T>(new Just<T>(value));
}
public static <T> Maybe<T> nothing() {
return new Maybe<T>((State<T>) Nothing.INSTANCE);
}
private Maybe(State<T> state) {
this.state = state;
}
@Override
public <E> Bindable<E> bind(final Function<T, Bindable<E>> function) {
return state.accept(new StateVisitor<T, Bindable<E>>() {
@Override
public Bindable<E> visitJust(T value) {
return function.apply(value);
}
@Override
public Bindable<E> visitNothing() {
return nothing();
}
});
}
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("state", state)
.toString();
}
private static interface State<T> {
<E> E accept(StateVisitor<T, E> visitor);
}
private static interface StateVisitor<T, E> {
E visitJust(T value);
E visitNothing();
}
private static class Just<T> implements State<T> {
private final T value;
private Just(T value) {
this.value = value;
}
@Override
public <E> E accept(StateVisitor<T, E> visitor) {
return visitor.visitJust(value);
}
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("value", value)
.toString();
}
}
private static enum Nothing implements State<Object> {
INSTANCE;
@Override
public <E> E accept(StateVisitor<Object, E> visitor) {
return visitor.visitNothing();
}
@Override
public String toString() {
return "Nothing";
}
}
}
My Identity
implementation:
public class Identity<T> implements Bindable<T> {
private final T value;
public static <T> Identity<T> create(T value) {
return new Identity<T>(value);
}
private Identity(T value) {
this.value = value;
}
@Override
public <E> Bindable<E> bind(Function<T, Bindable<E>> function) {
return function.apply(value);
}
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("value", value)
.toString();
}
}
And of BindableSequence
:
public class BindableSequence<T> implements Bindable<T> {
private final Iterable<Bindable<T>> sequence;
public static <T> BindableSequence<T> create(Iterable<T> sequence) {
ImmutableList.Builder<Bindable<T>> builder = ImmutableList.builder();
for (T item : sequence) {
builder.add(Identity.create(item));
}
return new BindableSequence<T>(builder.build());
}
public static <T> BindableSequence<T> create(T... sequence) {
return BindableSequence.create(Lists.newArrayList(sequence));
}
private BindableSequence(Iterable<Bindable<T>> sequence) {
this.sequence = sequence;
}
@Override
public <E> Bindable<E> bind(Function<T, Bindable<E>> function) {
List<Bindable<E>> result = Lists.newArrayList();
for (Bindable<T> t : sequence) {
Bindable<E> bind = t.bind(function);
result.add(bind);
}
return new BindableSequence<E>(result);
}
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("sequence", sequence)
.toString();
}
}
And Sample
class of how they can be used
public class Sample {
public static void main(String[] args) {
Bindable<Integer> result = Identity.create(5)
.bind(new Function<Integer, Bindable<Integer>>() {
@Override
public Maybe<Integer> apply(Integer integer) {
return Maybe.just(integer * 10);
}
})
.bind(new Function<Integer, Bindable<Integer>>() {
@Override
public Bindable<Integer> apply(Integer integer) {
return BindableSequence.create(integer, integer + 10);
}
})
.bind(new Function<Integer, Bindable<Integer>>() {
@Override
public Maybe<Integer> apply(Integer integer) {
return Maybe.just(integer + 10);
}
});
System.out.println(result);
}
}
return
in your interface (which you can't in Java because interfaces can only specify instance methods), you can't do half the things with your Bindable interface that you can do with monads in Haskell (e.g. you won't be able to implement most of the functions fromControl.Monad
) includingsequence
,mapM
etc. This makesBindable
much less useful then monads. \$\endgroup\$return
" argument does -- can you give a small example of what your Bindable definition can do in its current form? Perhaps show some generic code that operates abstractly on Bindable to do something useful/different for each of Maybe, Identity, and BindableSequence. \$\endgroup\$Bindable
can be used \$\endgroup\$bind
should return the same kind of monad that you're callingbind
on. I.e. you're not allowed to return an Identity from a function that's given as an argument tobind
on aMaybe
. This works in this case, but if you consider for example the list monad, you'll see why this is problematic. \$\endgroup\$<M extends Bindable, T, U> M<U> bla(M<T>, Function<T,U>, M<U>)
would be illegal. This makes the interface next to useless. \$\endgroup\$