Computation Expressions

[Sequence Expressions](types/sequence.html), [Asynchronous Workflows](async_workflows.html) and [Query Expressions](query_expressions.html) are types of computation expressions.

Logging all assignments

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
type LoggingBuilder() =
  let log p = printfn "expression is %A" p
  member this.Bind(x, f) =
    log x
    f x
  member this.Return(x) =
    x

let logger = LoggingBuilder()
let loggedWorkflow =
  logger {
    let! x = 42
    let! y = 43
    let! z = x + y
    return z
  }

use! can be applied instead of let! when the result implements IDisposable (same use as use).

Handling divide by zero

Handle a series of divide operations, shortcutting if any divide by zero

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
let divideBy bottom top =
  if bottom = 0
  then None
  else Some(top / bottom)

let (>>=) m f = Option.bind f m
let divideByWorkflow x y w z =
  x |> divideBy y
  >>= divideBy w
  >>= divideBy z

let printResult result =
  match result with
  | Some result -> printfn "Good workflow: %i" result
  | None -> printfn "Bad workflow: Divide by zero happened"

let good = divideByWorkflow 12 3 2 1
let bad = divideByWorkflow 12 3 0 1

printResult good
printResult bad

Return early if a call succeeds

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
19: 
20: 
21: 
22: 
23: 
type OrElseBuilder() =
  member this.ReturnFrom(x) = x
  member this.Combine(a, b) =
    match a with
    | Some _ -> a
    | None -> b
  member this.Delay(f) = f()

let orElse = OrElseBuilder()

let map1 = [("1", "One"); ("2", "Two")] |> Map.ofList
let map2 = [("A", "Alice"); ("B", "Bob")] |> Map.ofList
let map3 = [("CA", "California"); ("NY", "New York")] |> Map.ofList

let multiLookup key = orElse {
  return! map1.TryFind key
  return! map2.TryFind key
  return! map3.TryFind key
}

multiLookup "A" |> printfn "Result for A is %A"
multiLookup "CA" |> printfn "Result for CA is %A"
multiLookup "X" |> printfn "Result for X is %A"

Continuations

Caller decides what to do with a result instead of the function.

1: 
2: 
3: 
4: 
let divide ifZero ifSuccess top bottom =
  if (bottom = 0)
  then ifZero()
  else ifSuccess (top / bottom)

Print out the result

1: 
2: 
3: 
4: 
5: 
6: 
7: 
let ifZeroPrint () = printfn "bad"
let ifSuccessPrint x = printfn "good %i" x

let dividePrint = divide ifZeroPrint ifSuccessPrint

let goodPrint = dividePrint 6 3
let badPrint = dividePrint 6 0

Convert to an option

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
let ifZeroOption () = None
let ifSuccessOption x = Some x

let divideOption = divide ifZeroOption ifSuccessOption

let goodOption = divideOption 6 3
let badOption = divideOption 6 0

printfn "End.\n"