Link to Snippet

Imperative computation builder

Defines an F# computation builder for encoding imperative computations. The 'return' construct returns immediately and terminates the rest of the computation. It is also possible to return value from a 'for' or 'while' loop.

// For more information about this snippet, see the blog post:// * http://tomasp.net/blog/imperative-i-return.aspx/// A type that represents imperative computation/// that runs and may return a result at the endtypeImperative<'T>=unit->option<'T>typeImperativeBuilder() =// Creatae computation that returns the given value memberx.Return(v) :Imperative<_>=
(fun () ->Some(v))
// Create computation that doesn't return any valuememberx.Zero() = (fun () ->None)
// Return a computation that will evaluate the provided function // only when the computation is being evaluatedmemberx.Delay(f:unit->Imperative<_>) =
(fun () ->f()())
// Combines two delayed computations (that may return // value imperatively using 'return') into one memberx.Combine(a, b) = (fun () ->// run the first part of the computationmatcha() with// if it returned, we can return the result immediately
| Some(v) ->Some(v)
// otherwise, we need to run the second part
| _ ->b() )
// Execute the imperative computation // expression given as an argumentmemberx.Run(imp) =// run the computation and return the result or // fail when the computation didn't return anythingmatchimp() with
| Some(v) ->v
| None->failwith"nothing returned!"memberx.For(inp:seq<_>, f) =// Process next element from the sequenceletrecloop(en:IEnumerator<_>) =// If ther are no more elements, return empty computationifnot(en.MoveNext()) thenx.Zero() else// Otherwise call body and combine it with a // computation that continues loopingx.Combine(f(en.Current), x.Delay(fun () ->loop(en)))
// Start enumerating from the first elementloop(inp.GetEnumerator())
memberx.While(gd, body) =// Perform one step of the 'looping'letrecloop() =// If the condition is false, return empty computationifnot(gd()) thenx.Zero() else// Otherwise, call body and then loop againx.Combine(body, x.Delay(fun () ->loop()))
loop()
letimperative=newImperativeBuilder()