How to implement Haskell IO input type in Java?

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 

I do not understand that the world class is not related to a type variable, but javac thinks this is so. What am I doing wrong?

+5
source share
2 answers

Leaving aside the loyalty of your approach to replication like Haskell IO in Java:

The compiler believes that A in your bind method signature matches A in the class definition. You told us this not in words. To tell the compiler this, you need to make things static and enter a few method 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); }); } } 
+4
source

I think the “operational monad” approach better explains the essence of Haskell I / O. The Haskell version can be quite simple:

 data PrimOp a where PutStr :: String -> PrimOp () GetLine :: PrimOp String -- Whatever other primitives you want data MyIO a where Pure :: a -> MyIO a Bind :: !(MyIO a) -> (a -> MyIO b) -> MyIO b LiftPrim :: !(PrimOp a) -> MyIO a instance Functor MyIO where fmap = liftM instance Applicative MyIO where pure = Pure (<*>) = ap instance Monad MyIO where (>>=) = Bind 

MyIO values MyIO not some magical world-transmitting functions; it's just data. We can interpret the data to actually perform the presented action if we want to:

 runPrimOp :: PrimOp a -> IO a runPrimOp (PutStr s) = putStr s runPrimOp GetLine = getLine runMyIO :: MyIO a -> IO a runMyIO (Pure a) = pure a runMyIO (Bind mf) = runMyIO m >>= runMyIO . f runMyIO (LiftPrim prim) = runPrimOp prim 

In fact, you could write a Haskell compiler whose type IO looks just like MyIO , and the runtime system directly interprets the values ​​of this type.


I tried to do a Java translation below. I have never been a Java programmer, and I have been using it for a long time, so this can be terribly uniomatic or even wrong. I suggest that you probably want to use some version of the “visitor pattern” to represent Bind and Pure interpretations (translating things into something common, like Control.Monad.Operational ) using the run method to subclass PrimOp IO . Since I really don't know the correct Java method, I tried to just keep it simple.

 public interface IO <A> { public A run (); } public final class Pure <A> implements IO <A> { private final A val; Pure (A x) { val = x; } public A run () { return val; } } 

Hard Bind bit that requires existential quantification. I don't know the idiomatic way to do this in Java, so I did something awkward that seems to work. Namely, I wrote a helper class, OpenBind , which provides two type variables, and then a Bind class, which completes OpenBind , leaving one of these wild variables.

 import java.util.function.Function; public final class Bind <A> implements IO <A> { private final OpenBind <?,A> ob; public <B> Bind (IO <B> m, Function <B,IO <A>> f) { ob = new OpenBind <B,A> (m, f); } public A run() { return (ob.run()); } private final static class OpenBind <Fst,Snd> { private final IO <Fst> start; private final Function <Fst, IO <Snd>> cont; public OpenBind (IO <Fst> m, Function <Fst, IO <Snd>> f) { start = m; cont = f; } public final Snd run () { Fst x = start.run(); IO <Snd> c = cont.apply(x); return (c.run()); } } } 

The primitives themselves are pretty simple (I could not find the Java () equivalent, so I wrote my own Unit ):

 public class PutStr implements IO <Unit> { private String str; public PutStr (String s) { str = s; } public Unit run () { System.out.print(str); return Unit.unit; } } public final class Unit { private Unit () {} public static final Unit unit = new Unit (); } public class GetLine implements IO <String> { private GetLine () {} public static final GetLine getLine = new GetLine (); public String run () { // Replace the next line with whatever you actually use to // read a string. return ""; } } 
+2
source

Source: https://habr.com/ru/post/1259643/


All Articles