Question

This question has been inspired by this question. I understand the example (ListBuilder) but I have not been able to create a while loop for my state monad. What is not clear to me is how to bind the body of the whileloop as the iterations follow one an another.

Thank you.

/////////////////////////////////////////////////////////////////////////////////////
// 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)
Was it helpful?

Solution

If you look at the different computation builders in ExtCore, there is one interesting thing to note - for any monad, the implementation of While (and also For) member is usually the same.

This is because you can always express the operation in terms of Bind, Zero and recursive uses of While. So if you are working with monads, you will always define something like this (Just replace M<_> with your 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 ()

This is not true for all computations - if the computation is more interesting in some way, then it may need a different implementation of While, but the above is a reasonable default.

Aside - I think that the need to define custom computation expressions in F# should be quite rare - idiomatic F# code does not use monads nearly as often as e.g. Haskell and most of the time, you should be fine with what the standard library has (or what ExtCore defines, if you are doing something more advanced). Perhaps you need a custom computation, but keep in mind that this might be a distraction leading you in a wrong direction...

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top