// [snippet:Link to blog post]
// Script example (3 of 3) for Monad blog post
// at http://rodhern.wordpress.com/2014/02/
// [/snippet]
open System
/// Today's date and time for use in examples below.
let today = DateTime.Now
/// The test parameter I will use is "2" as in "two weeks from now".
let myInputParam = 2
// ---- ---- ----
// -- The Monad example of function composition (Bind)
// ---- ---- ----
/// In this example, as is often the case, the Monad, M<'V>,
/// is a container for, 'V, the value if interest.
// [snippet:The Monad defined]
type M<'V> =
{
Value: 'V
LogMessages: string list
}
with
static member StaticReturn v = { Value= v; LogMessages= [] }
static member StaticBind (mt: M<'a>) (fn: 'a -> M<'b>) =
let result = fn mt.Value
let newmessage = sprintf ("Passing value %+A") mt.Value
let logmessages = result.LogMessages @ (newmessage :: mt.LogMessages)
{ Value= result.Value; LogMessages= logmessages }
// [/snippet]
// [snippet:Some Monad sugar]
/// A shorter name for M.StaticReturn.
/// Turning a variable of type 'a into M<'a> .
let ret x = M<_>.StaticReturn x
/// Turning a function with signature 'a -> 'b into M<'a> -> M<'b> .
let package fn mt = M<_>.StaticBind mt (fn >> ret)
/// The builder type may be an empty class like this one.
/// Often the builder instance is really nothing more than
/// a syntactic requirement.
type ComputationExpressionBuilderType () =
member public self.Return v = M<_>.StaticReturn v
member public self.Bind (mt, fn) = M<_>.StaticBind mt fn
// [/snippet]
/// Our good old compose function from the first example
let compose (fun1, fun2) = fun arg -> fun2 (fun1 (arg))
(* FSSnip is not impressed with this notation ;-)
type A = int type R = M
type B = float type S = M**
type C = DateTime type T = M
type D = string type U = M
*)
// [snippet:Types]
type A = int
type B = float
type C = DateTime
type D = string
type R = M****
type S = M****
type T = M
type U = M
// [/snippet]
// Functions from first example ( f: A -> B, g: B -> C, g: C -> D)
// [snippet:Functions (first example repeated)]
let f (noWeeks: A): B = float (7 * noWeeks)
let g (noDays: B): C = today.AddDays noDays
let h (date: C): D = date.ToShortDateString ()
// [/snippet]
// The same functions packaged ( pf: R -> S, pg: S -> T, h: T -> U)
// [snippet:Functions (packaged)]
let pf: R -> S = package f
let pg: S -> T = package g
let ph: T -> U = package h
// [/snippet]
// The actual composition of the three functions
// [snippet:Function composition]
let fghOne: R -> U = compose(compose(pf, pg), ph)
let fghTwo: R -> U = compose(pf, compose(pg, ph))
// [/snippet]
// [snippet:Some results (packaged functions)]
let fghOneResult = fghOne (ret myInputParam)
let fghTwoResult = fghTwo (ret myInputParam)
// [/snippet]
// The preparation (Mf: A -> S, Mg: B -> T, Mg: C -> U)
// [snippet:Getting ready for computation expression]
let Mx: R = ret myInputParam
let Mf: A -> S = f >> M<_>.StaticReturn
let Mg: B -> T = g >> M<_>.StaticReturn
let Mh: C -> U = h >> M<_>.StaticReturn
// [/snippet]
// Example of calling functions using computation expression syntax
/// By the construction of the syntax the builder must be an instance
/// of the builder type. The static methods will not do by themselves.
// [snippet:Some results (computation expression)]
let builder = new ComputationExpressionBuilderType ()
let result =
builder {
let! a = Mx
let! b = Mf a
let! c = Mg b
let! d = Mh c
return d
}
// [/snippet]
**