2014-01-27 44 views
4

此問題受this question啓發。 我理解這個例子(ListBuilder),但我一直無法爲我的狀態monad創建一個while循環。我不清楚如何bindwhile循環的主體,因爲迭代是一個又一個。State Monad - While-loops

謝謝。

///////////////////////////////////////////////////////////////////////////////////// 
// Definition of the state 
///////////////////////////////////////////////////////////////////////////////////// 
type StateFunc<'State, 'T> = 'State -> 'T * 'State 



///////////////////////////////////////////////////////////////////////////////////// 
// Definition of the State monad 
///////////////////////////////////////////////////////////////////////////////////// 
type StateMonadBuilder<'State>() = 

    // M<'T> -> M<'T> 
    member b.ReturnFrom a : StateFunc<'State, 'T> = a 

    // 'T -> M<'T> 
    member b.Return a : StateFunc<'State, 'T> = (fun s -> a, s) 

    // M<'T> * ('T -> M<'U>) -> M<'U> 
    member b.Bind(p : StateFunc<_, 'T>, rest : 'T -> StateFunc<_,_>) : StateFunc<'State, 'U> = 
     (fun s -> 
      let a, s' = p s 
      rest a s') 

    member b.Zero() = fun s ->(), s 

    member b.Delay f = f 

    member b.Run f = f() 

    // Getter for the whole state, this type signature is because it passes along the state & returns the state 
    member b.getState : StateFunc<'State, _> = (fun s -> s, s) 

    // Setter for the state 
    member b.putState (s:'State) : StateFunc<'State, _> = (fun _ ->(), s) 

    // (unit -> bool) * M<'T> -> M<'T> 
    member b.While (cond, body: StateFunc<'State, _>): StateFunc<'State, _> = 
     (fun s -> 
      if cond() then 
       let bind = 
        let _, s' = body s 
        fun s' -> body s'  
       b.While (cond, bind) // This is wrong 
      else 
       body s) 
+1

您的示例代碼似乎來自[ExtCore](https://github.com/jack-帕帕斯/ ExtCore)。你看過那裏的代碼,看它是否包含'StateBuilder'類型的'While'成員的定義? –

+0

太棒了。我在https://github.com/jack-pappas/ExtCore/blob/master/ExtCore/Control.fs找到了一個。謝謝。 – NoIdeaHowToFixThis

回答

4

如果您在ExtCore看看不同的計算建設者,還有要注意的一個有趣的事情 - 對任何單子,While(也For)成員的實現通常是一樣的。

這是因爲您總是可以根據Bind,ZeroWhile的遞歸使用來表達操作。所以,如果你有單子的工作,你將永遠定義是這樣的(只需用單子替換M<_>):

// (unit -> bool) * M<'T> -> M<'T> 
member this.While (guard, body : M<_>) : M<_> = 
    if guard() then 
     this.Bind (body, (fun() -> this.While (guard, body))) 
    else 
     this.Zero() 

這不是所有計算真正的 - 如果計算是在某些方面更有趣,那麼它可能需要不同的實現While,但以上是合理的默認值。

另外 - 我認爲需要在F#中定義自定義計算表達式應該是非常罕見的 - 慣用的F#代碼幾乎不像使用monad那樣頻繁。 Haskell和大多數時候,你應該對標準庫有什麼好(或者如果你正在做更高級的事情,ExtCore定義了什麼)。也許你需要一個自定義的計算,但請記住,這可能是一個分心,導致你在一個錯誤的方向...