Tell me more ×
Code Review Stack Exchange is a question and answer site for peer programmer code reviews. It's 100% free, no registration required.

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);
    }
}
share|improve this question
Note that without having 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 from Control.Monad) including sequence, mapM etc. This makes Bindable much less useful then monads. – sepp2k Jan 21 '12 at 14:00
It seems odd to have a "monad implementation" that doesn't define anything named "Monad". Maybe this reflects the problem cited by @sepp2k ? Is "Bindable" that models the "bind" part of Monad behavior as close as you can get (without perhaps a radically different approach)? I'm trying to decide how much damage the "lack of 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. – Paul Martel Jan 21 '12 at 16:10
@PaulMartel I added sample code of how Bindable can be used – Mairbek Khadikov Jan 22 '12 at 11:16
@Mairbek: Note that what you wrote would actually be illegal in Haskell. The function given to bind should return the same kind of monad that you're calling bind on. I.e. you're not allowed to return an Identity from a function that's given as an argument to bind on a Maybe. This works in this case, but if you consider for example the list monad, you'll see why this is problematic. – sepp2k Jan 22 '12 at 11:30
Also note that it's impossible to write methods that work with arbitrary monads in Java since already the type <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. – sepp2k Jan 22 '12 at 11:34
show 5 more comments

Know someone who can answer? Share a link to this question via email, Google+, Twitter, or Facebook.

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Browse other questions tagged or ask your own question.