2013-10-29 31 views
0

我在玩功能性編程,特別是功能性Java。我已經成功實施了我的IO Monad版本,並且正在爲我的核心寫入IO操作。它基本上是將對象序列化爲Xml文件(對象類型擴展了自定義的XmlWritable接口)。範圍管理 - 狀態IO Monad?

不幸的是,爲了做到這一點,需要創建一個OutputStream實例和一個XmlSerializer實例。 OutputStream的範圍比XmlSerializer的範圍要寬,這意味着我能夠正確處理IO 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。

+1

我通過現在(對不起,Java 6的是痛苦的FP),閱讀你的代碼苦讀,但IO ** **是國家單子,在一個更專門的形式 – jozefg

+0

好吧,沒問題:)當你需要維持一些狀態跨Monadic函數時,它是一個常見的模式來傳遞元組? –

+0

請允許我提出一個問題:你爲什麼用一種不太適合它的語言來做這件事?您可以使用Scala,Clojure或Frege編寫程序的功能部分。 – Ingo

回答

2

我實際上從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查詢作爲參數傳遞給被綁定的功能。你可以有更多的參數runStatement。

把它放在一起,你喜歡的東西:

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

希望這有助於!

+0

不錯,謝謝!我也接近看到如何處理這個問題,但我仍然看不到它。基本上,在您的頁面之後,如何處理我需要將Connection連接到計算結束的事實,以便關閉它? –

+1

嘿卡普,我試着回答你的問題在上面的身體.. – Shlomi

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 

    } 
}