0

我正在玩函数式编程,尤其是函数式 Java。我已经成功实现了我的 IO Monad 版本,并且正在为我的核心编写 IO 操作。它基本上是将对象序列化为 Xml 文件(对象类型扩展了自定义 XmlWritable 接口)。

不幸的是,为了做到这一点,需要创建一个 OutputStream 实例和一个 XmlSerializer 实例。OutputStream 的范围比 XmlSerializer 的范围更广,这意味着我认为能够在我的 IO monad 中正确处理这两个生命周期的唯一方法是将它们放在一个元组中,在使用 XmlSerializer 编写后关闭 OutputStream .

这会导致代码繁重而丑陋(Java 6 绝对不是最好的):

public abstract class IO<R> {
    [...]
}

public class IOActions {

    public final F<String, IO<OutputStream>> openFileFn() {
        return new F<String, IO<OutputStream>>() {
            @Override
            public IO<OutputStream> f(String fileName) {
                [...]
            }
        };
    }

    /* This will be partially applied, encoding will be fixed */
    public static final F<OutputStream, IO<P2<OutputStream, XmlSerializer>>> initSerializer() {
        return new F<OutputStream, IO<P2<OutputStream, XmlSerializer>>>() {
            @Override
            public IO<P2<OutputStream, XmlSerializer>> f(OutputStream os) {
                XmlSerializer = new ...
                [...]
            }

        };
    }

    /* This will be partially applied as well */
    public static final F2<XmlWritable, P2<OutputStream, XmlSerializer>, IO<P2<OutputStream, XmlSerializer>>> writeObjectFn() {
        return new F2<XmlWritable, P2<OutputStream, XmlSerializer>, IO<P2<OutputStream, XmlSerializer>>>() {
            @Override
            public IO<P2<OutputStream, XmlSerializer>> f(XmlWritable object, P2<OutputStream, XmlSerializer> p) {
                [...]
            }
        };
    }

有没有更惯用的为什么要在函数式编程中处理我的用例?

潜伏着,我发现了 State Monad……但我有点害怕看到如果我在函数式 Java 中的 IO Monad 之上应用 State Monad 会发生什么。

4

2 回答 2

2

I actually took great inspiration from Functional-Java's DB combinators to solve similar problems. I made my very own "XML combinators" (and more) from this pattern, so its worth learning.

You might find this discussion on google groups useful.

edit - replying to the comment:

follow the code:
notice how you start a new connection using the StateDb, see that you have a few options to start a connection, one that eventually commits, and one that eventually rollback. these are just two examples of things you can "carry" with the computation. Essentially, every computation that you bind (a plain modaic bind), could potentially carry information.

here is an example i gave in the discussion above:

DB<PreparedStatement> prepareStatement(final String sql) {
  return new DB<PreparedStatement>() {
     public PreparedStatement run(Connection c) throws SQLException {
        return c.prepareStatement(sql);
}}}

// for a query that a reader might perform, i might have a function like this:   
F<PreparedStatement, DB<ResultSet>> runStatement() {
   public DB<ResultSet> f(final PreparedStatement s) {
      return new DB<ResultSet>() {
        public ResultSet run (Connection c) throws SQLException {
           return s.executeQuery();
}}}

So in this example, you can pass extra information, namely the sql query as a parameter to the function that gets bound. you could just as well had more parameters to runStatement.

to put it all together, you get something like:

ResultSet rs = DbState.reader("conn-url").run(prepareStatement("select * from table").bind(runStatement());

Hope this helps!

于 2013-11-10T23:01:51.373 回答
0

这是我想出的。非常感谢您的反馈。我遵循上面的答案,从链接的讨论中获得灵感:

public class IOXml<T extends XmlWritable> implements DataWriter<T>{

    private final XmlSerializer mXmlSerializer;
    private final Option<String> mXmlEncoding;
    private final IO<OutputStream> ioCreateStream;
    private final F<OutputStream, IO<Unit>> ioCloseStream;

    @Inject
    IOXml(IO<OutputStream> createStream, F<OutputStream, IO<Unit>> closeStream, XmlSerializer xmlSerializer, Option<String> xmlEncoding) {
        mXmlSerializer = xmlSerializer;
        mXmlEncoding = xmlEncoding;
        ioCreateStream = createStream;
        ioCloseStream = closeStream;
    }

    /**
     * Write a T object which is XmlWritable.

     * @param osAndSer The tuple containing OutputStream and XmlSerializer.
     * @param object The object to write.
     * @return IO monad object.
     */
    protected IO<Unit> writeObject(final T object) {
        return new IO<Unit>() {
            @Override
            public Unit performIO() throws IOException {
                object.writeXml(mXmlSerializer);
                return Unit.unit();
            }
        };
    }

    protected final F<Unit, IO<Unit>> writeObjectFn(final T object) {
        return new F<Unit, IO<Unit>>() {
            @Override
            public IO<Unit> f(Unit a) {
                return writeObject(object);
            }
        };
    }

    /**
     * Initialize the XmlSerializer before using it.
     * @param os An OutputStream.
     * @param encoding The encoding of the xml file.
     * @return An IO action returning nothing.
     */
    protected IO<Unit> initXml(final OutputStream os) {
        return new IO<Unit>() {
            @Override
            public Unit performIO() throws IOException {
                mXmlSerializer.setOutput(os, mXmlEncoding.toNull());
                mXmlSerializer.startDocument(mXmlEncoding.toNull(), true);
                return Unit.unit();
            }
        };
    }

    /**
     * Close the XmlSerializer after.
     * @return An IO action returning nothing.
     */
    protected IO<Unit> closeXml() {
        return new IO<Unit>() {
            @Override
            public Unit performIO() throws IOException {
                mXmlSerializer.endDocument();
                return Unit.unit();
            }
        };
    }

    protected final F<Unit, IO<Unit>> closeXmlFn() {
        return new F<Unit, IO<Unit>>() {
            @Override
            public IO<Unit> f(Unit a) {
                return closeXml();
            }
        };
    }

    @Override
    public void close() throws IOException {
        closeXml().performIO();
    }

    @Override
    public void write(T object) {
        throw new UnsupportedOperationException("Are you sure? IOXml is a functional class. Use the function returned by liftIO instead.");
    }

    /**
     * Curried function to write XML objects, given the object itself and an OutputStream.
     * @return The curried function.
     */
    protected F<OutputStream, F<T, IO<Unit>>> writeFn() {
        // returning the outer
        return new F<OutputStream, F<T, IO<Unit>>>() {
            @Override
            public F<T, IO<Unit>> f(final OutputStream os) {
                // Returning the inner
                return new F<T, IO<Unit>>() {
                    @Override
                    public IO<Unit> f(T object) {
                        return initXml(os).bind(writeObjectFn(object)).bind(closeXmlFn());
                    }
                };
            }
        };
    }

    @Override
    public IO<Unit> writeIO(final T object) {
        return IOImpl.bracket(ioCreateStream,                      // init
                       ioCloseStream,                              // close
                       Function.partialApply2(writeFn(), object)); // body

    }
}
于 2013-12-04T19:13:46.747 回答