Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Forgive my ignorance, I'd would like an example of the "functional" (hehe) benefits of automatic currying


Partial application and function composition are the meat-and-potatoes of functional programming.

Automatic currying is syntactic sugar that can make these two easier and clearer as it gets rid of a bunch of named arguments and lambdas. For example, I can write:

  foo a b c = a * b + c  
I can then apply this partially like this (foo2 just takes argument c):

  foo2 = foo 10 20  
and compose it with other functions, for example like this:

  composed = foo2 >> bar >> baz
Without currying, you'd have something like:

  foo (a, b, c) = a * b + c 

  foo2 (z) = foo (10, 20, z)

  composed (a) = (\b -> foo2 (b)) ((\c -> bar (c)) (baz (a)))


I find this phrase "Automatic Currying" that people are using in this thread to be very strange.

It implies that `(add 3)` is being magically converted into `(\x -> (add 3 x))` by some fancy front-end feature.

But no, `add` is just a function that returns a function.

This pattern is core to the entire paradigm of functional programming, all the way down to the lambda calculus.

All functional programming languages are essentially just fancy syntax around lambda calculus. They have different implementation strategies (strict vs lazy), different type systems, and they have been extended with different primitives (floats, operations on floats, etc).

But at their core, they are all just lambda calculus.

In the lambda calculus, there is ONLY functions, and all functions take one argument. There are no tuples. So, multiple arguments are implemented with the pattern in question. For example:

mul = (λx. λy. λz. x(yz))


Before I start, let me say that I'm on mobile and I don't know how to post code on this site. I'm assuming backticks work.

---

One benefit is that functions are more reusable. Functions with more arguments can be partially applied and used where applicable.

For instance, if you wanted to increment the values in a list you might do something like this in python:

  map(lambda x: x + 1, [1,2,3])
But in an ML like language you can do something like:

  map((+ 1), [1,2,3])
Because plus is actually a function that takes two arguments and can be curried down to one argument.

Additionally, you can think of currying as a form of dependency injection.

For example, if you wanted to inject a database client and a logger before running a query you might do something like this:

  def queryer(db, logger):
    def _q(query):
      logger.info("before query")
      results = db.fetch(query)
      logger.info("after query")
      return results

    return _q

  rows = queryer(db, logger)(query)

You could write it a bit more simply in this hypothetical ML like language:

  queryer db logger query =
      logger.info("before query")
      results = db.fetch(query)
      logger.info("after query")
      results
MLs don't require parens/commas for function calls, but you can use parens to force a particular execution order. The lines below have the same result, but some produce and execute intermediate curried functions

  rows = queryer db logger query
  rows = (queryer db) logger query
  rows = ((queryer db) logger) query
It's a bit contrived, but you have the flexibility to provide some arguments based on the call site rather than the definition site. I.e., the first example was broken up into 2 function calls at the definition site, but the second example could have 1, 2, or 3 function calls.

---

I hope this makes sense and I wish I could preview my comment to see if it was formatted reasonably!




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: