Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you implement Haskell's IO type in Java?

Tags:

java

haskell

This is my first attempt:

import java.util.function.*;
import java.util.ArrayList;
public class IO<A> {
    private Function<World,Tuple<World,A>> transform;
    private class World {
        private ArrayList<String> stdin;
        private ArrayList<String> stdout;
        public World() {
            this.stdin  = new ArrayList<String>();
            this.stdout = new ArrayList<String>();
        }
    }
    private class Tuple<F,S> {
        public F fst;
        public S snd;
        public Tuple(F fst, S snd) {
            this.fst = fst;
            this.snd = snd;
        }
    }
    public IO(Function<World,Tuple<World,A>> transform) {
        this.transform = transform;
    }
    public IO<A> pure(A a) {
        return new IO<A>(r -> new Tuple<World,A>(r,a));
    }
    public <B> IO<B> bind(IO<A> io, Function<A,IO<B>> f) {
        return new IO<B>(r -> {
            Tuple<World,A> result = io.transform.apply(r);
            IO<B> ioB = f.apply(result.snd);
            return ioB.transform.apply(result.fst);
        });
    }
}

But when I try to compile this I get the following error:

IO.java:29: error: incompatible types: IO<B>.World cannot be converted to IO<A>.World
            Tuple<World,A> result = io.transform.apply(r);
                                                        ^
  where B,A are type-variables:
    B extends Object declared in method <B>bind(IO<A>,Function<A,IO<B>>)
    A extends Object declared in class IO

What I don't understand is the world class has no relationship to the type variable but javac thinks it does. What am I doing wrong?

like image 680
Kyle McKean Avatar asked Nov 12 '16 18:11

Kyle McKean


People also ask

How does the IO Monad work?

The I/O monad contains primitives which build composite actions, a process similar to joining statements in sequential order using `;' in other languages. Thus the monad serves as the glue which binds together the actions in a program.

How does Haskell IO work?

Haskell separates pure functions from computations where side effects must be considered by encoding those side effects as values of a particular type. Specifically, a value of type (IO a) is an action, which if executed would produce a value of type a .

What is an IO action?

IO actions are used to affect the world outside of the program. Actions take no arguments but have a result value. Actions are inert until run. Only one IO action in a Haskell program is run ( main ).

What is an IO string?

The StringIO module is an in-memory file-like object. This object can be used as input or output to the most function that would expect a standard file object. When the StringIO object is created it is initialized by passing a string to the constructor.


1 Answers

Leaving aside the fidelity of your approach to replicating Haskell's IO type in Java:

The compiler thinks that the A in your bind method's signature is the same as the A in the class definition. You've told us it's not in words. In order to communicate that to the compiler, you need to make things static and introduce a couple of method-level type parameters:

import java.util.function.*;
import java.util.ArrayList;
public class IO<A> {
    private Function<World,Tuple<World,A>> transform;
    private static class World {
        private ArrayList<String> stdin;
        private ArrayList<String> stdout;
        public World() {
            this.stdin  = new ArrayList<String>();
            this.stdout = new ArrayList<String>();
        }
    }
    private static class Tuple<F,S> {
        public F fst;
        public S snd;
        public Tuple(F fst, S snd) {
            this.fst = fst;
            this.snd = snd;
        }
    }
    private IO(Function<World,Tuple<World,A>> transform) {
        this.transform = transform;
    }
    public static <A> IO<A> pure(A a) {
        return new IO<A>(r -> new Tuple<World,A>(r,a));
    }
    public static <A,B> IO<B> bind(IO<A> io, Function<A,IO<B>> f) {
        return new IO<B>(r -> {
            Tuple<World,A> result = io.transform.apply(r);
            IO<B> ioB = f.apply(result.snd);
            return ioB.transform.apply(result.fst);
        });
    }
}
like image 63
Matt McHenry Avatar answered Oct 13 '22 00:10

Matt McHenry