The functional alternative to mutating variables is to return an updated value from a function. Stateful computations can be modelled by functions which take the current value of the state and return a pair containing the result and the updated value of the state. You can define such a type in F# as
type State<'s, 'a> = S of ('s -> ('s * 'a))
which just wraps a stateful function. You can then define some functions for manipulating stateful functions:
let returnState x = S (fun s -> (s, x))
let runState (S(f)) s = f s
let putState s = S (fun _ -> (s, ()))
let getState<'s, 'a> = S (fun (s: 's) -> (s, s))
returnState
creates a stateful computation which ignores the state and returns the given value. runState
runs a stateful computation with a given initial state. putState
is a stateful computation which gets the current state, while putState
updates the current state and returns a dummy value.
You can then combine stateful computations with the following function:
let bindState<'s, 'a, 'b> (S(sf): State<'s, 'a>) (f : 'a -> State<'s, 'b>) = S (fun s ->
let (s', r) = sf s
let (S(sf')) = f r
sf' s')
this takes a stateful computation and a function to construct a new computation given the result value from the first. It then constructs a compound computation which will run the first computation, use the value to construct the next computation and then run that given with the intermediate state returned from the first computation.
This forms a monad so you can create a computation expression and use it to make stateful computations with a more convenient syntax:
type StateBuilder() =
member this.Return(x) = returnState x
member this.Bind(s, f) = bindState s f
member this.ReturnFrom(s: State<_,_>) = s
let state = StateBuilder()
let modifyState f = state {
let! s = getState
let s' = f s
return! (putState s')
}
modifyState
takes a function a -> a
to transform the value of the state in a computation. You can then write a function to increment the value of a counter in a stateful computation:
let incState = modifyState ((+)1)