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 Mon­ads, which many con­sid­er to be a scary word… That said, I know a few smart peo­ple who have done excel­lent talks to explain Mon­ads in a way that’s easy to under­stand, check out these links:

and this post is prob­a­bly the best of the lot, espe­cial­ly if you enjoy car­toon draw­ings!

 

Constraints

  • Exis­tence of an abstrac­tion to which val­ues can be con­vert­ed
  • This abstrac­tion pro­vides oper­a­tions to:
    1. wrap around val­ues, so that they become the abstrac­tion
    2. bind itself to func­tions, so to estab­lish sequences of func­tions
    3. unwrap the val­ue, to exam­ine the final result
  • Larg­er prob­lem is solved as a pipeline of func­tions bound togeth­er, with unwrap­ping hap­pen­ing at the end
  • Par­tic­u­lar­ly for The One style, the bind oper­a­tion sim­ply calls the giv­en func­tion, giv­ing it the val­ue that it holds, and holds on to the returned val­ue

 

Version 1 (simple bind)

In terms of a port from Crista’s exam­ple, most of code is very sim­i­lar to the Pipeline style, except their return val­ues have to be wrapped into anoth­er abstrac­tion.

I thought about this and couldn’t come up with a real­ly mean­ing­ful abstrac­tion, so I set­tled on a sin­gle-case Result type:

image

Next, we need to define the ‘bind’ oper­a­tor (which, the con­ven­tion is to use »=):

image

the rest is pret­ty straight for­ward (i.e. copy from Pipeline style, add |> Result to end of each func­tion):

image

Before we move on though, I wan­na bring your atten­tion to the removeStop­Words func­tion briefly:

image

If you recall, read­File 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 sin­gle-cased, we can kin­da cheat by using pat­tern match­ing to extract the wrapped string val­ue out and bind it to the raw val­ue:

image

 

Now, we want to chain the func­tions togeth­er using our bind:

image

Oh no! What’s hap­pen­ing here?!?

Ah, the com­pil­er is telling us that ‘bind’ is expect­ing a con­tin­u­a­tion that returns a val­ue wrapped in the Result type but printMe returns unit instead.

That’s a bum­mer, so we can either rewrite printMe to be com­pli­ant of such require­ment. Or, we can use anoth­er con­cept that’s com­mon­ly used as a ‘lift’:

image

A lift is sim­ply a func­tion that takes a func­tion that returns an unwrapped val­ue, and returns a mod­i­fied ver­sion that returns a wrapped val­ue instead.

image

So with a tiny change, we can make every­thing work now:image

 

But but but, this is F#, and we have com­pu­ta­tion expres­sions, so we can do bet­ter than that!

(if you’re total­ly new to the idea of com­pu­ta­tion expres­sions, then I rec­om­mend read­ing at least the first few posts in Scott’s excel­lent series before pro­ceed­ing)

 

Version 2 (computation expressions)

Have you read Scott’s intro­duc­to­ry posts on com­pu­ta­tion expres­sions? Have a basic under­stand­ing of how they work?

Good, then let’s con­tin­ue.

 

Here is a very sim­ple CE that works with the Result type:

image

and now we can use it to refac­tor the removeStop­Words func­tion:

image

Here, return! is trans­lat­ed to the Return­From method in TheOneB­uilder and unwraps the Result<string> for us so we don’t need to man­u­al­ly unwrap it with pat­tern match­ing.

 

To chain the func­tions togeth­er, we no longer have to use bind and can instead let the CE deal with unwrap­ping val­ues for us:

image

Also, notice that we no longer have to ‘lift’ the printMe func­tion.


aside : you might also noticed that I’m shad­ow­ing pre­vi­ous instances of text, words and word­Fre­qs as I go. It’s not nec­es­sary, and many peo­ple would have pre­ferred text’, text’’, etc. instead.

I think in this par­tic­u­lar case shad­ow­ing actu­al­ly helps me pre­vent the acci­den­tal mis­use of dis­card­ed val­ues. E.g. the fol­low­ing would be a bug (and I’ve made this type of mis­takes in the past..)

theOne {

    let! text = read­File ”p & p”

    let! text’ = fil­ter­Chars text

    let! text’’ = nor­mal­ize text  // should be text’ instead

    …

}


That said, I per­son­al­ly think »= is still a very use­ful com­bi­na­tor, and would pre­fer to write the above as:

image

 

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