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?
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.
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 .
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 ).
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.
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);
});
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With