I think it is in some way overused in Haskell for example. When your function takes five different parameters, it's unlikely that the curried style really makes sense, other than that it's just convenient and idiomatic in Haskell—in many cases you would be better off using a record type for the parameters.
But if you consider for example the map function, it really makes sense to consider it a higher order function in a "curried" way. That is to say, map f makes sense on its own, because map takes a function and transforms it into a function that works on lists.
So the type
map :: (a -> b) -> ([a] -> [b])
is really nice, and nicer than the uncurried alternative: map :: (a -> b, [a]) -> [b]
Of course they're isomorphic, but the curried one seems to definitely win in terms of elegance, and you avoid ever having to type \x -> map (f, x)
or similar.I think if Haskell had really convenient named parameters, people would use currying less, but we'd still want to use the curried style for many functions that actually make sense as higher-order functions.
The primary advantage of curried functions is that they're trivially partially applicable.
With an uncurried function, if you have a function of two arguments and you want to apply the first but "hold off" on the second you have to wrap the thing in a new function with a single argument which will do the application later (the language may also provide a helper) whereas with curried functions, you can just apply one of the parameters:
let add5 = (a) => a.map((n) => n + 5)
versus let add5 = map (\n -> n + 5)
This means you can very easily create "small specialisations" of very generic functions.Languages with curried functions also tend to provide features like the ability to treat operators as functions ("sections"):
let add5 = map (+5)
and more function composition tooling: let add5ToEven = map (+5) . filter even
though these are not really intrinsic.To be honest, each concept like "partial application", or "applicative functors" deserves an hour-long lecture or more but if the content went that deep I think it'd lose its current audience.
validate :: Regex -> String -> T
validate r s = ...
validateIpAddr :: String -> T
validateIpAddr = validate ipRegex
validateZipCode :: String -> T
validateZipCode = validate zipCodeRegex
And so on and so forth. It can reduce boilerplate, and lets you do stuff like map partially applied functions to functors.
validate(IpAddr, Regex)
to validateIpAddr(Regex)
I have to admit I'm a little fuzzy on why currying is desirable so there's probably something obvious I'm missing. filter (validate ipRegex) listOfStrings
instead of filter validateIpAddr listOfStrings
where validateIpAddr string = validate ipRegex string map { validate(ipRegex, $) } listOfStrings