3 people like it.

Simple builder example: Nullable

Simple Computational expressions / monad / builder -example, using .NET Nullable as demo.

  1: 
  2: 
  3: 
  4: 
  5: 
  6: 
  7: 
  8: 
  9: 
 10: 
 11: 
 12: 
 13: 
 14: 
 15: 
 16: 
 17: 
 18: 
 19: 
 20: 
 21: 
 22: 
 23: 
 24: 
 25: 
 26: 
 27: 
 28: 
 29: 
 30: 
 31: 
 32: 
 33: 
 34: 
 35: 
 36: 
 37: 
 38: 
 39: 
 40: 
 41: 
 42: 
 43: 
 44: 
 45: 
 46: 
 47: 
 48: 
 49: 
 50: 
 51: 
 52: 
 53: 
 54: 
 55: 
 56: 
 57: 
 58: 
 59: 
 60: 
 61: 
 62: 
 63: 
 64: 
 65: 
 66: 
 67: 
 68: 
 69: 
 70: 
 71: 
 72: 
 73: 
 74: 
 75: 
 76: 
 77: 
 78: 
 79: 
 80: 
 81: 
 82: 
 83: 
 84: 
 85: 
 86: 
 87: 
 88: 
 89: 
 90: 
 91: 
 92: 
 93: 
 94: 
 95: 
 96: 
 97: 
 98: 
 99: 
100: 
101: 
102: 
103: 
104: 
105: 
106: 
107: 
108: 
109: 
module BuilderExample
open System

//------------------------------------------------
//Example 1: First try, just return

type Nullable1Builder() =
    member this.Return(x) = Nullable(x)

let myNullable1 = Nullable1Builder()

let MakeNullable =
    myNullable1{
        let a = 5 + 1
        return a // calls builders Return(x)
    }
//val test : Nullable<int> = 6

//------------------------------------------------
//Example 2: nullable { ... } with combining functionality

