1 people like it.

Monad Bind 3 of 3

After watching this clip (http://www.youtube.com/watch?v=ZhuHCtR3xq8) on Youtube featuring Brian Beckman I wanted to try to sketch Brian's main argument that Monads' main purpose is function composition. I will post my sketch to http://rodhern.wordpress.com/2014/02/ . These snippets are the companion examples to the blog post.

Link to blog post

1: 
2: 
// Script example (3 of 3) for Monad blog post
// at http://rodhern.wordpress.com/2014/02/

The Monad defined

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
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 }

Some Monad sugar

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
/// 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

Types

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
type A = int       
type B = float     
type C = DateTime  
type D = string    

type R = M<A>
type S = M<B>
type T = M<C>
type U = M<D>

Functions (first example repeated)

1: 
2: 
3: 
let f (noWeeks: A): B = float (7 * noWeeks)
let g (noDays: B): C = today.AddDays noDays
let h (date: C): D = date.ToShortDateString ()

Functions (packaged)

1: 
2: 
3: 
let pf: R -> S = package f
let pg: S -> T = package g
let ph: T -> U = package h

Function composition

1: 
2: 
let fghOne: R -> U = compose(compose(pf, pg), ph)
let fghTwo: R -> U = compose(pf, compose(pg, ph))

Some results (packaged functions)

1: 
2: 
let fghOneResult = fghOne (ret myInputParam)
let fghTwoResult = fghTwo (ret myInputParam)

Getting ready for computation expression

1: 
2: 
3: 
4: 
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

Some results (computation expression)

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
let builder = new ComputationExpressionBuilderType ()

let result =
    builder {
              let! a = Mx
              let! b = Mf a
              let! c = Mg b
              let! d = Mh c
              return d
            }
type M<'V> =
  {Value: 'V;
   LogMessages: string list;}
  static member StaticBind : mt:M<'a> -> fn:('a -> M<'b>) -> M<'b>
  static member StaticReturn : v:'a -> M<'a>

Full name: Script.M<_>


 In this example, as is often the case, the Monad, M<'V>,
 is a container for, 'V, the value if interest.
M.Value: 'V
M.LogMessages: string list
Multiple items
val string : value:'T -> string

Full name: Microsoft.FSharp.Core.Operators.string

--------------------
type string = String

Full name: Microsoft.FSharp.Core.string
type 'T list = List<'T>

Full name: Microsoft.FSharp.Collections.list<_>
static member M.StaticReturn : v:'a -> M<'a>

Full name: Script.M`1.StaticReturn
val v : 'a
static member M.StaticBind : mt:M<'a> -> fn:('a -> M<'b>) -> M<'b>

Full name: Script.M`1.StaticBind
val mt : M<'a>
val fn : ('a -> M<'b>)
val result : M<'b>
M.Value: 'a
val newmessage : string
val sprintf : format:Printf.StringFormat<'T> -> 'T

Full name: Microsoft.FSharp.Core.ExtraTopLevelOperators.sprintf
val logmessages : string list
M.Value: 'b
val ret : x:'a -> M<'a>

Full name: Script.ret


 A shorter name for M.StaticReturn.
 Turning a variable of type 'a into M<'a> .
val x : 'a
val package : fn:('a -> 'b) -> mt:M<'a> -> M<'b>

Full name: Script.package


 Turning a function with signature 'a -> 'b into M<'a> -> M<'b> .
val fn : ('a -> 'b)
Multiple items
type ComputationExpressionBuilderType =
  new : unit -> ComputationExpressionBuilderType
  member Bind : mt:M<'a> * fn:('a -> M<'b>) -> M<'b>
  member Return : v:'c -> M<'c>

Full name: Script.ComputationExpressionBuilderType


 The builder type may be an empty class like this one.
 Often the builder instance is really nothing more than
 a syntactic requirement.


--------------------
new : unit -> ComputationExpressionBuilderType
val self : ComputationExpressionBuilderType
member ComputationExpressionBuilderType.Return : v:'c -> M<'c>

