25 ms·
Currying in JavaScript
- graftak 5y agoCurrying is nice but in practice it’s rather esoteric in the JavaScript ecosystem. I’ve only seen currying being used (if at all) when transforming data with utility libraries like ramda[1]. What makes it worse it that partial application is rather cumbersome to type. Lucky for us there is a partial application proposal, albeit still in stage 1, which makes partial application—to make functions curried—a lot easier[2]. [1] https://ramdajs.com/docs/#curry https://ramdajs.com/docs/#curry [2] https://github.com/tc39/proposal-partial-application https://github.com/tc39/proposal-partial-application
- renke1 5y agoThe partial application proposal looks reasonable and intuitive. Definitely something I would use. Is there any prior art (other languages etc.)? Mh, it's already 4 years old. What a shame.
- weego 5y agoScala has partially applied functions that use a very similar idiom
- alexfoxgill 5y agoIt is used heavily in Scala https://alvinalexander.com/scala/how-to-use-partially-applied-functions-in-scala-syntax-examples/ https://alvinalexander.com/scala/how-to-use-partially-applie...
- masklinn 5y agoClojure has the "thread-as" macro (`as->`), but I don't think it's used much: usually thread-first (`->`) and thread-last (`->>`) will more than do the job and have less mental overhead due to their regularity.
- graftak 5y agoThere’s a lot of bikeschedding on how to use it in conjunction with the (also stage 1 proposal and forever delayed) pipeline operator[1] unfortunately. Add in never ending arguments on which symbol we should use (the questionmark has a lot of responsibilities already) and here we are. To make matters worse it appear that generally OOP style darlings such as static class blocks and true private fields seem to gain more momentum. [1] https://github.com/tc39/proposal-pipeline-operator/wiki https://github.com/tc39/proposal-pipeline-operator/wiki
- eurasiantiger 5y agoNote that currying, especially with Ramda, has a very large negative performance impact. ” To improve the compositionality of our code we had defined a ton of curried functions using Ramda. But every abstraction has a cost associated with it. On profiling, we found out that the `curry` function was taking up a lot of CPU. Benchmarks show that removing curry from the code makes it up to 100 times faster. Curry function is at the core of Ramda, almost every function is curried.” https://blog.dream11engineering.com/lessons-learned-from-running-graphql-at-scale-2ad60b3cefeb https://blog.dream11engineering.com/lessons-learned-from-run...
- IceDane 5y agoWow, that is pretty awful. I'm a pretty big fan of strong, static type systems and FP in general, and I have been writing typescript professionally for a few years now. I decided a long time ago that trying to write Haskell in TS just isn't worth the effort. Adopting FP ideas such as pure functions, restricting side-effects and then otherwise just using the type system to model the flow of your program goes a very long way. You don't have to write Haskell in TS to reap most of the benefits.
- toastal 5y agoYou can also just not use TypeScript if you want something more appropriate for functional programming styles. I know it's not always practical to switch languages, but it's equally impractical to push unergonomic, unidiomatic styles on language not built for it. There are a lot of good options to compile an FP language to JavaScript, and frankly those languages deserve more mindshare than pressing so hard on fp-ts.
- khalilravanna 5y agoWhat’s unergonomic or unidiomatic about TS? As far as I understand it TS is supposed to follow as closely to JS as possible. I always saw it as “let’s add types to JS” rather than a separate language. I’m not sure I’ve seen a “this is how you write TS vs JS” formal declaration from the TS team. Am I missing something?
- runarberg 5y agoI have strong doubts that partial application will ever advance from stage 1. The reason is that partial application was supposed to compliment the pipeline operator[1]. value |> fn(?, "static string") However earlier this month they advanced a version of the pipeline operator with a definite topic marker which makes partial application useless in the RHS of the pipeline operator. This was of course really controversial, as you can see if you read thought the most resent issues (particularly the closed ones). https://github.com/tc39/proposal-pipeline-operator https://github.com/tc39/proposal-pipeline-operator
- leodriesch 5y agoNice explanation, however I don't really see the use case for currying yet. If you want functions with fixed arguments, you could also just create a new function that calls the other with a fixed first argument and just takes the other arguments. Am I missing something?
- goto11 5y agoWhat you describe is currying.
- jhgb 5y agoIsn't that more like partial application?
- schpaencoder 5y agoAt that moment, Anton became enlightened.
- deleted 5y ago[deleted]
- Normal_gaussian 5y agoWith the main benefit of being able to also transform the argument orser or output value at the same time.
- graftak 5y agoThe most common case I’ve seen is when you want to map using 'point free' style functions, i.e. “x.map(transformItem)”[1]. Another (more) common scenario—but not yet relevant in JavaScript—is piping/composing functions[2]. [1] https://en.wikipedia.org/wiki/Tacit_programming https://en.wikipedia.org/wiki/Tacit_programming [2] https://github.com/tc39/proposal-pipeline-operator/wiki https://github.com/tc39/proposal-pipeline-operator/wiki
- ttiurani 5y agoWhat you're missing is that the curry() function is generic: you don't need to manually write that new function, but just type a simple oneliner using curry() to generate the function you're describing.
- jonplackett 5y agoSeems like more trouble than it’s worth
- SPBS 5y agoAny discussion on the utility of currying (i.e. closures) vs constructing an object with methods always reminds me of this parable: ``` The venerable master Qc Na was walking with his student, Anton. Hoping to prompt the master into a discussion, Anton said "Master, I have heard that objects are a very good thing - is this true?" Qc Na looked pityingly at his student and replied, "Foolish pupil - objects are merely a poor man's closures." Chastised, Anton took his leave from his master and returned to his cell, intent on studying closures. He carefully read the entire "Lambda: The Ultimate..." series of papers and its cousins, and implemented a small Scheme interpreter with a closure-based object system. He learned much, and looked forward to informing his master of his progress. On his next walk with Qc Na, Anton attempted to impress his master by saying "Master, I have diligently studied the matter, and now understand that objects are truly a poor man's closures." Qc Na responded by hitting Anton with his stick, saying "When will you learn? Closures are a poor man's object." At that moment, Anton became enlightened. ``` Currying (and closures) are great when you don't want to go through the overhead of constructing an entire object just to hold state. They're lightweight pseudo-objects. But the moment the number of variables you are closing over starts getting too big, it is better to formalize it into an object instead. That's how I interpret this story.
- isaacimagine 5y agoClosures and objects are both ways to pair code and data — I don't think the lesson is entirely about switching to objects when a closure becomes to large; personally, I find it to be an invitation to discover how and why code and data are paired together in the first place. Why do closures and objects arise in such a natural fashion? I'm not entirely sure, and I don't think there's one right answer. All I can think is that I can't imagine the depth of Alan Kay's knowledge on the subject, given he pioneered scheme and smalltalk, two languages that define the core of the closure/object paradigm.
- fendy3002 5y ago> Why do closures and objects arise in such a natural fashion My reasoning is we need a partitioned stateful function which is impossible to achieve and the alternative are worse: - partition global state with your own object id - passing all state as arguments
- goldenkey 5y agoThe implementation in the submission is awfully inelegant, dated, and slow. I quickly wrote up a fast elegant ES2021 solution and tested it thoroughly. Below is the code, feel free to use it: const curry = fn => { return (...args) => { const curried = (...a) => { args = [...args] return args.push(...a) >= fn.length ? fn(...args) : curried } return curried() } }
- dotancohen 5y agoThe topic is not the implementation, but rather the concept. To make the topic accessible to as wide an audience as possible, including old C stogies, the traditional anonymous function is much easier to understand.
- thrttnkrnl 5y agoYour implementation is broken for any function that has more than two arguments. const sum3 = curry(( a, b, c ) => a + b + c ) const sum3_10 = sum3( 10 ) // ... const sum3_10_20 = sum3_10( 20 ) // ... sum3_10_20( 30 ) // 60 sum3_10_20( 5 ) // 60 One of the points of currying is to be able to fork the partial application of the function at any point.
- goldenkey 5y agoVery good catch, thank you. Here is my take on a better version that fixes the issue: const curry = fn => { const curried = (...a) => a.length >= fn.length ? fn(...a) : curried.bind(undefined, ...a) return curried }
- somehnacct3757 5y agoI get the dated claim, but how is yours faster? It's nearly the same logic written with syntax sugar, no? The differences I see are that by using rest params instead of a deferred Array.prototype.concat, you've lifted the Array instance creation into all branches, where the original saves itself the trouble in the invariant case. And yours defines a variable whereas the original didn't. Is there some under the hood reason why this version would be more performant? As for the inelegant claim, beauty is in the eye of the beholder. If performance is not a concern (as it won't be in most currying use-cases) I think the original version is much easier to read without years of js experience. It can be hard for newer or cross-discipline devs to understand mixed usage of rest params and spread operator since they look the same. In a scenario like this I think maintainability is a key ingredient of elegance.
- tomxor 5y agoI've actually grown to dislike currying, it seems like a neat idea when you start using it, but whenever I come across it in real life code it just results in layers and layers of yuk - making me want to scream "show me the fucking code", you can go through 100s of lines before seeing a single operator... I'm not a big fan of OOP either, the older I get the more I end up writing a cross between procedural and functional code with the least possible abstractions in the way - while also trying to find the smallest natural coupling between blocks of code. Patterns are very subjective, and they usually have a place where it's objectively good to use them - but I'm not sure about currying, it feels more like a hack for when you can't do what should be done to the underlying code.
- qsort 5y ago> I've actually grown to dislike currying I believe this is a notational issue more than anything. Fixpoint style in languages like Haskell is very natural and intuitive, if you have to write a `curry` function like the article shows, you have already lost. It's much more convenient to `ad-hoc curry` with things like: const f = x => myFunc(someArg, x, someOtherArg); return myArr.map(f).whateverElse();
- masklinn 5y agoCurrying makes sense if haskell because it’s a curried langage, so you may need the odd uncurry, then curry it back to normal. In uncurried langages, generic curring has few use cases. Partial application does, and some libraries may want to provide both curried and uncurried versions of some things, but generally “curry” itself is unhelpful.
- kortex 5y agoWhat's the difference between currying and partial application/closures?
- masklinn 5y agoCurrying is the conversion of a n-ary function to a chain of n 1-ary functions. So if you have a function `(a, b, c) => d` (a 3-function which returns a result) and you curry it it becomes a => b => c => d (a 1-fn which returns a 1-fn which return a 1-fn which returns a result). Partial application is the application of an n-ary function to k arguments (k <= n, often but not necessarily l < n) without actually evaluating (calling) the function, so given the same `f = (a, b, c) => d`, `partial(f, 1, 2)` returns a function `c => d`. A closure is a function which "closes over" its lexical initialisation context and carries it along. A curried function is trivially partially applicable, just call it with less than `n` parameters and it'll yield a (also curried) function taking `n - k` parameters. It's useful to build a language on that principle, it's not really useful to provide that as a generic utility, because in most scenarios you'd rather just partially apply a function. A closure is a completely freeform tool so it can do more or less anything you want.
- mjbrusso 5y agoIn the `logNow` example, the date argument will be eager evaluated, not lazzy evaluated. Am I right? So what was supposed to be a real use case doesn't seem useful to me.
- fendy3002 5y agoNice catch. Rather than date the author should've pass the DEBUG constant instead. Or a function that return new date, but it'll make things complicated.
- mjbrusso 5y agoHowever , to pass a constant I could use default parameter value.
- fendy3002 5y agoIt's the function definition that's responsible for defining default parameter. It's the function caller that's responsible for that parameter with partial application / currying / closure. It doesn't matter if you develop both, but if you use a library you'll need partial application to define default parameter.
- elboru 5y agoTo me the problem with this kind of explanations is that they miss the “why”. I understand “what” is currying and “how” to implement it. But I’m missing the “why”. The real-world example doesn’t tell me why I would prefer currying instead of putting the date at the end and making it optional, then checking if missing to assign current date. And for the other argument I could create separate functions logDebug, logWarning and logError, those would call the generic function with fixed strings, right?
- ImprobableTruth 5y ago>And for the other argument I could create separate functions logDebug, logWarning and logError, those would call the generic function with fixed strings, right? Sure, but what would you do if e.g. the importance string isn't fixed? Well, you might write a function that takes in the importance string and returns a function that calls the generic function with the dynamic string. That's currying.
- misterpurple45 5y agoWe use currying at work to inject dependencies. E.g. `sendEmail(mockEmailService)(to, subject, body) ` I find it helps to differentiate between side-effecting dependencies and parameters, making code purer, easier to test, more scalable etc.
- kesor 5y agoCringe at the new Date() example ... will logNow() actually create a new Date() each time? no, it will use the one provided in the curry initialization.
- zodiakzz 5y agoThat's already built in to the language: let logNow = log.bind(null, new Date()); // [sic, "now" is fixed] let debugNow = logNow.bind(null, "DEBUG"); Also, uglifiers and transpilers do some horrifying stuff, this won't break if fn.length is tempered with. Unlike the article implementation.
- masklinn 5y agoThat is not currying, it’s partial application.
- q-rews 5y agoCurrying makes it implicit, but it's the same thing. const one = curriedSum(1) one(2) is the same as const one = sum.bind(null, 1) one(2) with the exception that the latter is explicit and not as slow as `curriedSum`. Both `sum` and `curriedSum` can be used the same way: curriedSum(1)(2) sum.bind(null, 1)(2) or just: curriedSum(1, 2) sum(1, 2) One of the advantages is that non-curried functions make the return value explicit. You can't implement `sum(...addends)` with currying because curried functions have a fixed number of parameters. Once past that, a call will not return a function anymore. sum.bind(null, 1).bind(null, 2).bind(null, 3).bind(null, 4) // Still not called curriedSum(1)(2)(3)(4) // Maybe it works, maybe undefined is not a function
- zodiakzz 5y agoThey're technically correct. And if you gotta write on HN, you better get the terminology correct (and that derails threads into interesting tangents often) so my bad. Gotta say currying seems to be useless in real world. No advantage whatsoever over partial application.
- zodiakzz 5y agoAh, TIL. If we are being pedants. Wikipedia says: > currying is the technique of converting a function that takes multiple arguments into a sequence of functions that each takes a *single argument*. So the article isn't currying either. It's a hybrid between currying and partial application. TBH I can't think of a practical case where you'd prefer currying over partial application. Theory/philosophy aside.
- guntars 5y agoA word of caution - using functional programming principles in a language that is not optimized for it can result in awful performance. In JavaScript, whenever you create a closure it’s a heap allocation. If you’re doing that in a tight loop, it’ll completely dominate the time it takes to run your program.
- avinassh 5y agoHow do functional programming languages optimises these?
- oweiler 5y agoI don't know about other languages, but in Kotlin these are normally inlined, which means no allocation.
- chakkepolja 5y agoMost of them, using compilers ('deforestation' optimizations etc..), and of course they also provide escape hatches for tight loops.
- eurasiantiger 5y agoYes. In JS it is best to avoid FP principles such as immutability in hot code paths, the extra allocations and GCs are not worth the cleanliness.
- brundolf 5y agoAre you speaking from experience or speculating? It's intuitive that there would be some overhead, but the amount of overhead could be anywhere from tiny to huge, especially given how aggressive V8 is about optimizing certain things
- guntars 5y agoA bit of both. V8 can and will optimize away closures if they can be inlined or they're unused, but it has the additional wrinkle that it's a JIT compiler and we regularly throw megabytes of compressed Javascript at it all while the user is waiting for something to happen. Not an enviable position for a compiler to be in because all analysis and optimization has to be weighed against any potential payoffs and also why I wouldn't rely on the optimizations like you could in an AOT compiled language like Rust or Haskell. Here are some trivial examples. As you increase the N, the closure containing ones get faster, except the last one (Chrome/M1). I think in real world scenarios the benefits of the optimizations are less clear cut and also the reason why V8 has a reputation for being a bit of a memory hog which is why we should give it a hand and write dumb imperative code. https://perf.link/#eyJpZCI6InN0NmQ4cTRrd20zIiwidGl0bGUiOiJDbG9zdXJlIG9wdGltaXphdGlvbiIsImJlZm9yZSI6ImNvbnN0IE4gPSAxMDA7IC8vIFRyeSBkaWZmZXJlbnQgdmFsdWVzIGhlcmVcbmNvbnN0IGRhdGEgPSBbLi4uQXJyYXkoTikua2V5cygpXVxuZnVuY3Rpb24gcHJvY2Vzcyhmbikge1xuICBkYXRhLmZvckVhY2goZm4pXG59IiwidGVzdHMiOlt7Im5hbWUiOiJUZXN0IENhc2UiLCJjb2RlIjoicHJvY2VzcyhmdW5jdGlvbiAoaSwgaikge1xuICByZXR1cm4gaSAqIDI7XG59KSIsInJ1bnMiOls2MTAwMCwyMDkwMDAsNTgwMDAsMzY3MDAwLDE1NDAwMCw0MTQwMDAsMTUwMDAsMTEzMDAwLDU0MDAwLDQ1MTAwMCwyMjAwMCw0MjUwMDAsNDIwMDAsMTQ1MDAwLDcxMDAwLDQ1MTAwMCw0NDgwMDAsNDUxMDAwLDQ1MTAwMCwxOTAwMCwxNjgwMDAsMzYyMDAwLDEwMDAsNDU2MDAwLDI0NDAwMCwxMTYwMDAsNDc2MDAwLDQ0NDAwMCw0ODkwMDAsMzQxMDAwLDEwODAwMCw0NTEwMDAsNjIwMDAsNDUxMDAwLDIwMTAwMCwyNzUwMDAsMTQ4MDAwLDM0OTAwMCw0OTkwMDAsMzY4MDAwLDQ1MDAwLDQ1MTAwMCw0MzIwMDAsMjgyMDAwLDY2MDAwLDEzMzAwMCw0NTUwMDAsMTYwMDAwLDQ1MTAwMCwzMzAwMCw1NDIwMDAsNTMyMDAwLDUwNzAwMCw0MDYwMDAsMzIxMDAwLDQ1MTAwMCw0NjkwMDAsMjI4MDAwLDQzNTAwMCwxMTcwMDAsNTY5MDAwLDQ1MTAwMCwxMDkwMDAsNDUxMDAwLDM3MTAwMCw1NzUwMDAsMzUxMDAwLDU5NTAwMCwyMzUwMDAsMjcwMDAwLDQ0MDAwLDQ1MTAwMCw4OTAwMCw4NDAwMCw0MzAwMDAsNDUxMDAwLDE3OTAwMCwxNzQwMDAsMjkwMDAsMTU2MDAwLDE1NTAwMCw0NDAwMDAsMjQxMDAwLDIwODAwMCw0OTAwMDAsNDUxMDAwLDMxMDAwLDQ4NTAwMCw0OTAwMDAsNDY3MDAwLDQ0MDAwMCw0NTEwMDAsMzc2MDAwLDQ5NzAwMCw0OTkwMDAsMjY1MDAwLDIzMzAwMCw1MTgwMDAsNTI4MDAwLDI1MDAwXSwib3BzIjozMDMwMDB9LHsibmFtZSI6IlRlc3QgQ2FzZSIsImNvZGUiOiJwcm9jZXNzKGZ1bmN0aW9uIChpLCBqKSB7XG4gIHJldHVybiBmb28oaSk7XG4gIFxuICBmdW5jdGlvbiBmb28oaSkge1xuICAgIHJldHVybiBpICogMjtcbiAgfVxufSkiLCJydW5zIjpbMTE4MDAwLDE0MDAwMCw3MDAwLDY5MDAwLDQwMDAwLDIyNjAwMCwxNDEwMDAsMTAwMCw0OTAwMCw3MjAwMCwxMjcwMDAsMTQzMDAwLDE2MzAwMCwxMDAwMCwxNjUwMDAsMTcwMDAwLDE5MTAwMCwxNDMwMDAsMTI4MDAwLDE2NzAwMCwxMzAwMDAsMjA0MDAwLDIyMDAwMCwxMjIwMDAsODMwMDAsMTgwMDAsMjAzMDAwLDE0NTAwMCwxODUwMDAsMzAwMDAsMzgwMDAsMTYxMDAwLDM1MDAwLDkzMDAwLDg3MDAwLDU5MDAwLDQ2MDAwLDcwMDAwLDIwNjAwMCwxNTEwMDAsMTYwMDAsNTYwMDAsMTU2MDAwLDEyNTAwMCwxNjAwMDAsMTQ1MDAwLDIyMTAwMCwxMzIwMDAsNDkwMDAsNzUwMDAsNTAwMDAsMTk5MDAwLDIxNzAwMCwxMzcwMDAsMTU1MDAwLDE0MzAwMCwxNDMwMDAsMTEwMDAwLDIxOTAwMCwxMDgwMDAsMjI2MDAwLDU5MDAwLDU0MDAwLDY2MDAwLDI4MDAwLDIwMjAwMCwyMDcwMDAsMjIyMDAwLDE4MTAwMCwzOTAwMCw0OTAwMCw4NzAwMCwyMjYwMDAsMjE2MDAwLDkzMDAwLDE5MDAwLDMxMDAwLDk3MDAwLDE3ODAwMCw5OTAwMCw5NDAwMCwxNTAwMDAsODYwMDAsMTQ5MDAwLDM4MDAwLDE3MTAwMCwxMTUwMDAsNDkwMDAsMjI2MDAwLDIyMDAwMCwxOTgwMDAsMTcyMDAwLDEyOTAwMCwzNzAwMCwxNjEwMDAsMTYwMDAwLDEwMDAsMjI2MDAwLDIyNDAwMCwyMTgwMDBdLCJvcHMiOjEyMzc1MH0seyJuYW1lIjoiVGVzdCBDYXNlIiwiY29kZSI6InByb2Nlc3MoZnVuY3Rpb24gKGksIGopIHtcbiAgcmV0dXJuIGZvbygpO1xuICBcbiAgZnVuY3Rpb24gZm9vKCkge1xuICAgIHJldHVybiBpICogMjtcbiAgfVxufSkiLCJydW5zIjpbOTIwMDAsMTAwMCwxNDgwMDAsMjIwMDAsMTAwMCw2MDAwMCwxNjAwMCwxNTEwMDAsMTUyMDAwLDk3MDAwLDE1NDAwMCw0MDAwMCwxNzMwMDAsNDkwMDAsMjMwMDAsNjIwMDAsMTEwMDAwLDYxMDAwLDE1MTAwMCwxMDgwMDAsOTgwMDAsNDcwMDAsMTM1MDAwLDgwMDAsNzYwMDAsNTMwMDAsMTIyMDAwLDUwMDAsMTY1MDAwLDE3NTAwMCwxODAwMCwxMDMwMDAsMTYxMDAwLDEzMDAwLDExODAwMCw3ODAwMCwzNzAwMCwxNTEwMDAsMTEyMDAwLDE5MDAwLDE3OTAwMCw0MjAwMCwxNTEwMDAsNjgwMDAsMTUxMDAwLDEwMjAwMCwxNzUwMDAsODAwMCwxNTEwMDAsNTUwMDAsMjUwMDAsMTUwMDAwLDEzNTAwMCwyMTAwMCw3MjAwMCw5ODAwMCwxNDgwMDAsMTUxMDAwLDE1MTAwMCwxMjQwMDAsNzUwMDAsMTMyMDAwLDEyODAwMCwzMTAwMCwxNTAwMCwxMTcwMDAsMTcyMDAwLDE1MTAwMCwxNjkwMDAsMTUzMDAwLDY3MDAwLDE1MTAwMCwxNTEwMDAsMTUwMDAsMTUxMDAwLDkzMDAwLDY3MDAwLDEzNjAwMCwxNDAwMCwxMTEwMDAsMTAyMDAwLDEzMDAwMCwxMDAwLDcwMDAwLDM2MDAwLDQxMDAwLDU5MDAwLDgwMDAsNDEwMDAsMTI4MDAwLDcyMDAwLDc0MDAwLDE1NzAwMCwxNzEwMDAsMTU1MDAwLDYwMDAwLDMxMDAwLDMxMDAwLDcyMDAwLDE1MTAwMF0sIm9wcyI6OTIxMTB9LHsibmFtZSI6IlRlc3QgQ2FzZSIsImNvZGUiOiJsZXQgYmFyID0gdW5kZWZpbmVkO1xucHJvY2VzcyhmdW5jdGlvbiAoaSwgaikge1xuICAvLyBPaCBub2VzLCBpdCBlc2NhcGVzIVxuICBiYXIgPSBmdW5jdGlvbiBmb28oKSB7XG4gICAgcmV0dXJuIGkgKiAyO1xuICB9XG4gIHJldHVybiBiYXIoKTtcbn0pXG5iYXIgPSB1bmRlZmluZWQ7IiwicnVucyI6WzkzMDAwLDgxMDAwLDI1MDAwLDQ1MDAwLDUyMDAwLDc0MDAwLDQwMDAwLDg0MDAwLDE0MjAwMCw3NDAwMCw2MDAwMCw5NDAwMCwxNDUwMDAsMTQ4MDAwLDE2MTAwMCwxNTIwMDAsNTAwMCwzMDAwMCwxODEwMDAsNDkwMDAsNTUwMDAsMTU1MDAwLDExNjAwMCwxMjYwMDAsMTAxMDAwLDgwMDAsNzQwMDAsMTM3MDAwLDQ2MDAwLDIxMDAwLDg2MDAwLDExMTAwMCwyNjAwMCw5MDAwMCwxMTkwMDAsNDUwMDAsMTUxMDAwLDU2MDAwLDExOTAwMCwxODEwMDAsNTIwMDAsNzQwMDAsMjUwMDAsMjMwMDAsNTQwMDAsMzkwMDAsOTUwMDAsNTcwMDAsNDYwMDAsNDQwMDAsMTM4MDAwLDEyMjAwMCw2MDAwLDY1MDAwLDExOTAwMCwzMTAwMCw1NjAwMCwxNjIwMDAsMTMxMDAwLDEzOTAwMCw5NTAwMCwxMjAwMDAsMTM2MDAwLDExMTAwMCwxMzAwMDAsMTAwMCwxMTgwMDAsMzAwMDAsNjgwMDAsMTA0MDAwLDc4MDAwLDEwODAwMCwxNDgwMDAsOTUwMDAsODkwMDAsNjQwMDAsMjIwMDAsMzEwMDAsMjAwMDAsMTgwMDAsMTAwMCwxODEwMDAsNjYwMDAsMzcwMDAsMTYwMDAsNzQwMDAsNjEwMDAsNzAwMCwxMjEwMDAsNzQwMDAsNjIwMDAsMTE3MDAwLDQ0MDAwLDUzMDAwLDEwNzAwMCwyNjAwMCw1OTAwMCw4MDAwMCwxMDAwLDMyMDAwXSwib3BzIjo3ODQxMH1dLCJ1cGRhdGVkIjoiMjAyMS0wOS0yMFQwOTozNjowMy41NjJaIn0%3D https://perf.link/#eyJpZCI6InN0NmQ4cTRrd20zIiwidGl0bGUiOiJDb...
- thomas_moon 5y agoIn my experience, currying is usually helpful as a hack and is tolerable as long as you don't get carried away. One "curry" is fine. Two+ is really pushing it and is probably a signal that something should be refactored. A lot of people here asking "why". Only solid example of it being the more elegant solution (in my opinion) is on the front end with redux. Maybe you have a large amount of objects in state. Each object has many properties and is used by many components. If you are using an implementation pattern that predefines selectors, you don't want tons of different functions selecting one property each. The simpler way is a single selector that "picks" the property dynamically by currying the property name to the single, more general selector.
- tenaciousDaniel 5y agoIt's funny, I learned about currying back when I was first learning Javascript and programming in general. That was 10 years ago, and not once in my 10 years as a professional programmer have I ever needed to curry. Maybe I've been missing out, but I don't feel like I have.
- asimpletune 5y agoI remember feeling very excited the first time I ran into a legitimate situation, where there was no better way to do things than curry the function.
- weatherlight 5y agoCurrying is useful, especially if you are doing point-free style functional programming or just functional programming in general. its a useful tool with a time and a place, I don't get all the "currying hate."
- 734129837261 5y agoJust because you can doesn't mean you should. Shit, for adding an array of objects with numbers just use a simple map/reduce instead of confusing future developers with your fancy curry. Whenever I run into a curry, I can usually solve the same issue with a simple for loop that makes it instantly clear what the code does, always.
- mnemotronic 5y ago... and I still don't understand why I'd want to use currying, except as a mental exercise in "look at the weird things I can do".
- ufo 5y agoIn my experience, currying is problematic in dynamically typed languages. If you accidentally pass less arguments than you should, then you'll only detect the error much later, when you try to use the result and find that it is a partially-applied function instead of whatever you were expecting. A question for the Javascript experts: is there a way to set a property in the returned function, saying in what line of code it was created? Perhaps something like the __LINE__ macro from C...
- anoctopus 5y agoAutomatic, implicit currying is the Haskell feature that most surprised me with how much I like having it. It makes taking apart and composing functions so much nicer, by making a very common case for partial application syntactically and mentally cheap. Trying to use currying in languages where it isn't built in makes no sense to me. It doesn't seem to compare well against doing the obvious partial application with a lambda. And it doesn't play well with variadic functions, default arguments, etc. (I have ideas for automatic currying of record types that I hope to explore in a toy language at some point, but that only does so much.)
- es7 5y agoIn my 10+ years as a JS dev, I’ve seen this pattern in numerous code bases. Inevitably it leads to bugs and confusion. Currying can lead to code that looks simpler, but I believe it hides real complexity in a non-obvious way. Well-named wrapper functions like ‘logNow’ can be great, but arbitrary curried functions are too tricky to keep track of on teams that include juniors, backend engineers and experienced senior FE too.
- brundolf 5y agoI've never seen it abused, maybe just because the team cultures didn't lend themselves to that, but I have used it in one or two places myself where it was very helpful. The main one that comes to mind is pre-binding callback to be passed down to react components, so that the exact same function instance can be passed on each re-render of the parent component and the child component doesn't get triggered to render What are some ways you've seen it abused in JS projects?
- saimondl 5y agoCurrying may be useful if you have a function that you are passing around as a first class object, and you don't receive all of the parameters needed to evaluate it in one place in the code. You can simply apply one or more parameters when you get them and pass the result to another piece of code that has more parameters and finish evaluating it there. The code to accomplish this is going to be simpler than if you need to get all the parameters together first. Also, there is the possibility of more code reuse, since functions taking a single parameter (another curried function) don't have to match as specifically with all the parameters. http://net-informations.com/js/iq/default.htm http://net-informations.com/js/iq/default.htm
- DemocracyFTW 5y agoTo be sure, the example they give is the worst they could possibly find. I man, to keep out the current date is a laudable goal for a useful log function. But to make the current date a constant is OMG. This could even be useful in a limited form when you want to issue a bunch of messages that should all point to the same moment, but even then, saying `now = Date.now(); log now, ...; log now, ...` is so much simpler and clearer. Also you totally do not need to be able to call `f( a, b ,c )` as `f( a )( b )( c )`, rather, you should avoid the latter for reasons of clarity, simplicity, and performance. You'd be just producing anonymous single-use throwaway functions for no good reason at all. You do not want to curry when you can just `g = ( a, c ) => f( a, 'xy', c );`. This is simpler, clearer, more flexible, and more to the point. Where this is not enough, just use a class. Everyone talks about inheritance, but where classes shine is that you can bind a multitude of functionality (implemented in methods) to a single set of configuration data (`this`) (and mutable state where called for).