type Nullable2Builder() =
    let hasValue (a:Nullable<'a>) = a.HasValue 
    member t.Return(x) = Nullable(x)
    member t.Bind(x, rest) = 
        match hasValue x with 
        | false -> System.Nullable() 
        | true -> rest(x.Value)

let nullable = Nullable2Builder()

let test =
    nullable{
        let! a = System.Nullable(4) // Call Bind
        let! b = System.Nullable(5) // Call Bind

        //Inside computation expression(/monad): easy programming without Nullable-worries:
        let mult = a * b 
        let sum = mult + 1 

        return sum //Call Return
    }
//val test : Nullable<int> = 16



//------------------------------------------------------------------------------
// using e.g.
//     let! b = System.Nullable() 
// would cause to return:
//     val test : System.Nullable()

// own { ...} syntax is made with implementing Builder-class
// and (some of) the "interface" members




//------------------------------------------------------------------------------
// Some optional technical details:
// Actual stack in example2, you don't have to care about this:

// nullable.Bind(Nullable(3), (fun res1 ->
//   nullable.Bind(Nullable(5), (fun res2 ->
//     nullable.Let(res1*res2, (fun res3 ->
//       nullable.Let(res3+1, (fun res4 ->
//         nullable.Return(res4)))))))))



//------------------------------------------------

// Further reading:
// Interface described in: http://msdn.microsoft.com/en-us/library/dd233182.aspx
// More info: http://blogs.msdn.com/b/dsyme/archive/2007/09/22/some-details-on-f-computation-expressions-aka-monadic-or-workflow-syntax.aspx

// Check also Reactive Extensions 2.0 with F# observe { ... }:
// https://github.com/panesofglass/FSharp.Reactive/blob/master/src/Observable.fs

// and: http://fssnip.net/tags/computation+builder and http://fssnip.net/tags/monad




//------------------------------------------------
//<just for intellisense>
type M<'T> = System.Collections.Generic.IEnumerable<'T>
type ImplementJustWhatYouWantBuilder = 
//</just for intellisense>

// The builder methods are described in MSDN:

      abstract member Bind:  M<'T> * ('T -> M<'U>) -> M<'U>  // Called for let! and do! in computation expressions.
      abstract member Delay:  (unit -> M<'T>) -> M<'T> // Wraps a computation expression as a function.
      abstract member Return:  'T -> M<'T> // Called for return in computation expressions.
      abstract member ReturnFrom: M<'T> -> M<'T> // Called for return! in computation expressions.
      abstract member Run:  M<'T> -> M<'T>  // Executes a computation expression.
// or abstract member Run:  M<'T> -> 'T //  Executes a computation expression.
      abstract member Combine: M<'T> * M<'T> -> M<'T> // Called for sequencing in computation expressions.
      abstract member Combine: M<unit> * M<'T> -> M<'T> // Called for sequencing in computation expressions. 
      abstract member For:  seq<'T> * ('T -> M<'U>) -> M<'U>  // Called for for...do expressions in computation expressions.
// or abstract member For:  seq<'T> * ('T -> M<'U>) -> seq<M<'U>> // Called for for...do expressions in computation expressions.
      abstract member TryFinally: M<'T> * (unit -> unit) -> M<'T> // Called for try...finally expressions in computation expressions.
      abstract member TryWith: M<'T> * (exn -> M<'T>) -> M<'T> // Called for try...with expressions in computation expressions.
      abstract member Using:  'T * ('T -> M<'U>) -> M<'U> when 'U :> IDisposable // Called for use bindings in computation expressions.
      abstract member While:  (unit -> bool) * M<'T> -> M<'T> // Called for while...do expressions in computation expressions.
      abstract member Yield:  'T -> M<'T> // Called for yield expressions in computation expressions.
      abstract member YieldFrom: M<'T> -> M<'T> // Called for yield! expressions in computation expressions.
      abstract member Zero:  unit -> M<'T> // Called for empty else branches of if...then expressions in computation expressions.
module BuilderExample
namespace System
Multiple items
type Nullable1Builder =
  new : unit -> Nullable1Builder
  member Return : x:'a -> Nullable<'a> (requires default constructor and value type and 'a :> ValueType)

Full name: BuilderExample.Nullable1Builder

--------------------
new : unit -> Nullable1Builder
val this : Nullable1Builder
member Nullable1Builder.Return : x:'a -> Nullable<'a> (requires default constructor and value type and 'a :> ValueType)

Full name: BuilderExample.Nullable1Builder.Return
val x : 'a (requires default constructor and value type and 'a :> ValueType)
Multiple items
type Nullable =
  static member Compare<'T> : n1:Nullable<'T> * n2:Nullable<'T> -> int
  static member Equals<'T> : n1:Nullable<'T> * n2:Nullable<'T> -> bool
  static member GetUnderlyingType : nullableType:Type -> Type

Full name: System.Nullable

--------------------
type Nullable<'T (requires default constructor and value type and 'T :> ValueType)> =
  struct
    new : value:'T -> Nullable<'T>
    member Equals : other:obj -> bool
    member GetHashCode : unit -> int
    member GetValueOrDefault : unit -> 'T + 1 overload
    member HasValue : bool
    member ToString : unit -> string
    member Value : 'T
  end

Full name: System.Nullable<_>

--------------------
Nullable()
Nullable(value: 'T) : unit
val myNullable1 : Nullable1Builder

Full name: BuilderExample.myNullable1
val MakeNullable : Nullable<int>

Full name: BuilderExample.MakeNullable
val a : int
Multiple items
type Nullable2Builder =
  new : unit -> Nullable2Builder
  member Bind : x:Nullable<'a> * rest:('a -> Nullable<'b>) -> Nullable<'b> (requires default constructor and value type and 'a :> ValueType and default constructor and value type and 'b :> ValueType)
  member Return : x:'c -> Nullable<'c> (requires default constructor and value type and 'c :> ValueType)

Full name: BuilderExample.Nullable2Builder

--------------------
new : unit -> Nullable2Builder
val hasValue : (Nullable<'a> -> bool) (requires default constructor and value type and 'a :> ValueType)
val a : Nullable<'a> (requires default constructor and value type and 'a :> ValueType)
property Nullable.HasValue: bool
val t : Nullable2Builder
member Nullable2Builder.Return : x:'c -> Nullable<'c> (requires default constructor and value type and 'c :> ValueType)

Full name: BuilderExample.Nullable2Builder.Return
val x : 'c (requires default constructor and value type and 'c :> ValueType)
member Nullable2Builder.Bind : x:Nullable<'a> * rest:('a -> Nullable<'b>) -> Nullable<'b> (requires default constructor and value type and 'a :> ValueType and default constructor and value type and 'b :> ValueType)

Full name: BuilderExample.Nullable2Builder.Bind
val x : Nullable<'a> (requires default constructor and value type and 'a :> ValueType)
val rest : ('a -> Nullable<'b>) (requires default constructor and value type and 'a :> ValueType and default constructor and value type and 'b :> ValueType)
property Nullable.Value: 'a
val nullable : Nullable2Builder

Full name: BuilderExample.nullable
val test : Nullable<int>

Full name: BuilderExample.test
val b : int
val mult : int
val sum : int
type M<'T> = Collections.Generic.IEnumerable<'T>

Full name: BuilderExample.M<_>
namespace System.Collections
namespace System.Collections.Generic
type IEnumerable<'T> =
  member GetEnumerator : unit -> IEnumerator<'T>

Full name: System.Collections.Generic.IEnumerable<_>
type ImplementJustWhatYouWantBuilder =
  interface
    abstract member Bind : M<'T> * ('T -> M<'U>) -> M<'U>
    abstract member Combine : M<'T> * M<'T> -> M<'T>
    abstract member Combine : M<unit> * M<'T> -> M<'T>
    abstract member Delay : (unit -> M<'T>) -> M<'T>
    abstract member For : seq<'T> * ('T -> M<'U>) -> M<'U>
    abstract member Return : 'T -> M<'T>
    abstract member ReturnFrom : M<'T> -> M<'T>
    abstract member Run : M<'T> -> M<'T>
    abstract member TryFinally : M<'T> * (unit -> unit) -> M<'T>
    abstract member TryWith : M<'T> * (exn -> M<'T>) -> M<'T>
    ...
  end

Full name: BuilderExample.ImplementJustWhatYouWantBuilder
abstract member ImplementJustWhatYouWantBuilder.Bind : M<'T> * ('T -> M<'U>) -> M<'U>

Full name: BuilderExample.ImplementJustWhatYouWantBuilder.Bind
abstract member ImplementJustWhatYouWantBuilder.Delay : (unit -> M<'T>) -> M<'T>

Full name: BuilderExample.ImplementJustWhatYouWantBuilder.Delay
type unit = Unit

Full name: Microsoft.FSharp.Core.unit
abstract member ImplementJustWhatYouWantBuilder.Return : 'T -> M<'T>

Full name: BuilderExample.ImplementJustWhatYouWantBuilder.Return
abstract member ImplementJustWhatYouWantBuilder.ReturnFrom : M<'T> -> M<'T>

Full name: BuilderExample.ImplementJustWhatYouWantBuilder.ReturnFrom
abstract member ImplementJustWhatYouWantBuilder.Run : M<'T> -> M<'T>

Full name: BuilderExample.ImplementJustWhatYouWantBuilder.Run
abstract member ImplementJustWhatYouWantBuilder.Combine : M<'T> * M<'T> -> M<'T>

Full name: BuilderExample.ImplementJustWhatYouWantBuilder.Combine
abstract member ImplementJustWhatYouWantBuilder.Combine : M<unit> * M<'T> -> M<'T>

Full name: BuilderExample.ImplementJustWhatYouWantBuilder.Combine
abstract member ImplementJustWhatYouWantBuilder.For : seq<'T> * ('T -> M<'U>) -> M<'U>

Full name: BuilderExample.ImplementJustWhatYouWantBuilder.For
Multiple items
val seq : sequence:seq<'T> -> seq<'T>

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

--------------------
type seq<'T> = Collections.Generic.IEnumerable<'T>

Full name: Microsoft.FSharp.Collections.seq<_>
abstract member ImplementJustWhatYouWantBuilder.TryFinally : M<'T> * (unit -> unit) -> M<'T>

Full name: BuilderExample.ImplementJustWhatYouWantBuilder.TryFinally
abstract member ImplementJustWhatYouWantBuilder.TryWith : M<'T> * (exn -> M<'T>) -> M<'T>

Full name: BuilderExample.ImplementJustWhatYouWantBuilder.TryWith
type exn = Exception

Full name: Microsoft.FSharp.Core.exn
abstract member ImplementJustWhatYouWantBuilder.Using : 'T * ('T -> M<'U>) -> M<'U> (requires 'U :> IDisposable)

Full name: BuilderExample.ImplementJustWhatYouWantBuilder.Using
type IDisposable =
  member Dispose : unit -> unit

Full name: System.IDisposable
abstract member ImplementJustWhatYouWantBuilder.While : (unit -> bool) * M<'T> -> M<'T>

Full name: BuilderExample.ImplementJustWhatYouWantBuilder.While
type bool = Boolean

Full name: Microsoft.FSharp.Core.bool
abstract member ImplementJustWhatYouWantBuilder.Yield : 'T -> M<'T>

Full name: BuilderExample.ImplementJustWhatYouWantBuilder.Yield
abstract member ImplementJustWhatYouWantBuilder.YieldFrom : M<'T> -> M<'T>

Full name: BuilderExample.ImplementJustWhatYouWantBuilder.YieldFrom
abstract member ImplementJustWhatYouWantBuilder.Zero : unit -> M<'T>

Full name: BuilderExample.ImplementJustWhatYouWantBuilder.Zero

More information

Link:http://fssnip.net/bE
Posted:12 years ago
Author:Tuomas Hietanen
Tags: builder , monad , computation builder , nullable