Exercises in Programming Style–The One

NOTE : read the rest of the series, or check out the source code.

If you enjoy read­ing these exer­cises then please buy Crista’s book to sup­port her work.

exercises-prog-styles-cover

Fol­low­ing on from the last post, we will look at The One style today.

 

Style 9 – The One

You may also know this style as Monads, which many consider to be a scary word… That said, I know a few smart people who have done excellent talks to explain Monads in a way that’s easy to understand, check out these links:

and this post is probably the best of the lot, especially if you enjoy cartoon drawings!

 

Constraints

  • Existence of an abstraction to which values can be converted
  • This abstraction provides operations to:
    1. wrap around values, so that they become the abstraction
    2. bind itself to functions, so to establish sequences of functions
    3. unwrap the value, to examine the final result
  • Larger problem is solved as a pipeline of functions bound together, with unwrapping happening at the end
  • Particularly for The One style, the bind operation simply calls the given function, giving it the value that it holds, and holds on to the returned value

 

Version 1 (simple bind)

In terms of a port from Crista’s example, most of code is very similar to the Pipeline style, except their return values have to be wrapped into another abstraction.

I thought about this and couldn’t come up with a really meaningful abstraction, so I settled on a single-case Result type:

image

Next, we need to define the ‘bind’ operator (which, the convention is to use >>=):

image

the rest is pretty straight forward (i.e. copy from Pipeline style, add |> Result to end of each function):

image

Before we move on though, I wanna bring your attention to the removeStopWords function briefly:

image

If you recall, readFile now returns a Result<string> so in order to split the wrapped string we’ll need to unbox it first. Since our Result type is single-cased, we can kinda cheat by using pattern matching to extract the wrapped string value out and bind it to the raw value:

image

 

Now, we want to chain the functions together using our bind:

image

Oh no! What’s happening here?!?

Ah, the compiler is telling us that ‘bind’ is expecting a continuation that returns a value wrapped in the Result type but printMe returns unit instead.

That’s a bummer, so we can either rewrite printMe to be compliant of such requirement. Or, we can use another concept that’s commonly used as a ‘lift’:

image

A lift is simply a function that takes a function that returns an unwrapped value, and returns a modified version that returns a wrapped value instead.

image

So with a tiny change, we can make everything work now:image

 

But but but, this is F#, and we have computation expressions, so we can do better than that!

(if you’re totally new to the idea of computation expressions, then I recommend reading at least the first few posts in Scott’s excellent series before proceeding)

 

Version 2 (computation expressions)

Have you read Scott’s introductory posts on computation expressions? Have a basic understanding of how they work?

Good, then let’s continue.

 

Here is a very simple CE that works with the Result type:

image

and now we can use it to refactor the removeStopWords function:

image

Here, return! is translated to the ReturnFrom method in TheOneBuilder and unwraps the Result<string> for us so we don’t need to manually unwrap it with pattern matching.

 

To chain the functions together, we no longer have to use bind and can instead let the CE deal with unwrapping values for us:

image

Also, notice that we no longer have to ‘lift’ the printMe function.


aside : you might also noticed that I’m shadowing previous instances of text, words and wordFreqs as I go. It’s not necessary, and many people would have preferred text’, text’’, etc. instead.

I think in this particular case shadowing actually helps me prevent the accidental misuse of discarded values. E.g. the following would be a bug (and I’ve made this type of mistakes in the past..)

theOne {

    let! text = readFile ”p & p”

    let! text’ = filterChars text

    let! text’’ = normalize text  // should be text’ instead

    …

}


That said, I personally think >>= is still a very useful combinator, and would prefer to write the above as:

image

 

You can find the source code for this exer­cise here (v1) and here (v2).

Leave a Reply

Your email address will not be published.