Full name: Script.ComputationExpressionBuilderType.Return
val v : 'c
member ComputationExpressionBuilderType.Bind : mt:M<'a> * fn:('a -> M<'b>) -> M<'b>

Full name: Script.ComputationExpressionBuilderType.Bind
type A = int

Full name: Script.A
Multiple items
val int : value:'T -> int (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.int

--------------------
type int = int32

Full name: Microsoft.FSharp.Core.int

--------------------
type int<'Measure> = int

Full name: Microsoft.FSharp.Core.int<_>
type B = float

Full name: Script.B
Multiple items
val float : value:'T -> float (requires member op_Explicit)

Full name: Microsoft.FSharp.Core.Operators.float

--------------------
type float = Double

Full name: Microsoft.FSharp.Core.float

--------------------
type float<'Measure> = float

Full name: Microsoft.FSharp.Core.float<_>
type C = DateTime

Full name: Script.C
Multiple items
type DateTime =
  struct
    new : ticks:int64 -> DateTime + 10 overloads
    member Add : value:TimeSpan -> DateTime
    member AddDays : value:float -> DateTime
    member AddHours : value:float -> DateTime
    member AddMilliseconds : value:float -> DateTime
    member AddMinutes : value:float -> DateTime
    member AddMonths : months:int -> DateTime
    member AddSeconds : value:float -> DateTime
    member AddTicks : value:int64 -> DateTime
    member AddYears : value:int -> DateTime
    ...
  end

Full name: System.DateTime

--------------------
DateTime()
   (+0 other overloads)
DateTime(ticks: int64) : unit
   (+0 other overloads)
DateTime(ticks: int64, kind: DateTimeKind) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, calendar: Globalization.Calendar) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, kind: DateTimeKind) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, calendar: Globalization.Calendar) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int) : unit
   (+0 other overloads)
DateTime(year: int, month: int, day: int, hour: int, minute: int, second: int, millisecond: int, kind: DateTimeKind) : unit
   (+0 other overloads)
type D = string

Full name: Script.D
type R = M<A>

Full name: Script.R
type S = M<B>

Full name: Script.S
type T = M<C>

Full name: Script.T
type U = M<D>

Full name: Script.U
val f : noWeeks:A -> B

Full name: Script.f
val noWeeks : A
val g : noDays:B -> C

Full name: Script.g
val noDays : B
val today : DateTime

Full name: Script.today


 Today's date and time for use in examples below.
DateTime.AddDays(value: float) : DateTime
val h : date:C -> D

Full name: Script.h
val date : C
DateTime.ToShortDateString() : string
val pf : (R -> S)

Full name: Script.pf
val pg : (S -> T)

Full name: Script.pg
val ph : (T -> U)

Full name: Script.ph
val fghOne : (R -> U)

Full name: Script.fghOne
val compose : fun1:('a -> 'b) * fun2:('b -> 'c) -> arg:'a -> 'c

Full name: Script.compose


 Our good old compose function from the first example
val fghTwo : (R -> U)

Full name: Script.fghTwo
val fghOneResult : U

Full name: Script.fghOneResult
val myInputParam : int

Full name: Script.myInputParam


 The test parameter I will use is "2" as in "two weeks from now".
val fghTwoResult : U

Full name: Script.fghTwoResult
val Mx : R

Full name: Script.Mx
val Mf : (A -> S)

Full name: Script.Mf
val Mg : (B -> T)

Full name: Script.Mg
val Mh : (C -> U)

Full name: Script.Mh
val builder : ComputationExpressionBuilderType

Full name: Script.builder


 By the construction of the syntax the builder must be an instance
 of the builder type. The static methods will not do by themselves.
val result : M<D>

Full name: Script.result
val a : A
val b : B
val c : C
val d : D
Raw view Test code New version

More information

Link:http://fssnip.net/lI
Posted:10 years ago
Author:Robert Nielsen
Tags: monad , bind , example , learning f#