Currying makes positional only parameters look cooler and fancier. It's a trap. Labeled arguments is the way to go for 99% of all parameters. Accidental currying is horrible.
If you look at how people actually write PureScript, which is much like Haskell, but with anonymous records, you can see that they're not actually used 99% of the time.
Somewhat related - be wary of implicit parameter passing in function pipelines. For example, Try ['10', '10', '10'].map(Number.parseInt) in your browser console. What's actually being called is:
One of my favorite features in OCaml is labeled arguments. You get a similar flavor of partial application, but without the strict argument order requirement (most of the time -- higher order functions are strict about labeled-function arguments).
The technique you describe requires us to predict what data should be partially applied (before actually using it), and what data should be applied and used immediately. In contrast, currying/partial application doesn't require this decision from us -- for this reason I consider the functional approach more flexible than the object-oriented one.
In addition to that, I don't always find the constructor/method approach more readable. It _can_ be readable, if you design your API with care; however, it is common to group related data into records in the FP world as well, which mimicks OOP constructors, in a sense.
Yes, like many refactorings, rewriting a function as an immutable class with a method is not always a win. It depends on how you want to group a function’s parameters, whether that grouping has an intuitive name, and whether you might want to write other methods that take the same initial parameters. Sometimes grouping parameters using a record type is better, though you don’t get the nice syntax.
If currying isn’t built-in, you can emulate it. In TypeScript you need to do it explicitly, which means anticipating that it will be used, and whenever I try that, I later decide that it’s needlessly obscure and rewrite the code some other way.
Even with built-in currying, you still need to anticipate usage by changing the order of the parameters, based on which ones you expect the callers to have first. It’s less general than writing an inline function with exactly the parameters you need. TypeScript has nice syntax for defining tiny, one-off functions and I think that’s better than having currying.
One side-effect of having currying in a language is that it discourages naming things, and I think that’s often bad for readability. A chain of method calls gives you a lot of names to look up and a place to put documentation.
No, not exactly. And partial application of any function is nowadays possible using anonymous functions to wrap the original one, no need for constructors and special methods.
What currying in Haskell and any other ML does, is the following as Javascript. Let's say we have a function with four parameters `func(a, b, c, d) {return a + b + c + d;}`, then the curried version is the following:
func = (a) => {
return (b) => {
return (c) => {
return (d) => a + b + c + d;
}
}
}
You're assuming that the poster doesn't understand what currying is and you're jumping in with a correction which is kinda rude.
Instead of assuming you know better and jumping in with a correction, could you reread that post and assume that the person knows what they're talking about but isn't communicating exactly the way you would have communicated it? I.e., read with the intent of understanding intent, rather than read with the intent of correcting?
Are you saying this to be helpful, or just to show you're smarter? If you're saying this to be helpful, you should probably explain more.
Currying is a subset of partial application, so I suspect the person you're responding to is just being a bit loose with their wording, as opposed to not understanding the topic.
* more difficult to distinguish arguments and return values (there's a reason most languages have distinct syntax for them)
* it encourages you to put your arguments in an order that might not be the most logical
And on top of that it only works for the last argument(s).
Feels like a lot of disadvantages to allow an overly clever trick that you can do in other languages much more readable using lambda functions. And those work for any arguments, not just the last one(s).
I much prefer partial application. It just makes more intuitive sense - you take a function with many arguments, "fix" some of those arguments, and get a new function out. No need to mess with argument ordering, or all that.
It's a shame the only language that did partial application well is, weirdly, Python.
Partial application just means fixing some arguments. My point was you can do that easily using lambda functions.
I guess you were talking about `functools.partial`, which is basically the same as currying and also only works on the last arguments. It's better to use lambda functions.
Also Python is far from the only language to have a function like that, e.g. see C++'s `std::bind` which nobody* uses since lambdas were supported.
In my entire life as a programmer, this is the one thing that I've found most confusing. Our old codebase used a lot of curried Ramda functions and nobody on the team could read any of it. We spent a week ripping every single instance of it out and replaced it with regular lodash and never looked back and never had an issue with it again.
After like ten or fifteen hours of trying to understand what currying is, I still have no idea, and frankly don't care. If I see it in a codebase it'd just be a big red flag to avoid that job altogether.
In Haskell it is easy. If you "forget" the last argument to a function, you get returned a function where you can provide that later on. A bit like saying "you can fill this in later". That is a "curried" function.
Example
add 1 2 // add is curried, you can use it like this, the "normal" way, returns 3
p = add 1 // since add is curried I can also provide just the first argument
p 2 // and then apply the last argument to the intermediate results, returns 3
What is the point of using curried functions in JS? I am not really sure. It is not very ergonomic and I wouldn't like to use them in general. Maybe for some specific things it could be useful.
In Haskell curried form is the default and the syntax and semantics really suits it. In JS non-curried is the default and it just looks odd, and you need libraries to support it. That library you mentioned doesn't look nice to use.
There is a sliding scale and even at the Haskellers have a limit of how much point-free they can take.
In JavaScript use of the style is problematic in another way: unlike Haskell in JS the length of the argument list is variable, which means that if someone adds another argument to either the caller or callee it can break the code in subtle and unexpected ways.
For this reason it’s good practice to always wrap functions being passed as arguments to another function in a lambda expression.
i.e instead of writing: g(f), you should usually write: g(x => f(x)) unless you have good reason to believe that g(f) is safe. This makes it difficult to use point-free style at all in JS.
For example arr.map(f) is generally unsafe in JS because if `f` adds an extra default argument of type number then your code will break and even TypeScript won’t let you know.
Honestly, a lot of these 'advanced' features aren't a good choice in most codebases IMO. The vast majority of 'enterprise' coding is CRUD and glue, where execution speed doesn't matter and being as simple and explicit as possible is the highest virtue. And the significant number of professional coders who worked hard to get their heads around things like pointers and printf format specifiers are still plenty productive in those kinds of codebases.
Things like currying are fun but like anything that encourages gratuitously deep call trees, they wreck your locality of reference (as a developer) and force you to 'decompile' the code in your head in order to understand it. I'm sure that a top level developer would be able to write curried JS in such a way that it was clear and readable to another top level developer, but that's not the point. The code's not for you, it's for newbie who gets stuck with it when you move on.
What if you forget, not in quotes, to give the second argument? Why on earth would you want to get a type error in a completely different part of the code because you got a function instead of an integer? Wouldn't it be desirable to have the compiler just tell you that you forgot the second argument where you forgot the second argument?
Is it really valuable to be able to do:
p = add 1
...instead of:
inc = (\x -> add 1 x)
...or, heaven forbid:
inc x = add 1 x
...?
I mean, I get the idea, they're following the lambda calculus, but this is one of the things that should have been dropped when they started to expand the lambda calculus to a general purpose programming language.
> After like ten or fifteen hours of trying to understand what currying is
I have doubts on whether this is a sincere attempt at understanding the concept. The linked wiki page is like a five minute read and that's enough to understand the concept at a user's level.
What I suspect is that something else is tripping you up and causing code readability issues; but since you didn't know what currying is you incorrectly ascribed currying as the problem.
Heh, so much of the world runs on basic business software. If it ran fine after the rewrite, with much less cognitive load, why not? These were just bog standard web pages that someone overengineered the hell out of, to a point that nobody else could understand or maintain it. Sure, that might win them l33t points, but wasn't very useful otherwise.
Almost as if an astronaut can come, do their thing in a whirlwind and leave for another company 2 years later, while the remaining plebs have to figure it all out.
In my experience, functional programming really shines in rapid prototyping. However, in production code I would try to avoid it, because it can confuse developers that are not used to it and also sometimes I struggle myself when decoding the short "clever" one-liner that I wrote 5 years ago. But if you want to write very powerful code withing a single line it is really great and makes you very productive for finding solutions fast. It is a bit like the Perl throw-away code we were writing in the 1990s. ;-)
The Ramda docs, primarily, and Google searches. It just seemed like a lot of unnecessary complexity. We modified the parts we understood (or believed we did), and just rewrote the rest from scratch. The tests still passed, nobody else complained, the managers and customers never noticed, the devs were much happier, QA never said a word, and a whole class of bugs disappeared once we were able to reliably predict the output of simple helper functions. Shrug. Seemed like a win win. Probably the best thing we ever did to that codebase.
Was the codebase in strict Typescript? I find currying to be sometimes useful in the right language / paradigm, as long as you can rely on the compiler to yell at you until you get it right.
Dynamically typed functional retrofit on with no type checking though? Hell no, that's a write-once read-never mess of unmaintainable shite.
(I also have been on a team with The Guy Who Wanted To Ramda, and I'm someone who writes build scripts in ocaml)
It was a mishmash. The parts written by later employees were strongly typed and easy to follow. The parts he wrote weren't typed at all and frequently relied on implicit conversions and bitwise operators to do magic operations that would take us mere mortals several days to even begin to understand, all just to save what would've been like 3 lines of code and a single comment.
Inheriting that codebase felt like watching one of those serial killer dramas... obviously a really smart guy, but with a psychology unlike most people I've ever met, and really hard to follow as a normal person. I still see him with a mixture of respect, awe, curiosity, and wonder.
The thing is, none of the stuff he gave is was actually complicated. It's just a basic business dashboard. But he reinvented almost every single part of it in totally nonstandard ways that the whole project was less about coding and more about anthropology and forensic psychology.
Currying is only a part of the functional programming stack. It can make your life easier when you combine it with other functional programming techniques like tacit programming. e.g. you have a function with a very long arguments list and dont want to write the arguments over and over again, but still dont want to define an additional function.
when you use tacit programming and currying together you can write code in a "bash-like" pipe style which not only makes the program more readable but also is less error prone.
I think a lot of people would disagree that they make code more readable. These are all ways of removing the names of intermediate results. Some names are noise, but more often, they’re useful documentation.
You can make code much shorter by removing all the application-specific names and only using very abstract, domain-independent names or symbols. The result can be seen in complicated regular expressions, array languages, bash scripts, and so on. In the wrong hands, these are all notoriously unreadable.
But you can also go to the other extreme and give every little thing a very long name, and that’s less readable in its own way. Bob Nystrom wrote a nice article about how to combat that tendency. [1]
Naming things is an art. In functional languages, let expressions are convenient and useful. When you feel like you don’t want to define another function, sometimes it’s better to resist that tendency and think about a better name.
You can have partial function application without currying. The problem with currying is that it's implicit, and positional. Two very bad things in programming.
As I said in another comment down below. Currying is great for rapid prototyping because you dont have to define a new function. eg. add1 and add(1) which you then can use for map/reduce or pointfree programming. In production code it often makes more sense to be a bit more detailed and verbose to make it readable for devs who are not familiar with currying. But abstraction in general is not something that is "bad".
high level abstraction can make you very productive, but it can also be rather unreadable. e.g. two code snippets in python (yes this is valid python code with some operator overloading):
# compute pi by drawing random numbers in a circle
I understand currying to be using a closure to "bake in" a value for a parameter.
Reading through these comments, it's clear that currying isn't clearly understood, which makes me doubt it's worth the cognitive load to use in a codebase.
Currying is one of those things in Haskell that always makes me think that Haskellers were so preoccupied with whether or not they could, that they didn't stop to think if they should.
In my limited Haskell experience, you can mostly ignore that functions are curried, but every once in a while, someone uses it, and it has never, in my experience, made the code easier to understand, because the currying happens implicitly.
The more general case of currying is partial application, where instead of a function taking only its first argument and returning a function that takes its next argument, you can apply a function any of it's arguments and return a function that takes the remaining arguments. So if you have:
div x y = x / y
Then:
invert = (\x -> div 1 x)
-- invert is a partial application of div to the 1 as first argument
halve = (\x -> div x 2)
-- halve is a partial application of div to 2 as the second argument
Obviously, the first one could be done with currying. What makes partial application better in my opinion is that it's explicit about what's happening, and doesn't happen without you asking it to.
I don't think currying happens without you asking to, though. It happens because it happens, it's part of the language, and it's something you implicitly keep in the back of your mind every time you see a function call. I don't program a lot in Haskell, only some maths things I sometimes might need since it is rather useful for that, but the concept of currying is so natural that it's constantly expressing itself in the code. Very rarely do you apply arguments and consider that to be a function call in itself instead of like, three function calls. And since partial application is so incredibly important to Haskell and other similar languages, without currying writing would be very difficult. Consider the actual simple example of
> I don't think currying happens without you asking to, though. It happens because it happens, it's part of the language, and it's something you implicitly keep in the back of your mind every time you see a function call.
Eh, but that's my point: I want less cognitive load. Currying is another thing that I have to keep in the back of my mind, and Haskell already has way too many of those, and it's not a particularly useful thing. I've got limited space in the back of my mind for things and if I'm going to keep things in the back of my mind I want them to be useful. I mean, if your argument in favor of currying is that it saved you a few keystrokes in that example, color me unimpressed.
Maybe I'm just too stupid to understand easily, but the "simple" example you're giving is taking me a while to understand. If a junior dev on my team submitted that in a PR I'd send it back asking them to break it up into a few smaller named functions and probably not use a partial application at all. Something like "I know it's fun to be clever but let's make this easier for the next person who has to figure out what it does".
I guess what I'm saying is that for that example it seems like you're going for tersity rather than clarity for future readers of the code. If you were going for clarity you probably wouldn't write it either of the ways you've given.
And in big projects clarity is the number 1 concern[1]. In toy examples like this I can slog through and figure something like this out, but when it's 30 functions written like this, all calling each other in the most clever ways possible, nobody can figure it out.
[1] EDIT: Okay #2 after correctness perhaps. But it becomes hard to achieve correctness without clarity as a project grows.
Just a remark, currying is nothing that is special to Haskell, it's a general ML "speciality" including the syntax for function application of not using parens.
Which is because lambda calculus does not have functions with more than one parameter, I guess.
Yeah, I'd attribute this to the lambda calculus, for sure.
And in the lambda calculus, it makes a ton of sense, because it allows you to break proofs into smaller chunks more easily.
But in a general-purpose programming language where the proofs are automated, I think this just makes the code less communicative of programmer intent.
> Currying is one of those things in Haskell that always makes me think that Haskellers were so preoccupied with whether or not they could, that they didn't stop to think if they should.
This had to be said! Sure you can make the argument that this improves your productivity as a coder, or makes the code more concise, but to someone coming along cold and trying to pick apart your code to fix something it's going to be a nightmare.
Number.parseInt('10', 0) Number.parseInt('10', 1) Number.parseInt('10', 2)
JavaScript lacks typing, and then they decided on a weird signature for the callback...
Method calls are easier to read and work better with autocomplete.
In addition to that, I don't always find the constructor/method approach more readable. It _can_ be readable, if you design your API with care; however, it is common to group related data into records in the FP world as well, which mimicks OOP constructors, in a sense.
If currying isn’t built-in, you can emulate it. In TypeScript you need to do it explicitly, which means anticipating that it will be used, and whenever I try that, I later decide that it’s needlessly obscure and rewrite the code some other way.
Even with built-in currying, you still need to anticipate usage by changing the order of the parameters, based on which ones you expect the callers to have first. It’s less general than writing an inline function with exactly the parameters you need. TypeScript has nice syntax for defining tiny, one-off functions and I think that’s better than having currying.
One side-effect of having currying in a language is that it discourages naming things, and I think that’s often bad for readability. A chain of method calls gives you a lot of names to look up and a place to put documentation.
What currying in Haskell and any other ML does, is the following as Javascript. Let's say we have a function with four parameters `func(a, b, c, d) {return a + b + c + d;}`, then the curried version is the following:
Instead of assuming you know better and jumping in with a correction, could you reread that post and assume that the person knows what they're talking about but isn't communicating exactly the way you would have communicated it? I.e., read with the intent of understanding intent, rather than read with the intent of correcting?
Currying is a subset of partial application, so I suspect the person you're responding to is just being a bit loose with their wording, as opposed to not understanding the topic.
It makes the code way less readable:
* more difficult to distinguish arguments and return values (there's a reason most languages have distinct syntax for them)
* it encourages you to put your arguments in an order that might not be the most logical
And on top of that it only works for the last argument(s).
Feels like a lot of disadvantages to allow an overly clever trick that you can do in other languages much more readable using lambda functions. And those work for any arguments, not just the last one(s).
It's a shame the only language that did partial application well is, weirdly, Python.
I guess you were talking about `functools.partial`, which is basically the same as currying and also only works on the last arguments. It's better to use lambda functions.
Also Python is far from the only language to have a function like that, e.g. see C++'s `std::bind` which nobody* uses since lambdas were supported.
*standard HN disclaimer
how is `add a b c = a + b + c` being curried an issue to you?
or (where available) sections
After like ten or fifteen hours of trying to understand what currying is, I still have no idea, and frankly don't care. If I see it in a codebase it'd just be a big red flag to avoid that job altogether.
Example
What is the point of using curried functions in JS? I am not really sure. It is not very ergonomic and I wouldn't like to use them in general. Maybe for some specific things it could be useful.In Haskell curried form is the default and the syntax and semantics really suits it. In JS non-curried is the default and it just looks odd, and you need libraries to support it. That library you mentioned doesn't look nice to use.
https://wiki.haskell.org/Pointfree
There is a sliding scale and even at the Haskellers have a limit of how much point-free they can take.
In JavaScript use of the style is problematic in another way: unlike Haskell in JS the length of the argument list is variable, which means that if someone adds another argument to either the caller or callee it can break the code in subtle and unexpected ways.
For this reason it’s good practice to always wrap functions being passed as arguments to another function in a lambda expression.
i.e instead of writing: g(f), you should usually write: g(x => f(x)) unless you have good reason to believe that g(f) is safe. This makes it difficult to use point-free style at all in JS.
For example arr.map(f) is generally unsafe in JS because if `f` adds an extra default argument of type number then your code will break and even TypeScript won’t let you know.
Things like currying are fun but like anything that encourages gratuitously deep call trees, they wreck your locality of reference (as a developer) and force you to 'decompile' the code in your head in order to understand it. I'm sure that a top level developer would be able to write curried JS in such a way that it was clear and readable to another top level developer, but that's not the point. The code's not for you, it's for newbie who gets stuck with it when you move on.
What if you forget, not in quotes, to give the second argument? Why on earth would you want to get a type error in a completely different part of the code because you got a function instead of an integer? Wouldn't it be desirable to have the compiler just tell you that you forgot the second argument where you forgot the second argument?
Is it really valuable to be able to do:
...instead of: ...or, heaven forbid: ...?I mean, I get the idea, they're following the lambda calculus, but this is one of the things that should have been dropped when they started to expand the lambda calculus to a general purpose programming language.
I have doubts on whether this is a sincere attempt at understanding the concept. The linked wiki page is like a five minute read and that's enough to understand the concept at a user's level.
What I suspect is that something else is tripping you up and causing code readability issues; but since you didn't know what currying is you incorrectly ascribed currying as the problem.
Software is in good hands.
just my 2 ct
Maybe you could recall which learning materials you used?
Dynamically typed functional retrofit on with no type checking though? Hell no, that's a write-once read-never mess of unmaintainable shite.
(I also have been on a team with The Guy Who Wanted To Ramda, and I'm someone who writes build scripts in ocaml)
Inheriting that codebase felt like watching one of those serial killer dramas... obviously a really smart guy, but with a psychology unlike most people I've ever met, and really hard to follow as a normal person. I still see him with a mixture of respect, awe, curiosity, and wonder.
The thing is, none of the stuff he gave is was actually complicated. It's just a basic business dashboard. But he reinvented almost every single part of it in totally nonstandard ways that the whole project was less about coding and more about anthropology and forensic psychology.
Deleted Comment
Uncurried:
((x, y) => x * y)(3, 5) === 15
Curried:
(x => y => x * y)(3)(5) === 15
If you don't understand that, your problem is that you don't understand anonymous functions (lambdas), not that you don't understand currying.
https://en.wikipedia.org/wiki/Tacit_programming
https://medium.com/@jesterxl/real-world-uses-of-tacit-progra...
when you use tacit programming and currying together you can write code in a "bash-like" pipe style which not only makes the program more readable but also is less error prone.
https://wiki.haskell.org/Pointfree
You can make code much shorter by removing all the application-specific names and only using very abstract, domain-independent names or symbols. The result can be seen in complicated regular expressions, array languages, bash scripts, and so on. In the wrong hands, these are all notoriously unreadable.
But you can also go to the other extreme and give every little thing a very long name, and that’s less readable in its own way. Bob Nystrom wrote a nice article about how to combat that tendency. [1]
Naming things is an art. In functional languages, let expressions are convenient and useful. When you feel like you don’t want to define another function, sometimes it’s better to resist that tendency and think about a better name.
[1] https://journal.stuffwithstuff.com/2016/06/16/long-names-are...
When it comes to names, the binding and lexical scope and position in the AST helps me read the code more than the English pronunciation.
Tbh if I have a stream of English words in my head reading Haskell, it's probably already meh code at best :S
I'd say I see more Haskell code made unreadable due to over-naming than under-naming in the wild (i.e. industry)
high level abstraction can make you very productive, but it can also be rather unreadable. e.g. two code snippets in python (yes this is valid python code with some operator overloading):
# compute pi by drawing random numbers in a circle
or this: I would of course not use that in production code, but for a rapid prototype it is priceless to do something like this in python.just my 2 ct
Reading through these comments, it's clear that currying isn't clearly understood, which makes me doubt it's worth the cognitive load to use in a codebase.
In my limited Haskell experience, you can mostly ignore that functions are curried, but every once in a while, someone uses it, and it has never, in my experience, made the code easier to understand, because the currying happens implicitly.
The more general case of currying is partial application, where instead of a function taking only its first argument and returning a function that takes its next argument, you can apply a function any of it's arguments and return a function that takes the remaining arguments. So if you have:
Then: Obviously, the first one could be done with currying. What makes partial application better in my opinion is that it's explicit about what's happening, and doesn't happen without you asking it to.Eh, but that's my point: I want less cognitive load. Currying is another thing that I have to keep in the back of my mind, and Haskell already has way too many of those, and it's not a particularly useful thing. I've got limited space in the back of my mind for things and if I'm going to keep things in the back of my mind I want them to be useful. I mean, if your argument in favor of currying is that it saved you a few keystrokes in that example, color me unimpressed.
Maybe I'm just too stupid to understand easily, but the "simple" example you're giving is taking me a while to understand. If a junior dev on my team submitted that in a PR I'd send it back asking them to break it up into a few smaller named functions and probably not use a partial application at all. Something like "I know it's fun to be clever but let's make this easier for the next person who has to figure out what it does".
I guess what I'm saying is that for that example it seems like you're going for tersity rather than clarity for future readers of the code. If you were going for clarity you probably wouldn't write it either of the ways you've given.
And in big projects clarity is the number 1 concern[1]. In toy examples like this I can slog through and figure something like this out, but when it's 30 functions written like this, all calling each other in the most clever ways possible, nobody can figure it out.
[1] EDIT: Okay #2 after correctness perhaps. But it becomes hard to achieve correctness without clarity as a project grows.
Which is because lambda calculus does not have functions with more than one parameter, I guess.
And in the lambda calculus, it makes a ton of sense, because it allows you to break proofs into smaller chunks more easily.
But in a general-purpose programming language where the proofs are automated, I think this just makes the code less communicative of programmer intent.
This had to be said! Sure you can make the argument that this improves your productivity as a coder, or makes the code more concise, but to someone coming along cold and trying to pick apart your code to fix something it's going to be a nightmare.