8 ms·
Currying
- solardev 3y agoIn 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.
- auggierose 3y agoRamda, lodash, no idea what currying is, ... Software is in good hands.
- solardev 3y agoHeh, 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.
- yakshaving_jgt 3y agoAs a Haskell programmer, I have to agree with you. When you're writing Haskell, you should write idiomatic Haskell. Currying is part of that. It's a trivial concept and we use it liberally at work. If you're writing JavaScript though, I think it's a different story. I don't think JavaScript lends itself to FP in the way that Haskell or Elm does, and trying to make it do that only makes a project harder to work on.
- worddepress 3y agoAlmost 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.
- solardev 3y agoYeah, a lot of the codebase was like that. Some lone wolf mad genius that occasionally left pieces of pure brilliance behind, but more often than not, just wrote code that was incredibly idiosyncratic and unnecessarily reinvented. Like he'd spend several files making a color gradient visualization system with his own scales, with an enthusiastic but limited understanding of color theory, that ended up producing mostly reasonable but occasionally insane color scales. We spent several days of back and forth discussions with him before understanding what he was actually trying to do (which isn't what his code was actually doing) and then replaced it with a one liner out of a standard visualization/color lib. The entire codebase was like that. It stood out to me as the work of someone who was very smart but worked alone and never had to work with a team, and never considered what someone trying to retrace his mental process would have to go through. He left zero documentation or comments, built the whole thing in a rush, sold it to another company, and we were the ones hired later to clean up his mess. Some of it was very good. He was really good at efficiently encoding low level network traffic over packet encodings he invented or heavily modified. Seemed like he would've been a brilliant signals engineer or cryptologist. But having to work with his ordinary business logic code was pretty nightmarish, if interesting from a reverse engineering and psychology point of view. It's just not how I would ever want to write code meant for mere mortals. But then again, he's a multi millionaire now and I'm a rando nobody living paycheck to paycheck, so I'm in no place to judge lol.
- MarkSweep 3y ago> But then again, he's a multi millionaire now and I'm a rando nobody living paycheck to paycheck, so I'm in no place to judge lol. If he had not made such a big mess, you might not be getting this paycheck to clean it up. I think they call this “creating scope for other people” at big companies, real staff engineer stuff. =)
- bPspGiJT8Y 3y ago> After like ten or fifteen hours of trying to understand what currying is, I still have no idea Maybe you could recall which learning materials you used?
- solardev 3y agoThe 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.
- worddepress 3y agoIn 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.
- zarzavat 3y agoIt’s worth noting that even in Haskell, overuse of the so called point-free style is disliked for much the same reasons: https://wiki.haskell.org/Pointfree 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.
- epolanski 3y agoAll your JS/TS example says is that the `f` callback is not curried, so your point is entirely lost on me.
- taneq 3y agoHonestly, 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.
- 082349872349872 3y agoFunction arguments work like the power rule for exponents in elementary school math: f a b = g(a,b) = f' b a (x^2)^3 = x^(2*3) = (x^3)^2
- solardev 3y agoYou must've gone to a pretty kick ass elementary school. In mine, we'd play Connect Four and make paintings out of fake hieroglyphs...
- 082349872349872 3y agoI'm fortunate my parents didn't stick with free-to-play, but were willing to spend 5-6 digits more to buy a house in the "good" school district: https://news.ycombinator.com/item?id=39946026 https://news.ycombinator.com/item?id=39946026
- dxdm 3y agoWilling _and_ able.
- aebtebeten 3y agoI grew up during the Cold War, so I don't know what the Young Pioneers were learning*, but we were good little capitalists, and we learned the Golden Rule: "Whoever has the gold makes the rules" (*maybe the ones who spent their summers at Artek were learning "whoever makes the rules has the gold"?)
- inopinatus 3y agoWhen I were a lad, elementary school meant four hours of beatings in a muddy field, and we were glad of it
- 082349872349872 3y agoOn that note, I found Orwell's posthumously-published Such, Such Were the Joys (about his boarding school days) suggests the inspirations behind large swaths of 1984.
- akdor1154 3y agoWas 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)
- solardev 3y agoIt 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.
- HlessClaudesman 3y agoCSI: Code Shite Investigation
- kccqzy 3y ago> 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.
- threatofrain 3y agoYeah 10-15 hours is kinda crazy for an introductory topic.
- deleted 3y ago[deleted]
- curry_is_easy 3y agoSince you know JavaScript, this should help. 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.
- fxj 3y agoIn 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. ;-) just my 2 ct
- KingOfCoders 3y agoIt should be called Schönfinkel. But I guess, curry is easier to pronounce.
- selimthegrim 3y agoWWSD (What Would Smullyan Do?)
- mr_toad 3y agoNobody could ever say Schonfinkeling a function with a straight face.
- IshKebab 3y agoI always thought currying was a bad idea. 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).
- 082349872349872 3y ago> using lambda functions or (where available) sections
- yen223 3y agoI 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.
- bPspGiJT8Y 3y agoIn Gleam it also seems to be done well: https://tour.gleam.run/functions/function-captures/ https://tour.gleam.run/functions/function-captures/
- IshKebab 3y agoPartial 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. *standard HN disclaimer
- skybrian 3y agoWhen you pass some arguments to a constructor and then later, you pass some more arguments to a method, that’s equivalent to currying. Method calls are easier to read and work better with autocomplete.
- whilenot-dev 3y agoI think you confuse currying with partial application.
- kerkeslager 3y agoAre 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.
- whilenot-dev 3y agoI don't read it that way, no. Currying is a process to transform a function that takes multiple arguments into higher order functions that take one argument at a time to enable partial application (as written in the article). The partial application of a function is a different transformation of a function. Some languages provide partial application without currying a function - JavaScript has bind, Python has functools.partial etc.
- kerkeslager 3y agoOkay, I understand what you're saying, and you're right, but you're missing my point and doubling down on the same mistake again. Language is inherently imprecise and communication is never perfect. When people say something, it's rude to assume they are wrong or don't understand something because they don't say it exactly the way you would have said it. This is extremely common in technical communication and it's extremely counterproductive. I'm suggesting that you could be a better reader by trying to understand what people are trying to say even if they don't communicate it exactly the way you want them to. For example, no one in this comment chain is confused about what currying or partial application are, so it's a bit rude that you've assumed people are confused because you didn't take the time to figure out what people were trying to communicate. Instead, because people didn't say things exactly the way you would have said it, you jumped in with corrections. If you take a step back and resist the urge to correct people, can you understand how "Currying is a subset of partial application" is true?
- Aditgchdggg 3y ago[flagged]
- kerkeslager 3y agoCurrying 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.
- spiriftal 3y ago[dead]
- boxed 3y agoCurrying 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.
- iso8859-1 3y agoIf 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.
- ivanjermakov 3y agoCould you share an example?
- bPspGiJT8Y 3y agoThis function would use an anonymous record: splitString :: { pattern :: String, string :: String } -> Array String splitString { pattern, string } = ... This function would use positional arguments: splitString :: Pattern -> String -> Array String splitString pattern string = ...
- iso8859-1 3y agohttps://github.com/purescript/documentation/blob/master/language/Records.md https://github.com/purescript/documentation/blob/master/lang...
- boxed 3y agoI write quite a bit of Elm and I use currying a lot. I really wish it was explicit. Or at least that my IDE could highlight them.
- mjarrett 3y agoOne 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).
- fxj 3y agoCurrying 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. https://en.wikipedia.org/wiki/Tacit_programming https://en.wikipedia.org/wiki/Tacit_programming https://medium.com/@jesterxl/real-world-uses-of-tacit-programming-part-1-of-2-f2a0c3f9e00c 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 https://wiki.haskell.org/Pointfree
- boxed 3y agoYou can have partial function application without currying. The problem with currying is that it's implicit, and positional. Two very bad things in programming.
- fxj 3y agoAs 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 1000000 >> ψ( ψ(χ>>op("(x**2+y**2)**0.5<1")@rlµ<<χ)>>Σ*4>> _/_) or this: # 10 fibonacci numbers [x:=[1,1]] + [x := [x[1], sum(x)] for i in range(10)] 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
- boxed 3y agoSure, but why implicit? x = y 3 vs x = partial y 3 I would vastly prefer the latter. And if you do it a lot you could have a symbol for it in your language. Your examples are unrelated to currying though...
- danmur 3y agoHaskell always makes me hungry
- jraph 3y agoWith a bit of ginger, some lemon and perhaps some coco, it's excellent.
- indigosun 3y agoI recently saw the title, Learn Physics with Functional Programming, and thought to myself... I know physics and functional programming but I want to learn Haskell. Having gone through it, I highly recommend the book; especially to anyone knowledgeable about any 2 and interested in the third. I love Haskell. I love writing it, and reading it. Haskell is a beautiful programming language. One where I felt immediately at home in and comforted by. Currying is one of the foundational concepts of the language, but I do think it is one aspect of the language the community treats excessively sacredly. In Haskell, function composition has right associativity. What this means, in the event of a function that takes in 3 Integers as arguments and returns a Double would have a type definition of: FN_NAME :: Int -> Int -> Int -> Double The right associativity then also means this is equivalent to these type definition: fn0 :: Int -> Int -> Int -> Double fn0 x y z = (fromIntegral (x + y)) / (fromIntegral z) fn1 :: Int -> (Int -> Int -> Double) fn1 x y z = (fromIntegral (x + y)) / (fromIntegral z) fn2 :: Int -> (Int -> (Int -> Double)) fn2 x y z = (fromIntegral (x + y)) / (fromIntegral z) ghci> fn0 1 2 3 -- 1.0 ghci> fn1 1 2 3 -- 1.0 ghci> fn2 1 2 3 --1.0 This is currying in action. A function that takes three arguments is the same as a function that takes one argument and passes it to function that takes two arguments. As I understand it, the compiler sees ALL functions as a composition of functions of one input. This is pretty cool, and a powerful foundation to build on. Where I take a bit of issue is when the Haskell community allows the rigor of the type definition to carry some of the weight of the expressiveness of the function definition. Each function definition ('=') pairs with a type definition ('::'), as the ones above, ie. an example from the promoted book: type R = Double type VecDerivative = (R -> Vec) -> R -> Vec vecDerivative :: R -> VecDerivative vecDerivative dt v t = (((v (t + dt/2)) ^-^ (v (t - dt/2))) ^/ dt) velFromPos :: R -- dt -> (R -> Vec) -- position function -> (R -> Vec) -- velocity function velFromPos = vecDerivative The second function definition: "velFromPos = vecDerivative" ; is equal to saying, "velFromPos dt v t = vecDerivative dt v t". Haskell seems to allows this syntactical ~implicit argument passing because of it's reverence for currying. -- random example of 'vector-valued function' v1 :: R -> Vec v1 t = ((2 *^ (t**2 *^ iHat)) ^+^ (3 *^ (t**3 *^ jHat)) ^+^ (t**4 *^ kHat)) ghci> vecDerivative 0.01 v1 1 -- vec 3.999999999999937 9.000074999999885 4.000099999999962 ghci> velFromPos 0.01 v1 1 -- vec 3.999999999999937 9.000074999999885 4.000099999999962 This has some interesting effects, such as displayed above where the use of currying makes it explicitly clear that calculating the velocity is the exact same thing as calculating the derivative of position. But I always felt this ~implicit syntax is needlessly aggressive to newcomers. I also pathologically write my Haskell code like a Lisp because the Haskell community's celebration of the complex precedence structure of its order of operations causes practitioners to appear, to my eyes, over confident in their ability to sight read their own work's order of operations. ;P ghci> sqrt 2 + 1 * 3 + 3 * 2 + 1 / 7 -- 10.557070705230238 ghci> ((sqrt 2) + ((1 * 3) + ((3*2) + (1 / 7)))) -- 10.557070705230238 I will forever think the second is easier to read, but if you look at many Haskell code bases you will see stuff like the first all over the place, and I have been unable to find a reason why the implicit function arguments and lack of parenthesis syntax is considered idiomatic. In the face of the ability to write the functions explicitly and with expressive parentheses, it seems the answer is "because you can". https://nostarch.com/learn-physics-functional-programming https://nostarch.com/learn-physics-functional-programming
- electrondood 3y agoI 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.
- scrubs 3y agoHaskell ... always in the weeds with nerdy self talk.