Saturday, January 10, 2009

More Curry?

Sorry, I just can’t resist :P

image

I read most of F# for Scientists by Dr Jon Harrop over the Christmas holidays. Now, don’t be put of by the title, it’s a really wonderful little book. I’m no scientist, my undergraduate degree was a general social science pick and mix affair, but I found most of it straightforward. I had to skip some of the complex mathematics but that didn’t seem to hurt my appreciation of programming principles being discussed.

It’s really nice to find a small programming book. Far too many assume too little intelligence from the reader and waffle at length on trivial subjects. It doesn’t help that the IT profession seems to value its books by the killogram. Dr Harrop doesn’t suffer from either of these traits and is happy to introduce difficult subjects in a concise and direct style. Now that means that I sometimes had to spend a while on each page to make sure I understood it, but that’s far better than reading page after page of whatever for dummies.

If I’ve taken anything from this book, it’s a much better understanding of currying. I talked about it a while back when discussing an excellent presentation of functional C# by Oliver Sturm, but at that time I hadn’t understood how central it was to understanding F#.

I’m going to try to show how currying is built into F# as a core part of the language, and how what at first appears to be imperative style syntax is in fact very different.

The first thing to note about F# is that every function only has one argument and one return value. When you write a function that looks like it’s got several arguments, what you are actually creating is a curried set of functions. Take a simple add function:

let add a b = a + b

Now, when I first saw this syntax, I thought, OK add takes two arguments, a and b, and adds them with an implicit return value. But when we run this assignment in F# interactive we get this:

val add : int -> int –> int

This is telling us that we have a function that takes an int and returns a function. the function it returns takes and int and returns an int.

If we were to write this in C# it would look like this

Func<int, Func<int, int>> add = x => y => x + y;

Now if we write:

let add5 = add 5

We get:

val add5 : (int -> int)

In C# we would write this:

var add5 = add(5);

It makes sense that we are using our function that takes and int and returns a function. The new function is called add5, it takes and int as its argument and returns an int. We can use this function like this:

add5 2

which gives:

val it : int = 7

If we write:

add 5 2

We’re actually writing the same as above but on one line. In C# it would look like this:

add(5)(2)

F# is built from the ground up to leverage currying. This is very powerful, but you have to get it, or it becomes very confusing.

But why is it useful? I spend a lot of time talking about Dependency Injection as a way of creating component oriented software. In our object oriented C# world, dependency injection means that we can write generic high level classes that encapsulate the orchestration of lower level ones and defer their concrete resolution until runtime. Currying is a way of doing this at a functional level. By factoring out higher level functions that take lower level functions as arguments we can reuse those patterns of higher level orchestration. Think of it as functional dependency injection.

No comments:

Post a Comment

Note: only a member of this blog may post a comment.