我正在使用函数式编程,尤其是Functional 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,我会害怕看到它会发生什么。
答案 0 :(得分:2)
我实际上从Functional-Java的DB combinators中获得了很大的灵感来解决类似的问题。我从这个模式中创建了自己的“XML组合器”(以及更多),因此值得学习。
您可能会发现Google群组中的this discussion有用。
编辑 - 回复评论:
按照代码:
注意如何使用StateDb启动新连接,看看你有几个选项来启动连接,一个最终提交,一个最终回滚。这些只是你可以随身携带计算的两个例子。基本上,您bind(一个简单的modaic绑定)的每个计算都可能带有信息。
这是我在上面的讨论中给出的一个例子:
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查询)作为参数传递给绑定的函数。你也可以为runStatement提供更多参数。
将它们放在一起,你会得到类似的东西:
ResultSet rs = DbState.reader("conn-url").run(prepareStatement("select * from table").bind(runStatement());
希望这有帮助!
答案 1 :(得分:0)
这是我想出的。反馈非常感谢。 我按照上面的答案,从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
}
}