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 while
loop 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)
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 alsoFor
) member is usually the same.This is because you can always express the operation in terms of
Bind
,Zero
and recursive uses ofWhile
. So if you are working with monads, you will always define something like this (Just replaceM<_>
with your monad):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...