【问题标题】:Scope management - Stateful IO Monad?范围管理 - 有状态的 IO Monad?
【发布时间】:2013-11-09 03:48:28
【问题描述】:

我正在玩函数式编程,尤其是函数式 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 会发生什么。

【问题讨论】:

  • 我现在正在阅读您的代码(抱歉,Java 6 对 FP 来说很痛苦),但是 IO 状态单子,以更专业的形式跨度>
  • 好的,没问题 :) 当您需要跨 Monadic 函数维护某些状态时,传递元组是一种常见的模式吗?
  • 请允许我问一个问题:您为什么要用一种不太适合它的语言来做这些事情?您可以使用 Scala、Clojure 或 Frege 来编写程序的功能部分。
  • 你的问题确实很好:) 不幸的是我有这个限制。但我知道 Scala,所以任何 Scala 的答案都可以接受!谢谢!
  • 约束到底是什么?使用“函数式java库”,还是生成java代码?

标签: java functional-programming functional-java


【解决方案1】:

实际上,我从 Functional-Java 的 DB combinators 中获得了很大的灵感来解决类似的问题。我根据这种模式制作了自己的“XML 组合器”(以及更多),因此值得学习。

您可能会在 google 群组上发现 this discussion 很有用。

编辑 - 回复评论:

按照代码:
请注意您如何使用StateDb 启动新连接,请注意您有几个选项来启动连接,一个最终提交,一个最终回滚。这些只是您可以通过计算“携带”的东西的两个示例。从本质上讲,您bind(普通的模态绑定)的每个计算都可能携带信息。

这是我在上面讨论中给出的一个例子:

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

所以在这个例子中,你可以传递额外的信息,即 sql 查询作为参数给被绑定的函数。你也可以有更多的参数来运行语句。

把它们放在一起,你会得到类似的东西:

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

希望这会有所帮助!

【讨论】:

【解决方案2】:

这是我想出的。非常感谢您的反馈。 我按照上面的答案,从linked 讨论中获得灵感:

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

    }
}

【讨论】:

    猜你喜欢
    • 2019-11-12
    • 1970-01-01
    • 2018-05-05
    • 1970-01-01
    • 2018-05-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多