Sunday, September 26, 2010

F# Computation Expressions, a Simple Bind/Return Mnemonic

I’ve gotten to the point where I can usually puzzle out what I need in a computation expression while I’m sitting at the computer. However, I can still have some trouble visualizing the process when I’m thinking “offline” (e.g. while in line at the grocery store, cleaning the rabbit’s litter pan, etc.). So I’m trying to come up with some useful patterns that I can keep in my head for such occasions.

Below is one I’ve come up with for basic Bind/Return operation. It’s just a simple computation expression that increments a different column of a base ten number depending on the operation. What are important to me are the names I came up with for the variables. I don’t claim that they are either academically correct or that they are the best possible names, but they are ones that make sense to me.

At the bottom of this post is my pattern. (As always, it is presented "as-is" and without warranty or implied fitness of any kind; use at your own risk.)

The Bind operation takes two parameters: “rhs” and “theRest.” “rhs” is the result from evaluating the right hand side of the let! assignment. In the example below, that’s the return from calling “add1.” “theRest” represents the remainder of the computation. As you can see, “rhs” (or in this case “rhs” after having 10 added to it), get passed down the line as an “assignedDownX” to “theRest.” When that call returns, the result gets “returnedUp.”

The Return operation takes a single parameter, “assignedDown.” For example purposes, this has 100 added to it. The result is then “returnedUp.”

The computation expression example shows the whole thing put together.

As I said, the names may not be ideal; they represent a particularly mechanical way of thinking about what is happening. In a context where more sophisticated computation is taking place, conceptual labels will be more meaningful. But I generally find it easier to remember the mechanical and work towards the conceptual, rather than vice versa. Perhaps I’m peculiar in that respect; I couldn’t really say for certain, lol.

Still, it's amazing how simple Bind/Return looks when sticking to the mechanics in light of the power that can be leveraged by augmenting those basic mechanics.

 
/// Add 1.
let add1 i = i+1
 
 
/// Adds a 10 on Bind, 
/// or a 100 on Return.
type BindAdd10ReturnAdd100 () =
 
  /// Add 10.
  member this.Bind (rhs,theRest) =
    let assignedDown = rhs+10
    let returnedUp = theRest assignedDown
    returnedUp
 
  /// Add 100.
  member this.Return assignedDown =
    let returnedUp = assignedDown+100
    returnedUp
 
 
// Test it out.
let result = BindAdd10ReturnAdd100() {
  let! assignedDownA = add1 0
  let! assignedDownB = add1 assignedDownA
  let! assignedDownC = add1 assignedDownB
  return assignedDownC
}
 
 
printfn "Your breakpoint here."

No comments: