Functional Fundamentals: Currying
This is the second article on this blog discussing functional programming concepts. Here is a link to the first, Pure v. Impure Functions.
Some time ago, I watched a talk that Scott Wlaschin gave at a conference about functional programming. He said often times people who don’t understand functional programming get scared off by the terminology. Terms such as currying, partial application, functor, monad, etc.
It’s easy to think these are super complex concepts, when in reality these terms are just unfamiliar.
Object oriented programming has similar terminology, encapsulation, polymorphism, inheritance, etc. We’ve just been exposed to those terms so they’ve lost their scariness.
Today lets dive into the topic at hand, currying.
Method Parameters in OO Land
In most OO style programming languages, C#, Java, Python, C/C++, etc. A method has a set number of parameters.
Take this C# method for concating strings.
It takes two strings as input, and returns one string.
Every time you call this method you must provide two parameters. If you don’t, the C# compiler complains.
Function Parameters in Functional Land
On the other hand, in most functional style programming languages, F#, Elm, Haskell, etc. Function parameters are defined and leveraged differently.
Here is how we define a function in Elm that needs two strings as input before it can return the output string.
Notice in Elm the definition of the function is on a separate line than the implementation. A lot of functional languages are like that.
Also notice how the definition is in a different format, “string -> string -> string”. If you’ve never been exposed to a functional language before that might be a bit odd.
Defining a similar function in F# is a bit different.
But the signature is the same, here’s a screenshot of this function in VS Code using the the Ionide plugin.
Ionide adds the (// string -> string -> string) function definition automatically, you’ll notice it’s the same as Elm’s.
Even though this take two strings as input, I can only give it one and the compiler doesn’t have any problem with it.
The signature of concateWordWith is now “string -> string”.
Or I can it two parameters but in two sets of parenthesis.
Why is that?
The reason is because of currying.
Currying is a fundamentally different way of handling functional parameters. In OO languages, it’s all or nothing. If a method has 3 inputs, give me 3 inputs or boom! Compile error.
Curried functions offer more flexibility. They let you build up the input parameters over time. You can give one input now. Then later give it another input. Then finally the last one right before it’s used.
Said another way, If a function has 3 inputs and you only give it one, it will return a function that takes 2 inputs. Then you can give that 2 input function one input and it will return a function that takes 1 input. Then you can give that 1 input function one input and it will finally return the output.
So if you see a function that has a signature of “string -> string -> string”, an OO way to describe it would be “a function that takes two strings as input and returns one string as output”. A more functional way of describing would be “a function that takes a string, that returns an intermediate function that takes a string that returns a string.
Because of currying, there’s really no such thing as a multi parameter function in most functional programming languages. If you’re passing multiple parameters, they’re really being applied one at a time to a sequence of intermediate functions. The compiler does this for you automatically.
In fact Wikipedia defines currying as “…the technique of translating the evaluation of a function that takes multiple arguments into evaluating a sequence of functions, each with a single argument”. So breaking every argument down into it’s own separate function.
Why is Currying Useful
So now that you know what currying is, the next logical question is “is it useful”? Is this just one of those “functional” things that only exist to make life more complicated.
In languages that support currying, currying provides a lot of flexibility with how you call functions.
Take the following example in C# that adds 2 to every number in a list.
Pretty simple code. Here’s how you could do it in F# without taking advantage of currying*.
Fairly straight forward. But since F# curries functions, here’s a more functional way of doing it.
The add function takes two inputs, but in this example we give it one input (2). Then let the List.map provide the second input, one for each element of the list.
Granted, that isn’t a world changing difference. In my day job I write C#, so I deal with non-curried functions all the time. But if you spend enough time in a curried language you begin to miss the little shortcuts and niceties.
*Yes, every function is automatically curried, so not technically a true statement. But different from the next example.
Currying is an important concept to know when working in functional languages. It’s a great language feature that comes in handy all over the place.
Currying is just one of many concepts that I was never exposed to in the OO world. I hope you take some time to learn functional programming and all the unique features it has to offer.