Live data from Hacker News

Currying

wiki.haskell.org

81–90 of 134 posts

Re: Currying

#81

Earlier quoted context omitted.

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)…

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 tha…

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

Re: Currying

#82

Earlier quoted context omitted.

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 tha…

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 defin…

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.

Re: Currying

#83

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.

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…

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.

Re: Currying

#84
post #66

Earlier quoted context omitted.

> 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 "curry…

It's ironic that your responses come off as much more rude and condescending than the comment in question.

I’m not offended. I was too brief and I can see how my post could be misleading.

Re: Currying

#85

Earlier quoted context omitted.

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.

> 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.

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.

Re: Currying

#86
post #73

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/@jest…

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...

Re: Currying

#87

Earlier quoted context omitted.

> 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…

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 cod…

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.

Re: Currying

#88
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

Re: Currying

#89
post #73

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/@jest…

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 la…

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)

Re: Currying

#90

Earlier quoted context omitted.

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…

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…

You don't have to use type inference everywhere.

But I don't think this is a problem in practice for whatever reason. Haskell does have a lot of "big scary type error, wtf" type problems, but not from currying. Or at least it did have when I last used it 5 years ago, it may have improved in the compiler error message front.

Also it is better than JS anyway (low bar):

    >  function add(a,b) {return a + b}; add(1);

    
Post reply on HN