Search code examples
f#computation-expression

Avoiding the pyramid of doom with Computation Expressions?


I came across this question about the "pyramid of doom" in F#. The accepted answer there involves using Active Patterns, however my understanding is that it can also be solved using Computation Expressions.

How can I remove the "pyramid of doom" from this code using Computation Expressions?

match a.TryGetValue(key) with
| (true, v) -> v
| _ -> 
  match b.TryGetValue(key) with
  | (true, v) -> v
  | _ -> 
    match c.TryGetValue(key) with
    | (true, v) -> v
    | _ -> defaultValue

Solution

  • F# for fun and profit has an example for this specific case:

    type OrElseBuilder() =
        member this.ReturnFrom(x) = x
        member this.Combine (a,b) = 
            match a with
            | Some _ -> a  // a succeeds -- use it
            | None -> b    // a fails -- use b instead
        member this.Delay(f) = f()
    
    let orElse = new OrElseBuilder()
    

    But if you want to use it with IDictionary you need a lookup function that returns an option:

    let tryGetValue key (d:System.Collections.Generic.IDictionary<_,_>) =
        match d.TryGetValue key with
        | true, v -> Some v
        | false, _ -> None
    

    Now here's a modified example of its usage from F# for fun and profit:

    let map1 = [ ("1","One"); ("2","Two") ] |> dict
    let map2 = [ ("A","Alice"); ("B","Bob") ] |> dict
    let map3 = [ ("CA","California"); ("NY","New York") ] |> dict
    
    let multiLookup key = orElse {
        return! map1 |> tryGetValue key
        return! map2 |> tryGetValue key
        return! map3 |> tryGetValue key
        }
    
    multiLookup "A" // Some "Alice"