[HN Gopher] Currying
       ___________________________________________________________________
        
       Currying
        
       Author : peter_d_sherman
       Score  : 53 points
       Date   : 2024-04-07 02:50 UTC (20 hours ago)
        
 (HTM) web link (wiki.haskell.org)
 (TXT) w3m dump (wiki.haskell.org)
        
       | solardev wrote:
       | 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.
        
         | auggierose wrote:
         | Ramda, lodash, no idea what currying is, ...
         | 
         | Software is in good hands.
        
           | solardev wrote:
           | 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.
        
             | yakshaving_jgt wrote:
             | As 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 wrote:
           | 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.
        
             | solardev wrote:
             | Yeah, 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 wrote:
               | > 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. =)
        
               | solardev wrote:
               | Heh, exactly. Some of us are just software janitors for
               | the bigwigs.
        
         | bPspGiJT8Y wrote:
         | > 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 wrote:
           | 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.
        
         | worddepress wrote:
         | 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.
        
           | zarzavat wrote:
           | It'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
           | 
           | 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.
        
           | taneq wrote:
           | 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.
        
           | kerkeslager wrote:
           | I've never liked this "feature".
           | 
           | 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.
        
             | bPspGiJT8Y wrote:
             | > What if you forget, not in quotes, to give the second
             | argument?
             | 
             | I will get a type error and it will take me 2-3 seconds to
             | figure out what it is about.
             | 
             | > 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?
             | 
             | Why would it be in a completely different part of the code?
             | At most it would be 2 lines away, but usually on the same
             | line.
        
               | kerkeslager wrote:
               | > I will get a type error and it will take me 2-3 seconds
               | to figure out what it is about.
               | 
               | You should really screen capture yourself coding
               | sometime. On a large codebase you're lucky if the
               | compiler even runs in 10 seconds.
               | 
               | > Why would it be in a completely different part of the
               | code? At most it would be 2 lines away, but usually on
               | the same line.
               | 
               | Because possibly you return the partial assuming it's a
               | number, and try to use it somewhere else, possibly even
               | in another file.
        
               | bPspGiJT8Y wrote:
               | > Because possibly you return the partial assuming it's a
               | number, and try to use it somewhere else, possibly even
               | in another file.
               | 
               | Sorry, I must've been more explicit instead of implying
               | certain usage patterns. What I meant here is that I have
               | a hard time imagining this happening because I would
               | start working on a function by writing its type
               | signature. Unless my types check out, I won't be able to
               | mark this function as "done" and jump to another part of
               | code. So the situation "you return the partial assuming
               | it's a number" simply _can not_ happen, that 's exactly
               | what type checking is for. By the time I use it in
               | another place, it has to already have been type checked.
        
         | 082349872349872 wrote:
         | Function 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 wrote:
           | You 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 wrote:
             | I'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
        
               | dxdm wrote:
               | Willing _and_ able.
        
               | 082349872349872 wrote:
               | I 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 wrote:
             | When I were a lad, elementary school meant four hours of
             | beatings in a muddy field, and we were glad of it
        
               | 082349872349872 wrote:
               | On 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_.
        
               | inopinatus wrote:
               | _Coming Up for Air_ is practically my childhood in a
               | nutshell
        
         | akdor1154 wrote:
         | 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)
        
           | solardev wrote:
           | 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.
        
             | OscarTheGrinch wrote:
             | CSI: Code Shite Investigation
        
         | kccqzy wrote:
         | > 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 wrote:
           | Yeah 10-15 hours is kinda crazy for an introductory topic.
        
         | curry_is_easy wrote:
         | Since 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 wrote:
         | 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. ;-)
         | 
         | just my 2 ct
        
       | KingOfCoders wrote:
       | It should be called Schonfinkel. But I guess, curry is easier to
       | pronounce.
        
       | IshKebab wrote:
       | I 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 wrote:
         | > _using lambda functions_
         | 
         | or (where available) sections
        
         | yen223 wrote:
         | 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.
        
           | bPspGiJT8Y wrote:
           | In Gleam it also seems to be done well:
           | https://tour.gleam.run/functions/function-captures/
        
             | yen223 wrote:
             | That looks really cool, thanks for sharing
        
           | IshKebab wrote:
           | 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.
           | 
           | *standard HN disclaimer
        
       | skybrian wrote:
       | When 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 wrote:
         | I think you confuse currying with partial application.
        
           | kerkeslager wrote:
           | 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.
        
             | whilenot-dev wrote:
             | I 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 wrote:
               | Okay, 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?
        
               | whilenot-dev wrote:
               | > ...can you understand how "Currying is a subset of
               | partial application" is true?
               | 
               | It isn't true and i can understand why someone would make
               | that statement. Currying and partial application both
               | come from a functional mindset that treats functions as
               | data, and like data also functions can be transformed.
               | 
               | Currying and partial application both satisfy a
               | transformation depending on a desired context:
               | - Currying "widens" a function "up" (as in you don't need
               | to know all arguments beforehand in the same context
               | anymore)       - Partial application "narrows" a function
               | "down" (as in arguments are contained within the context
               | of the partially applied function)
               | 
               | "Currying is a subset of partial application" is true in
               | the same sense as if "subtraction is a subset of
               | addition". Sure, you can subtract negative values to have
               | addition and add negative values to have subtraction, but
               | that can't be your point, is it?
               | 
               | > If you take a step back and resist the urge to correct
               | people...
               | 
               | Please keep it on topic.
        
               | kerkeslager wrote:
               | > It isn't true and i can understand why someone would
               | make that statement
               | 
               | It is true, and you are smart enough to understand it if
               | you stop assuming you're the only one who understands the
               | topic. I'm specifically sticking with the wording
               | "Currying is a subset of partial application" to make the
               | point that you can understand what someone is trying to
               | say even if they don't say it exactly the way you would
               | like them to say it.
               | 
               | > - Currying "widens" a function "up" (as in you don't
               | need to know all arguments beforehand in the same context
               | anymore) > - Partial application "narrows" a function
               | "down" (as in arguments are contained within the context
               | of the partially applied function)
               | 
               | So you're saying that I'm "widening" the function "up"
               | when I do:                   increment = add 1
               | invert = div 1
               | 
               | ...? But I'm "narrowing" the function "down" when I do:
               | increment = (\x -> add 1 x)         halve = (\x -> div x
               | 2)
               | 
               | ...?
               | 
               | When I curry `add` above, isn't the argument 1 contained
               | within the context of the partially applied function?
               | 
               | When I partially apply `div` above, don't I no longer
               | have to know all the arguments (i.e. I no longer need to
               | know the denominator 2)?
               | 
               | It sure seems like this up/down wide/narrow wording
               | you're fixated on isn't a particularly better
               | descriptions of what's going on with currying and partial
               | application.
               | 
               | It mean, maybe it's just my font, but the partial
               | applications are a bit wider on my screen. ;P
               | 
               | > "Currying is a subset of partial application" is true
               | in the same sense as if "subtraction is a subset of
               | addition". Sure, you can subtract negative values to have
               | addition and add negative values to have subtraction, but
               | that can't be your point, is it?
               | 
               | I'm not sure I understand that analogy to say it's my
               | point.
               | 
               | Are you saying you would behave condescendingly toward
               | someone who said "subtraction is a subset of addition"
               | too?
               | 
               | > Please keep it on topic.
               | 
               | The topic skybrian started was derailed when you decided
               | to "correct" him because you didn't make any effort to
               | understand what he was trying to say. Your behavior is
               | the topic now because your behavior became a problem.
        
               | whilenot-dev wrote:
               | Neither of your examples does curry a function. The first
               | one partially applies it (the Haskell way), the second
               | one wraps the partial application in a redundant lambda
               | (Haskell again). I think that's where the confusion is,
               | let me also try to use the Haskell syntax...
               | 
               | This is an _add_ function with two arguments:
               | add :: (Int, Int) -> Int       add (x, y) = x + y
               | 
               | At the moment it can not be partially applied, so let's
               | curry it:                 -- addCurr :: Int -> Int -> Int
               | addCurr = curry add
               | 
               | Now let's partially apply it for an _increment_ function:
               | -- increment :: Int -> Int       increment = addCurr 1
               | 
               | > When I curry `add` above, isn't the argument 1
               | contained within the context of the partially applied
               | function?
               | 
               | You didn't curry it, you partially applied it. This was
               | possible, because your function _add_ was already
               | curried.
        
               | kerkeslager wrote:
               | Okay, that's a fair critique, let it not be said I can't
               | admit when I'm wrong. I shouldn't have used `curry` as a
               | verb there, as my point was about the concept of currying
               | rather than the specific curry function.
               | 
               | My mistake there doesn't negate my larger point, but I've
               | gotta go touch grass.
        
               | whilenot-dev wrote:
               | > _no one in this comment chain is confused about what
               | currying or partial application are_
               | 
               | I can't agree here either... I know it from my own
               | experience when i first learned about currying and
               | partial application.
        
               | kerkeslager wrote:
               | It does not appear to me that skybrian is first learning
               | about currying and partial application, and I'm not first
               | learning about currying or partial application in this
               | thread either.
               | 
               | Your play at humility that you didn't understand currying
               | and partial application in the past kinda falls flat if
               | you then go on to less-humbly assume that everyone else
               | in the conversation is stuck where you once were.
        
               | threatofrain wrote:
               | I thought it was somewhat the reverse -- that the
               | original motivation was to simulate multi-arity functions
               | in a setting that only allows unary functions.
        
               | bPspGiJT8Y wrote:
               | I don't really understand the distinction between "multi-
               | arity" and "unary" functions. In my mental model, all
               | functions are unary, it's just that in "traditional",
               | non-FP languages it's more common to pack arguments into
               | tuples. That is `(a, b, c) => ...` in JS is a _unary_
               | function which takes a tuple of 3 items. But the thing
               | is, FP has tuples too (and in Haskell, with pretty much
               | the same syntax), it 's just that most of the time
               | arguments aren't packed into tuples.
               | 
               | So could you elaborate to me where the distinction lies
               | here and why is there a need to "simulate" things?
        
               | jraph wrote:
               | Say you are dealing with a callback mechanism that takes
               | a function and (only) one value to pass:
               | function c(f, v) { ... f(v) ... }
               | 
               | But what you actually need this callback mechanism to
               | call is a function that takes two arguments. No default
               | values.                   function f2(a, b) { ... }
               | 
               | There's no way in js that c is going to call f2 and set
               | its b parameter to something. You can't set v to be a
               | tuple so b will be filled:                   c(f2,
               | "value") => b will be undefined.              c(f2, [1,
               | 2]) => a will be set to [1, 2].
               | 
               | you need to wrap f2 for this when calling c:
               | function wrapf2(b) { return f2("fixed a", b) }
               | 
               | Or you can make c deconstruct v, or use apply:
               | function c(f, v) { ... f(...v) ...  }         function
               | c(f, v) { ... f.apply(null, v) ...  }
               | 
               | but then v always needs to be an array:
               | c(f, [1])         c(f, [1, 2])              c(f, 1) //
               | fails
               | 
               | This is why in Javascript, multi-arity is different from
               | unary with tuples. JS doesn't have tuples as a primitive
               | / first-class type anyway. In some math notations we
               | don't make the difference, but in most programming
               | languages there's a distinction. Your model where
               | functions are always unary but can take tuples, and (v)
               | is the same as v, is correct (and useful when the
               | distinction doesn't matter and is just annoying to deal
               | with), but doesn't match the way many programming
               | languages actually work. I believe even in most
               | functional programming languages, (v) is different from
               | v, you also said it.
               | 
               | In assembly, different parameters are in fully separate
               | registers or stack entries. Interestingly, you could
               | imagine representing tuples as C structs (that can have
               | only one member) and always pass structs, and passing a
               | one-member struct will actually have the exact same
               | effect as passing a value of a primitive type.
        
             | Hirrolot wrote:
             | Currying and partial application are dualities (think of
             | them as function introduction/function elimination), so
             | neither subsumes the other one.
             | 
             | However, I agree with the wording of the original comment:
             | when you _pass_ some arguments to a constructor and then to
             | a method, this is (at least conceptually) partial
             | application.
        
               | kerkeslager wrote:
               | > Currying and partial application are dualities (think
               | of them as function introduction/function elimination),
               | so neither subsumes the other one.
               | 
               | Could you say more? I'm not sure I understand what you're
               | saying here.
        
               | skybrian wrote:
               | You can think of them as equivalent ways to write the
               | same code. There are mechanical rewrites to go from one
               | to the other, and these rewrites work in any direction.
               | Which way is more readable depends on circumstances.
               | 
               | When a function needs a lot of parameters to do a
               | calculation, these parameters can be arbitrarily grouped
               | into records and supplied in any order. Languages have
               | convenient syntaxes for certain ways of grouping
               | parameters together.
               | 
               | For example, a closure creates a group of parameters in
               | the form of a function.
        
             | xeonmc wrote:
             | Currying wraps, partial application unwraps.
        
         | ReleaseCandidat wrote:
         | 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;           }           }          }
        
           | kerkeslager wrote:
           | 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?
        
             | ReleaseCandidat wrote:
             | > but isn't communicating exactly the way you would have
             | communicated it?
             | 
             | My answers are almost always less intented for the person
             | who's post I'm responding to, but more for a general
             | audience. As others have already posted the part about
             | "currying isn't partial application", and I mainly wanted
             | to add a (hopefully) understanable example.
             | 
             | And I would say that taking the OP's post as if he doesn't
             | exactly know, what currying means in comparison to partia
             | application _is_ the more favourable interpretation of
             | their post.
        
               | kerkeslager wrote:
               | > My answers are almost always less intented for the
               | person who's post I'm responding to, but more for a
               | general audience
               | 
               | Same. I don't expect you're going to admit your mistake,
               | but I'm pointing it out because the general audience is
               | the sort of audience who is prone to this mistake (which
               | I only know because I myself have been corrected for
               | making this mistake).
               | 
               | > As others have already posted the part about "currying
               | isn't partial application", and I mainly wanted to add a
               | (hopefully) understanable example.
               | 
               | And in doing so, you're making the same mistake as those
               | other posters, by assuming skybrian doesn't know what
               | currying and partial application are. You're not doing
               | your audience any favors by painting skybrian's post as
               | if he said something wrong.
               | 
               | > And I would say that taking the OP's post as if he
               | doesn't exactly know, what currying means in comparison
               | to partia application _is_ the more favourable
               | interpretation of their post.
               | 
               | How so?
        
               | luc4 wrote:
               | It's ironic that your responses come off as much more
               | rude and condescending than the comment in question.
        
               | skybrian wrote:
               | I'm not offended. I was too brief and I can see how my
               | post could be misleading.
        
             | ghusbands wrote:
             | It is clear that that poster does not understand what
             | currying is, as defined in the first line of the article
             | and easily found online; also, downthread [1], nor do you.
             | Currying is when you turn a function of multiple parameters
             | into nested functions of single parameters. Partially
             | applying a curried function is not called currying.
             | 
             | Though if enough people share the confusion, I guess it
             | becomes an alternative definition. Language evolves.
             | 
             | Also, in any case, a constructed object is nothing like a
             | partially applied function, so the parallel drawn is not
             | useful. It seems entirely reasonable in this case to assume
             | that the poster does not have sufficient experience or
             | knowledge.
             | 
             | [1] https://news.ycombinator.com/item?id=39959651
        
               | skybrian wrote:
               | I can see how someone might think I don't know the
               | difference, but it's rather that I don't think the
               | details are important. They are both ways of partially
               | applying a function. I wanted to demystify currying and
               | show the equivalence, but I was too brief about it.
               | 
               | A method is a function with convenient syntax. Some of
               | its arguments come from the "this" parameter. If the
               | object is immutable and the method has no side-effects,
               | it's a pure function.
               | 
               | Converting a function to an immutable class with a method
               | is a mechanical refactoring that I sometimes do, when the
               | class has an intuitive name. The code does the same
               | thing.
        
         | Hirrolot wrote:
         | 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.
        
           | skybrian wrote:
           | 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.
        
       | kerkeslager wrote:
       | 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 wrote:
         | 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.
        
           | kerkeslager wrote:
           | 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.
        
         | kamray23 wrote:
         | 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
         | 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 wrote:
           | > 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 wrote:
             | Hi, 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.
        
               | skybrian wrote:
               | They say that array languages are pretty readable once
               | you get used to them, too? But you're drastically
               | limiting your audience. Part of readability is writing
               | for people who aren't as fluent as you are.
               | 
               | Expert jargon can _sometimes_ be useful, but it often
               | obscures things that would be pretty simple if written
               | some other way.
               | 
               | With Haskell there's a tension between saying "I only
               | care about writing for other expert programmers" and
               | "more people should learn Haskell." The idioms are part
               | of the turnoff.
        
         | Simon_ORourke wrote:
         | > 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.
        
       | boxed wrote:
       | 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.
        
       | fxj wrote:
       | 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.
       | 
       | 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
        
         | boxed wrote:
         | 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.
        
           | fxj wrote:
           | 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
           | 1000000 >> ps(
           | ps(kh>>op("(x**2+y**2)**0.5<1")@rlu<<kh)>>S*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
        
         | skybrian wrote:
         | 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.
         | 
         | [1] https://journal.stuffwithstuff.com/2016/06/16/long-names-
         | are...
        
           | whateveracct wrote:
           | I read Haskell code structurally and don't pronounce symbols
           | in my head.
           | 
           | 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)
        
       | danmur wrote:
       | Haskell always makes me hungry
        
         | jraph wrote:
         | With a bit of ginger, some lemon and perhaps some coco, it's
         | excellent.
        
       | indigosun wrote:
       | I 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
        
       ___________________________________________________________________
       (page generated 2024-04-07 23:02 UTC)