4

这个问题受到了这个问题的启发。我理解示例(ListBuilder),但我无法while为我的状态单子创建循环。我不清楚的是,随着迭代一个一个地跟随循环bind体。while

谢谢你。

/////////////////////////////////////////////////////////////////////////////////////
// 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)
4

1 回答 1

4

如果您查看ExtCore中的不同计算构建器,有一件有趣的事情需要注意 - 对于任何 monad,While(以及For)成员的实现通常是相同的。

这是因为您始终可以根据BindZero递归使用来表达操作While。因此,如果您正在使用 monad,您将始终定义类似这样的内容(只需替换M<_>为您的 monad):

// (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# 代码不像 Haskell 那样经常使用 monad,而且大多数时候,你应该对标准库所拥有的(或ExtCore 定义的内容,如果您正在做更高级的事情)。也许您需要自定义计算,但请记住,这可能会分散您的注意力,将您引向错误的方向……

于 2014-01-27T15:41:10.030 回答