4 ms·
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
by kerkeslager 3y ago
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.
- ReleaseCandidat 3y agoJust 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.
- kerkeslager 3y agoYeah, 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.
- kamray23 3y agoI 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 gears = filter ((==2) . length) . map (neighbouringNumbers numbers) $ filter ((=='*') . fst) symbols which without currying would have to look like gears = (\xs -> filter ((\x -> x == 2) . length) xs) . (\xs -> map (\x -> neighbouringNumbers numbers x) xs) $ filter (\(c,_) -> c == '*') symbols It just makes partial application a lot easier, especially when this kind of code pops up all over the place.
- kerkeslager 3y ago> 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.
- nightlyherb 3y agoHi, as someone who has been interested in functional programming for a while but who struggled to read point-free Haskell code until recently, I think it might be useful to share my perspective. gears = filter ((==2) . length) . map (neighbouringNumbers numbers) $ filter ((=='*') . fst) symbols If this Haskell code does not look clear, it's because people are unfamiliar with point-free style, not because the this code is badly written, and especially not because the reader is stupid. The given Haskell code reads naturally to me now. (I'm only a little uncertain because I can't write Haskell.) It would have been line noise to me before before point-free style clicked. I tend to read Haskell code in articles from right to left, and it reads: "Choose the symbols whose first letter is '*', get their respective (neighboringNumbers numbers), and choose the results whose length is 2". I read (neighboringNumbers numbers) as if it were a noun. I don't know how this clicked, but I'm pretty sure it has nothing to do with stupidity. Perhaps it was by chance, or perhaps it was by banging my head against the wall enough times. Would I introduce point-free style in a JS codebase? Probably not. People are unfamiliar with this kind of style. Would I introduce this style in a Haskell codebase? Almost certainly, because it's clear and I think it reflects how Haskell programmers think.
- Simon_ORourke 3y ago> 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.