KittyGiraudel / SJSJ

Simplified JavaScript Jargon
http://jargon.js.org
2.27k stars 193 forks source link

Formal definition of currying vs common usage #166

Closed sgtpepper43 closed 8 years ago

sgtpepper43 commented 8 years ago

According to lodah's docs, currying

Creates a function that accepts arguments of func and either invokes func returning its result, if at least arity number of arguments have been provided, or returns a function that accepts the remaining func arguments, and so on.

And in Ramda

Returns a curried equivalent of the provided function. The curried function has two unusual capabilities. First, its arguments needn't be provided one at a time

And wu

Returns a new function that keeps currying until it receives expected arguments, at which point it evaluates fn with those arguments applied.

The mathematical definition (or, at least, the one on wikipedia) and the one currently in the guide is

A function that takes multiple arguments (or a tuple of arguments) into evaluating a sequence of functions, each with a single argument.

I'm not sure how it's done in other languages, but at least in all the javascript libraries a curried function can be called with multiple arguments, whereas the mathematical definition can only be called with one at a time. This type of currying might more rightly be called auto-currying, but I don't really know much about the formal definition anyway, so it might just still be currying. Seeing as how this is a javascript jargon glossary, I think it would make more sense to use the definition of currying as is commonly seen in javascript, even if it's a bit more complicated to explain.

KittyGiraudel commented 8 years ago

I’d like @gwer’s opinion as he wrote the one on currying.

KittyGiraudel commented 8 years ago

Note that I am totally up for a PR expending on the current definition, and basically summing up what you just said. :)

sgtpepper43 commented 8 years ago

I made an issue instead of a PR originally because I didn't want to write so much, but now I realize I probably wrote more just to argue the case than I would have if I'd just made a PR, haha.

KittyGiraudel commented 8 years ago

Haha, classic. I found your concern very insightful anyway, thank you for taking the time. Looking forward to have a more complete and explicit version of this definition soon one way or another. :)

KittyGiraudel commented 8 years ago

Actually currying has been added by @mallowigi, not @gwer. My bad.

gwer commented 8 years ago

Good remark. Other programming languages (functional) have a similar behavior. Currying - is not so much a mathematical transformation as a tool for easy partial application.

fatfisz commented 8 years ago

Let me take some definitions from the wiki of Haskell, a language where every function is curried:

Currying is the process of transforming a function that takes multiple arguments into a function that takes just a single argument and returns another function if any arguments are still needed.

and

Partial application in Haskell involves passing less than the full number of arguments to a function that takes multiple arguments.

So based on this, currying is the act of transforming the function, and partial application is about calling the function. This is also consistent with what I learned at school and with what is written in the Wikipedia article about Currying.

Now, currying changes the original function into a sequence of functions returning functions and so on, with each function accepting only one argument. But because in languages which require function arguments to be surrounded by parentheses (which JS is one of) writing something like magic(1)(2)(3) introduces a lot of noise, the popular JS libraries allow partial application of more than one arguments. This may or may not use currying underneath, so those two should not be confused.

For example:

function makePartiallyApplicable(fn) {
  function getApplier(previousArgs) {
    return function apply(...args) {
      const currentArgs = [].concat(previousArgs, args);

      if (currentArgs.length >= fn.length) {
        return fn(...currentArgs);
      } else {
        return getApplier(currentArgs);
      }
    };
  }

  return getApplier([]);
}

This function returns a new function that allows partial application of an arbitrary number of arguments to fn. However, it doesn't involve actual currying.

Let me provide another example: let's say fn accepts two arguments, fnCurry is a curried version of fn, and fnPartial allows partial application to fn. In this situation:

I know the difference might be almost non-existent at first glance, but it is actually big, so I hope this clears things up.

sgtpepper43 commented 8 years ago

In my mind, while the implementation of it is probably a mix of currying and partial application, fn(1, 2)(3) is just syntactic sugar for fn(1)(2)(3), and considering in Haskell it would be fn 1 2 3, I think the reason curry behaves this way in all the javascript libraries is to maintain that ease of use. While this isn't meant to be a place to document various libraries, I do think including a section on how currying is commonly presented in javascript would still be beneficial.

It's still definitely easier to reason about how currying works when you can just write

const curried = a => b => c => fn(a, b, c);

if this is the place people go when they find currying in javascript code and they don't know what's going on, it'd be good to know the common syntax for using a curried function.

sgtpepper43 commented 8 years ago

It seems it'd be more accurate to call curry in lodash and ramda and wu Haskell-style partial application (ie, normal partial application, but everything is curried, so, the partial application returns a curried function). Or really just a "call this method as if this was haskell" function.

fatfisz commented 8 years ago

In Haskell it's still ((fn 1) 2) 3, it's just that you don't have to use the parentheses :stuck_out_tongue: But the function signature says it all, and it's based on the original meaning of currying.

I think that pointing people to a separate definition of partial application would be the best thing to do here, what do you say?

sgtpepper43 commented 8 years ago

Yeah, I think a good explanation of partial application would be good. Though there's this gray area of "auto-curried partially applied functions" that wouldn't belong in partial application (because of the currying part), and since that's what curry is doing in lodash and what not, I think it still deserves a bit of explanation on the curry page. That's basically what's in my PR, just a small explanation of how curry behaves differently in those libraries, and how they're really using partial application for the most part.