2014-01-27 43 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定义了什么)。也许你需要一个自定义的计算,但请记住,这可能是一个分心,导致你在一个错误的方向...