Consider doing a bunch of HTTP requests.
doRequest('POST', 'api.example.com/foos', foos)
doRequest('POST', 'api.examples.com/bars', bars)
You find you'd like to avoid repeating the POST part, or maybe just bring the concept of a POST request as a first-class entity that can be passed around. Thinking in terms of classes and objects, you might be inclined to do this.
class Requester
constructor: (@method) ->
doRequest: (args...) ->
doRequest(@method, args...)
postRequester = new Requester 'POST'
postRequester.doRequest('api.example.com/foos', foos)
This step should be familiar to most, because we do this often in code. It's not just to reduce repetition and stay DRY but also to encode the fact that bits of information may arrive at different stages of processing. You might recognize the concept as dependency injection. In this specific scenario, the part that knows about the endpoint and the data may not want to know about the method being used or that one has been needed at all.
Now consider an alternative. We encoded the two-step process of providing first a method, then the rest of the args as the creation of an intermediary object to store the result of the first step. But do we need such an object, or can we use something more simple?
Look at the definition for Requester
again.
class Requester
constructor: (@method) ->
doRequest: (args...) ->
doRequest(@method, args...)
Hidden there are two functions that combined to exactly what we wished for and nothing else. Let's expose them.
(@method) ->
(args...) ->
doRequest(@method, args...)
This example is in Coffeescript to make it syntactically evident that we can rearrange this bit of code into:
(method) -> (args...) -> doRequest(method, args...)
This is doRequest
, curried on its first argument. You could make use of that as before.
doRequestWithMethod = (method) -> (args...) -> doRequest(method, args...)
doPostRequest = doRequestWithMethod 'POST'
doPostRequest('api.example.com/foos', foos)
Manually currying your functions like this is only slightly less boring than creating intermediary classes to hold early-bound arguments, but opens up a possibility. What if we could assume doRequest
was already curried and that we could partially apply it as is?
doPostRequest = doRequest 'POST'
doPostRequest('api.example.com/foos', foos)
We're achieving the same result as previously with the Requester
, only simpler.
Wait, so what if doRequest
actually isn't curried yet? Well, since all we're doing is putting together functions, we can create one to do the job for us.
curry = (f) -> (arg) -> (args...) -> f(arg, args...)
Observe the similarities between curry
and doRequestWithMethod
above. You'd use it in the same way.
doPostRequest = curry(doRequest)('POST')
doPostRequest('api.example.com/foos', foos)
There we go. In a dash of OO metaphor, partial application is "dependency injection" and currying is the automatic creation of "injectable" things. This should motivate why they're profoundly useful.
What do you think? Does that make sense?