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) a 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 you 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 max
value 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, as you will see with this example. 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!