5 ms·
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
by phyller 9y ago
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.
- catnaroek 9y ago> 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.)
- phyller 9y agoThat'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.
- deleted 9y ago[deleted]
- catnaroek 9y agoJust 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.
- marcosdumay 9y ago> The example is always some simple math, but usually functions are doing more On a language like Haskell, 90% of the time you see partial application it is something like `(+1)`, or `inClass isSpace`. That insistence on functions doing more is a very OOP thing. Functions that do too much do not compose well.
- emerongi 9y agoWell 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.
- cle 9y agoMy 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.
- pera 9y agoBut 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.
- cle 9y agoYes 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.
- splintercell 9y agoWith 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. Where currying is useful is in cases like this: ```javascript //Definitions const filterUsers = (fieldName, value, users) => //Implementation const sortUsersBy = (fieldName, users) => //Implementation const takeUsers = (count, users) => //Implementation //Without currying UserService .getUsers() .then(users => filterUsers('active', true, users)) .then(users => sortUsersBy('age', users)) .then(users => takeUsers(3, users)); //If these methods were curried UserService .getUsers() .then(filterUsers('active', true)) .then(sortUsersBy('age')) .then(takeUsers(3)); ``` 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]. 1. https://www.youtube.com/watch?v=m3svKOdZijA https://www.youtube.com/watch?v=m3svKOdZijA 2. https://brianmckenna.org/blog/howtostopfp https://brianmckenna.org/blog/howtostopfp
- ebola1717 9y agoThe 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.
- phyller 9y agoOk +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.