Partial Application vs Parameters - which do you prefer?

:thinking:
To qualify as curried I’d expect:

def get_templates(repo) do
  fn logger ->
    fn pagination ->
      # ...
    end
  end
end

i.e. an arity 1 function returning another arity 1 function (or finally a result)

def get_templates(repo, logger, pagination) do
  # ...
end

def get_templates(repo, logger) do
  fn pagination ->
    get_templates(repo, logger, pagination)
  end
end

Partial application takes a function with n parameters and returns a function with less than n parameters.
get_templates/2 returns an fn/1 function which will execute get_templates/3 once it gets that final argument.
That behaviour in my view at least emulates the intent behind partial application.

Note: the default argument syntax sugar can be a bit misleading as it may not be clear to a newcomer that

def get_templates(repo, logger, pagination \\ [] ) do
  # ...
end

actually generates get_templates/3 and get_templates/2 which are technically 2 distinct functions.

My take is that in a general interface you should stick to exposing plain full arity functions, i.e. there should be no speculative (convenience) partial application/currying going on that might be fashionable elsewhere:

const getTemplates = repo => logger => pagination => {
  // ...
}

However it is common practice to hold and encapsulate dependencies inside a function’s closure when there is a benefit to doing so.

I just miss the clearness of such of style since it is easier to separate the API from the dependency injection portion of it.

This is where I don’t follow:

  • The dependency injection aspect merely requires that a function of a specific arity is provided for later execution.
  • “partial application” only comes into play if we have a function that requires additional dependencies, arguments that we have to bind prior to passing it to another function.
  • So while “partial application” may be a (necessary) preliminary action prior to a particular dependency injection, “partial application” and dependecy injection are entirely separate concerns.

So as user of a function which requires another function to be injected, you explicitly “partially apply” only when circumstances actually demand it.

Shorter functions will tend to relinquish the CPU sooner, while longer functions will tend to hog it. But in the bigger scheme of things I don’t think it is something to worry about because eventually everybody will get another kick at the can.

I will agree however that writing functions speculatively in a curried style is pointless (i.e. leads to needless functions).

2 Likes