2

I'm trying to master continuation passing style (CPS) and am therefore reworking an example shown to me by Gary Short quite a while ago. I don't have his sample source code so I'm trying to rework his example from memory. Consider the following code:

let checkedDiv m n =
    match n with
    | 0.0 -> None
    | _ -> Some(m/n)

let reciprocal r = checkedDiv 1.0 r

let resistance c1 c2 c3 =
     (fun c1 -> if (reciprocal c1).IsSome then 
        (fun c2 -> if (reciprocal c2).IsSome then
            (fun c3 -> if (reciprocal c3).IsSome then 
                Some((reciprocal c1).Value + (reciprocal c2).Value + (reciprocal c3).Value))));;

What I can't quite figure out is how to structure the resistance function. I came up with this earlier:

let resistance r1 r2 r3 =
        if (reciprocal r1).IsSome then
            if (reciprocal r2).IsSome then
                if (reciprocal r3).IsSome then
                    Some((reciprocal r1).Value + (reciprocal r2).Value + (reciprocal r3).Value)
                else
                    None
            else
                None
        else
            None 

but, of course, that's not using CPS--not to mention the fact that it seems really hacky and there's quite a bit of repeated code which also seems like a code smell.

Can someone show me how to rewrite the resistance function in a CPS way?

hammar
  • 138,522
  • 17
  • 304
  • 385
Onorio Catenacci
  • 14,928
  • 14
  • 81
  • 132
  • Can you use computation expressions? Or is that not CPS-compliant? – Shlomo Dec 16 '11 at 17:43
  • @Shlomo--that's not a bad suggestion but it's sort of putting the cart before the horse. I'm trying to master CPS because CPS is part of the structure of computation expressions. – Onorio Catenacci Dec 16 '11 at 17:51

3 Answers3

3

straightforward way:

let resistance_cps c1 c2 c3 = 
    let reciprocal_cps r k = k (checkedDiv 1.0 r)
    reciprocal_cps c1 <| 
        function
        | Some rc1 -> 
            reciprocal_cps c2 <| 
                function
                | Some rc2 -> 
                    reciprocal_cps c3 <|
                        function 
                        | Some rc3 -> Some (rc1 + rc2 + rc3)
                        | _ -> None
                | _ -> None
        | _ -> None

or a bit shorter with Option.bind

let resistance_cps2 c1 c2 c3 = 
    let reciprocal_cps r k = k (checkedDiv 1.0 r)
    reciprocal_cps c1 <|
        Option.bind(fun rc1 -> 
            reciprocal_cps c2 <| 
                Option.bind(fun rc2 ->
                    reciprocal_cps c3 <| 
                        Option.bind(fun rc3 -> Some (rc1 + rc2 + rc3))
                )
        )
desco
  • 16,642
  • 1
  • 45
  • 56
2

This is a known task from "Programming F#" book by Chris Smith; the CPS-style solution code is given on page 244 there:

let let_with_check result restOfComputation =
    match result with
    | DivByZero -> DivByZero
    | Success(x) -> restOfComputation x

let totalResistance r1 r2 r3 =
    let_with_check (divide 1.0 r1) (fun x ->
    let_with_check (divide 1.0 r2) (fun y ->
    let_with_check (divide 1.0 r3) (fun z ->
    divide 1.0 (x + y + z) ) ) )
Gene Belitski
  • 10,270
  • 1
  • 34
  • 54
2

Using the Maybe monad defined here

let resistance r1 r2 r3 =
  maybe {
    let! r1 = reciprocal r1
    let! r2 = reciprocal r2
    let! r3 = reciprocal r3
    return r1 + r2 + r3
  }
Daniel
  • 47,404
  • 11
  • 101
  • 179
  • That's not a bad suggestion but I'm trying to work up to understanding monads and understanding CPS is part of understanding monads. – Onorio Catenacci Dec 16 '11 at 17:50
  • Then maybe a useful exercise would be desugaring this by looking at the definition of `Bind`. – Daniel Dec 16 '11 at 19:16