Currying functions

Haskell allows for both curried and uncurried functions:

greetCurried :: String -> String -> String
greetCurried title name
  = "Greetings " ++ title ++ " " ++ name

greetUncurried :: (String, String) -> String
greetUncurried (title, name)
  = "Greetings " ++ title ++ " " ++ name

Let's suppose that we need a function with the first argument fixed:

greetCurried' :: String -> String
greetCurried' = greetCurried "Ms"

greetUncurried' :: String -> String
greetUncurried' name = greetUncurried ("Ms", name)

In both cases, we have applied one of the arguments and thereby specialized our original function. For the uncurried function we needed to mention all parameters in the reshaped function, while for the curried one we could just ignore subsequent arguments.

Since it is fairly easy to translate a curried function to an uncurried function (and vice versa) the question arises: why and when would one want to use uncurried functions?

Currying and composability

Consider a function that returns a few pieces of data, which we choose to express as a tuple:

g n = (n^2, n^3)

Then suppose we want to find the maximum value in that tuple:

max (g 11)

This would not work because the max function is curried, but we can easily align the types by uncurrying:

uncurry max (g 11)

Whenever we have a function returning a tuple and we want to consume that tuple from a curried function, we need to uncurry that function. Alternatively, if we are writing a function to consume an output tuple from another function, we might choose to write our function in uncurried (tuple arguments) form so that we don't have to later uncurry our function or unpack the tuple.

It is idiomatic in Haskell to curry by default. There is a very important reason for this. Thanks to currying, we can do this:

map (map square) [[1], [2,2], [3,3,3]]

We cannot, however, do this:

let map' = uncurry map
map' (map' square) [[1], [2,2], [3,3,3]]

We need to explicitly curry map' in order to compose it with other functions:

(curry map') (curry map' square) [[1], [2,2], [3,3,3]]

Curried functions are composable, whereas uncurried functions are not.

Decoupling with currying

If we can apply one function argument at a time, nothing stops us from doing so at entirely different places in our codebase. For instance, we might "wire in" some authentication-related information into our function at one end of the codebase and use the specialized function in another part of the codebase that has no cognizance of the authentication argument and its related types.

This can be a powerful tool for decoupling, the site of decoupling being the function argument list!

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset