繁体   English   中英

如何在Java中实现Haskell的IO类型?

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

这是我的第一次尝试:

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);
        });
    }
}

但是当我尝试编译它时,我收到以下错误:

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

我不明白的是世界级与类型变量没有关系,但javac认为它确实存在。 我究竟做错了什么?

撇开您在Java中复制Haskell IO类型的方法的保真度:

编译器认为, A在你的bind方法的签名是一样的A类定义。 你告诉过我们这不是用语言。 为了将它传达给编译器,您需要将事物设置为静态并引入几个方法级类型参数:

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);
        });
    }
}

我认为“操作monad”方法更适合解释Haskell I / O的本质。 Haskell版本可以非常简单:

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价值观不是一些神奇的世界传递函数; 他们只是简单的数据。 如果我们愿意,我们可以解释数据以实际执行所表示的操作:

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 m f) = runMyIO m >>= runMyIO . f
runMyIO (LiftPrim prim) = runPrimOp prim

实际上可以编写一个Haskell编译器,其IO类型看起来很像MyIO ,并且其运行时系统直接解释该类型的值。


我试过下面的Java翻译。 我从来没有真正成为一名Java程序员,自从我使用它以来已经有很长一段时间了,所以这可能是非常单一的,甚至是错误的。 我想你可能想要使用某种版本的“访问者模式”来表示BindPure解释(将事物带入像Control.Monad.Operational这样的通用领域),同时为PrimOp子类使用run方法IO 因为我真的不知道正确的Java方式,所以我试图保持简单。

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;
      }
}

棘手的一点是Bind ,它需要存在量化。 我不知道在Java中这样做的惯用方法,所以我做了一些似乎有用的尴尬。 也就是说,我编写了一个辅助类OpenBind ,它暴露了两个类型变量,然后是一个包含OpenBind的类Bind ,其中一个变量是狂野的。

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());
        }
    }
}

初学者本身很简单(我找不到Java的等价物()所以我写了自己的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 "";
  }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM