This is interesting, but just because you can do something doesn't mean you should, particularly in JavaScript. As soon as I started working on a large project with a team of devs my coding style began to change dramatically. Simplicity and readability become paramount. Minimize how much a dev needs to understand about the code before they can work on it, and how long it takes them to read it. With the right coding conventions, anyone can start working within seconds just by seeing the names of the functions and variables.
The mental gymnastics required to read these functions and understand how they work is not negligible, it adds up. Especially when the function definition might be in another file from the one you are working on.
If any of my co-workers started actually using these techniques in our code, I would flag it. Usually, if you find yourself in a situation where it is beneficial to use techniques like this, it's a sign that you might be able to refactor your entire approach to make the design simpler.
> The mental gymnastics required to read these functions and understand how they work is not negligible, it adds up.
While I agree with avoiding unnecessary complication, partial application is no more complicated than object construction, something you've presumably been doing all your life. Every time you create an object whose only initialization logic is to set some internal variables, you are effectively partially applying a function whose arguments are (0) the constructor's arguments, and (1) a method selector. No mental gymnastics are required to understand this. (And this doesn't take into account open recursion, which makes object construction more complicated in ways that mere partial application isn't.)
That's a fair point, currying is the main offender here, partial application is just making a new function. With the right name it's not harder to understand. It does add complexity because you have layers of dependencies instead of everything being in the same place. The example is always some simple math, but usually functions are doing more, and when they get changed it isn't always obvious that the things that rely on partial applications of those functions are also affected.
But that is true of anything that uses any dependencies, and a fundamental part of how we usually code. I prefer to reasonably minimize the number of dependencies, and then of course good tests protect you against this problem.
Well in Haskell it all makes sense, since you have other powerful tools (function composition and application, very large standard library of small functions, etc) that all compose into readable and nice code.
If you just use currying randomly, it's going to create more headaches than solve. Ultimately you'd need to adopt something like Ramda in your project if you want to do this in your JS codebase, but even then I think Ramda-dependent code is way less readable than Haskell.
Just to be clear, I don't advocate currying everywhere. To me, the distinction between uncurried and curried functions lies primarily in the kinds of usage they suggest:
(0) Uncurried functions are easier to pass as arguments to higher-order functions, because the author of the latter doesn't have to anticipate the arity of the former.
(1) Curried functions are easier to partially apply. This is particularly useful for collection-traversing higher-order functions (map, filter, reduce, etc.).
Furthermore, functions of three or more arguments can be “partially curried” in various ways, and the optimal choice ultimately depends on how you intend to use them.
That being said, when in doubt, I tend to go for uncurried.
My main objection with currying, and partial application as it's presented, is the implicit argument ordering that's imposed.
I can't tell you the number of bugs I've seen that were caused by out-of-order arguments, even without currying/PA. I would never use currying like this in any non-trivial codebase, because it's very easy to make ordering mistakes, even in statically typed languages (if multiple arguments are the same type, your compiler may not protect you).
I could accept using partial application if the arguments were required to be explicitly named and/or they all had distinct types. And at that point, the OOP line blurs even more.
But isn't that true for functions in general? types may help you mitigating some of those bugs, but the real way to solve the issue is with good documentation: docstrings and well-named parameters is the way to go.
Yes it is also true for functions in general, which is why you shouldn't have functions with lots of arguments (and especially lots of arguments with the same type).
Currying exacerbates the problem because the function application doesn't occur in one place--not only do you have to get the ordering right, but the order in which the function is applied is scattered across your code and depends on the control flow.
It's a neat trick, but most of the time it significantly reduces the clarity and maintainability of a program.
With all due respect, I don't think you understand the purpose of currying and partial application. In your defense, the article merely explains what currying is, but does not explain why you need it.
For instance while writing functional Javascript code, I never curry my methods, instead I use helper methods from functional libraries such as lodash or Ramda to curry.
Here is a video which explains proper uses of currying (and does it very well) [1]. Here is another article if your coworker start using functional programming and you want to stop them [2].
Ok +1 for the super cynical article about how to stop functional programming :D
But obviously that's a cheap shot for an example, no one would have a problem with the function he starts out with, he doesn't need to have a side effect. The problem is when you are in an OOP app, with an OOP language, doing things that really lend themselves to OOP, and trying to shoehorn everything into FP because it's How Smart People Program. And the result is not less complicated, but way more complicated.
I've got these opinions because I've already been that guy saying please stop writing this complicated stuff that I don't understand. I'm not an idiot, we've just got a huge amount of work to do, and the time it takes for me to piece all this together and find the bugs is not worth it, especially because it is going to be just as opaque to you when you have to come back to it in a few months.
I'm not saying FP isn't the best thing ever and someday I'm sure it's going to take over the world, and given all the time in the world and a clean sheet I might choose it myself right now. But I care about working as a team more than the finer points of coding philosophy, and making simple, effective, and on time code. I'll choose practical over principle if I have to, though I'd prefer to have both. And if that means FP, then let's do it, but so far my experience has been the opposite.
For context, we are talking about JavaScript apps, and my comments are limited to front end apps using OOP frameworks and fetching data in the form of objects from a REST API.
I used to be the guy who would say that people need to stop writing code which others can't understand, but then I migrated to, "why am I working at a company where I need to lower the bar so much that making changes in a code you understand well takes ages".
I joined that school when I used FP for my own project (I use Elm, and RamdaJS+JS) and found how amazingly handling bugs and complicated logic became. This clearly came with a higher barrier to entry for other people to understand my code (but this cost only needs to be paid once, I look at other people's functional code and I got no problem understanding it btw).
I understand large companies where logic needs to be written so simply so that anyone can understand it (Python philosophy "Code is read a lot more times than it is written"), but they don't end up with fewer bugs, it's just a LOT LOT BIGGER codebase, with 5 million for loops, doing 1500 things in one for loop.
In fact, before I fully went to FP side, I started writing my JS code without using for loops or forEach. Even this simple change of writing code using map/reduce/filter/drop/reject/take etc made code lot simpler to maintain, reason with, and eliminate bugs.
In my experience, there is almost never a need for creating a loop on an array purely for the sake of side effect.
For instance, if you want to write something on the log, then loop and collect everything into a single array (using map) and then perform the side effect with a single command (which is a lot more performant in most cases).
For asynchronous side effects, create an array of promises using a map, and then run a Promise.all() on that array if you need to resolve all of them.
But again, if your experience is different or in your requirements, it is not possible to create a map/reduce/filter transoformation for side effects, then I'd love to hear about it.
The point-free style is harder to read and reason about imo, especially since the data gets transformed in that pipeline. We use scala, and will avoid currying for this reason.
Currying in JavaScript is really nice if you can remember the arities of each curried function you're using. If you forget to call up to the last argument, you'll be passing a curried function instead of the expected value, and it can throw an error really far away from the bug. And instead of normal data to inspect at the error site to point you to the bad call, you'll only have a generic function name to look at. I've spent more hours than I'd like to admit debugging these situations.
A type system would detect these cases, but TypeScript and Flow don't have great support for curried functions. Typing them is very verbose.
I think this exact problem is what makes currying in JS and dynamically typed languages not very practical.
In JS is even worse because of the weak typing, if by mistake you get a curried function instead of a value, JS will happily operate on it giving you things like "hellofunction () {}". When you realize that there is a problem, it would be hard to find the actual cause of the error.
I think this is the reason why dynamic functional languages such as Clojure don't use auto-currying. I a static typed world, like Haskell's this is not a problem at all.
Partial application on the other hand is very useful and practical in JS and other dynamic languages.
The type of a curried function in Typescript is just something like:
(a: number) => (b: number) => (c: number) => number
Sure, the parameter names and parentheses are a bit annoying, but I wouldn't call that "very verbose". Comparable concepts in C++ or Java would be a nightmare to type out.
Not only do you need to remember the arity, but the order as well, which is IMO worse (at least a type system can protect you from arity mistakes). If your function is not commutative for adjacent arguments of the same type, you're skipping through a minefield if you rely on currying.
Conceptually it's not that different. Practically, it's pretty different because you're effectively fragmenting a single function application across your codebase, making it harder to reason about and more likely to make ordering mistakes.
In what way is it not simple? It's hard to tell what you mean. The syntax is actually a bit of a nightmare if you are writing a parser, but from the perspective of a human reader, it shows you exactly what's going on under the hood.
Hmm... One way that I find easier to understand partial application is to imagine that it's an object. All of the bound variables are like instance variables in the object.
Imagine that you have a collection of items that you want to map over. Unfortunately the function you want to run takes 2 parameters instead of one. What if that function was really an object with one instance variable and a method that took 1 variable. So something like this (if it formats correctly):
class Person {
constructor(name) { this.name = name; }
is(adj) { console.log(this.name + ' is ' + adj); }
};
const mike = new Person('Mike');
['smart', 'handsome'].forEach((adj) => mike.is(adj));
I think most people from an object oriented background will find this easy to understand. Instead of making an object, though, we can just make a function that binds the value in conceptually the same way:
const is = name => adj => {
console.log(name + " is " + adj);
}
const mikeIs = is('Mike');
['smart', 'handsome'].forEach(mikeIs);
Note: I don't have to make a lambda in the second case because I don't have to trip over `this` pointers, but I could if I wanted:
Now, imagine that you have a bunch of curried functions, whose first argument is "name". Let's say "is", "jumps", "eats". You could write a function like this:
const Person = (name) => {
return { is: is(name), jumps: jumps(name), eats: eats(name) };
};
This is literally a class (minus the crazy `this` pointers).
This is one of the reasons, I really like Javascript. :-)
One major difference though, objects with multiple fields will require some sophisticated logic to ensure correct order of initialization (depending on the way constructors and setters are done). A partial function enforces some order. You can accumulate safely information and get closer to a usable and valid final piece of logic.
It's less easy when you need choice although one can use subfunctions .
In OOP you may have fluent interfaces, builder things but it's.. once again a lot for not much more.
Ok, it might be too early in the morning, but I still don't understand this. I read that post, and the post by 'raganwald that the former linked to, and I still don't see any difference between currying and partial application. And yet both posts seem to imply (without showing in any way) that the two concepts are different. How come?
That's not what currying is. You've just used partial application in two different contexts.
Currying takes a multi-argument function:
const f = (x, y, z) => x + y * z;
...and converts it to a function that takes a single argument, and returns a function taking the next argument, recursively:
const f_curried = x => y => z => x + y * z;
const result = f_curried(1)(2)(3)
edit: maybe you were trying to convey that, in your first example, f is already a curried function? As a whole, the first example just looks like a partial-application demonstration.
Basically a curried function could be described nested series of partially applied functions(one argument per step).
On a side note to me this article is a bit on the heavy side when it comes to conveying the message. For one thing partial application should be explained first.
Partial application (passing too few arguments to a function) is something that a programmer does. Function currying is something that a programming language does in response to partial application. It's basically wrapping the partially applied function in another one that expects the missing arguments and then performs the originally intended computation.
Currying is something the programmer has to do when defining a function - well before partial application happens at all! More precisely, the programmer has to choose between:
Partial application introduces one new closure, to capture some arguments now and allow the rest to be supplied later:
f = function (x, y, z) { return [x, y, z]; };
g = partiallyApply(f, 1, 2);
With these definitions (and some suitable definition of `partialApply`):
g(3) == f(1, 2, 3) == [1, 2, 3]
Note that `g` is a closure: a function with (references to) some values: the function `f`, and the data `x = 1` and `y = 2`. This closure will accept some argument (`z`) and return `f(x, y, z)`.
Whilst currying is similar, it's like partially-applying everything, and doing so implicitly. For example, given `h = curry(f)` we get a new function: a closure with a reference to `f`.
If we say `i = h(1)`, we get a new closure which references `f` and `x = 1`. We can say `j = i(2)` to get another new closure which references `f`, `x = 1` and `y = 2`. All of the following hold:
The important thing to notice is that in the case of partial-application, we only got one extra closure, and that happened when we explicitly called the `partialApply` function.
In the case of currying, we only called the `curry` function once, to get `h`. Yet we subsequently get more and more closures, without any extra calls to the `curry` function: `h` can return new closures, and those closures can return new closures (like `i` and `j`) and so on.
In a language with strict (call by value) semantics like Javascript, this makes it hard to know when the "real" function (in our case `f`) will actually get called. This is important if `f` has side-effects, or involves some heavy computation. It also makes it harder to know when values will be garbage collected, due to the references inside these closures.
In the case of `partialApply`, only one closure is created, we know exactly where (at the point where `partialApply` is called), we know exactly what references will be captured (the arguments to `partialApply`) and we know exactly when the "real" function will get called (it will be called whenever the resulting closure is called).
To add to all of the other references in this thread, I also wrote my own a few years ago :)
Here's a basic implementation of currying in Javascript:
a = curry(function(w, x) {
return function(y) {
return function(z) {
return w + x + y + z;
};
};
});
The "basic" version of `curry`, which is what I've seen most tutorials use, handles situations with "too few" arguments, by returning a closure:
a(1)(2)(3)(4) == a(1, 2)(3)(4) == 1 + 2 + 3 + 4
In the "improved" version we also handle situations with "too many" arguments, by passing them as arguments to the return value (of course, this assumes we're returning functions, like above); which makes all of the following equivalent:
It surprises me to no end that programming languages that promote a functional style with higher order functions do not support partial application as a part of the standard library. It covers 95% of the uses of curried procedures and gives you compiler warnings if you pass too few or too many arguments.
In scheme we have the cut macro that all self-respecting implementations provide which guarantees you zero runtime overhead.
It's a right curry (which I can reapply for N args). It's just personal preference, but I like to write "function templates" and then "particularize" them with a curry, from right to left. The direction is unusual but it's a personal choice and arbitrary.
It's not Haskell-specific terminology, I've seen it used in other functional language contexts (some Scheme libraries, etc.). But I admit I couldn't find another reference to link to the term, at least not with a quick Web search. :)
(It's still not currying, though! If you're providing partial arguments to an existing function, that's partial application; if you're transforming a multi-arg function into a single-arg, function-returning function, then it's currying!)
I see the complaint over abusing currying, currying comes from a world where things are reduced (npi) into binary and unary operations as much as possible. I don't think FP/ML guys would like having wild arguments counts too.
The mental gymnastics required to read these functions and understand how they work is not negligible, it adds up. Especially when the function definition might be in another file from the one you are working on.
If any of my co-workers started actually using these techniques in our code, I would flag it. Usually, if you find yourself in a situation where it is beneficial to use techniques like this, it's a sign that you might be able to refactor your entire approach to make the design simpler.