Live data from Hacker News

Currying

wiki.haskell.org

111–120 of 134 posts

Re: Currying

#111

Earlier quoted context omitted.

If you look at how people actually write PureScript, which is much like Haskell, but with anonymous records, you can see that they're not actually used 99% of the time.

Could you share an example?

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

Re: Currying

#112

Earlier quoted context omitted.

If you look at how people actually write PureScript, which is much like Haskell, but with anonymous records, you can see that they're not actually used 99% of the time.

Could you share an example?

https://github.com/purescript/documentation/blob/master/lang...

Re: Currying

#113
post #109
post #62

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.

Somewhat related - be wary of implicit parameter passing in function pipelines. For example, Try ['10', '10', '10'].map(Number.parseInt) in your browser console. What's actually being called is: Number.parseInt('10', 0) Number.parseInt('10', 1) Number.parseInt('10', 2)

Typing fixes this, and anonymous records make it even better.

JavaScript lacks typing, and then they decided on a weird signature for the callback...

Re: Currying

#114

Earlier quoted context omitted.

Thank you for bringing the topic back on track! Your example is currying in a manual fashion, so two questions remain for me: 1. How would you tell the compiler to do it for you, as a function transformation? 2. How would you curry a function with 3+ arguments?

I can answer both questions with a sprinkle of template magic: #include #include template class Curry { std::function fn; public: Curry(std::function fn): fn(fn) {}; Curry operator()(First arg) { return Curry {[this, arg](Rest... args) { return this->fn(arg, args...); }}; } }; template class Curry { std::function fn; public: Curry(std::function fn): fn(fn) {}; Ret operator()(Arg arg) { return this->fn(arg); } }; int…

I'm speechless, thank you very much! =)

Re: Currying

#115
post #109

Earlier quoted context omitted.

Somewhat related - be wary of implicit parameter passing in function pipelines. For example, Try ['10', '10', '10'].map(Number.parseInt) in your browser console. What's actually being called is: Number.parseInt('10', 0) Number.parseInt('10', 1) Number.parseInt('10', 2)

Typing fixes this, and anonymous records make it even better. JavaScript lacks typing, and then they decided on a weird signature for the callback...

Unfortunately typing doesn't fix this. The 2nd callback function argument of Array.map is (index:number), and the 2nd argument of Number.parseInt is (radix:number).

It's a very nasty issue to debug.

Re: Currying

#116

Earlier quoted context omitted.

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

It's not really saying "only expert programmers" though, is it? It's people who know Haskell, which by coincidence happens to be overzealous undergraduates and a certain subset of experienced programmers. FP is a paradigm among many, its basics somewhat predate (or since it's so close, co-date?) more imperative descriptions of computation. That we mostly use and as such mostly teach beginners with procedural language…

It’s true that readability is culture-specific, that if the culture were different and people learned different things then different languages would be more readable. But I still think there are differences between languages in the sense of how much you can understand without knowing the definition of every term.

For example, if you’re looking at Lisp code and you don’t know whether an outer term is a function, macro, or special form, you really don’t understand anything inside it except at the most superficial level. It might as well be JSON. Macros aren’t marked, so any unknown term might be a macro. (Knowing the surface syntax is still helpful, though. Well-known syntaxes for data are useful.)

With Forth it’s pretty bad, too. Not knowing what a single word does means that you don’t know what’s on the stack afterwards.

A Unix pipeline is a bit more orderly since you know that there are streams of bytes. You know the command names and the arguments to each command. But if you don’t know what a command does, you don’t know much at all about what the data looks like after that point.

I find currying and point-free programming to be pretty opaque because I can’t tell where the function calls are or how many arguments each function takes from usage. I don’t know what the dataflow looks like. It seems like you need to know some precedence rules too?

Languages with conventional function call syntax, augmented with named parameters, seem better. I can tell where the function calls are and where the inline functions are. Augmented with reasonable names for temporaries, I can make reasonable guesses about what the code does.

These syntax concerns seem independent of whether it’s a functional or imperative language? Making reasonable guesses is what we do when we read pseudocode. Maybe what I’m saying is that some syntaxes seem better for pseudocode than others, and I like languages that look like pseudocode better.

I suspect that these syntax differences also have an effect on how good the error messages are when you screw up.

Re: Currying

#117

Earlier quoted context omitted.

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

No partial application and currying are not both ways of partially applying a function. Partial application is a way of partially applying a function. I have to concur with grandparent and also with user https://news.ycombinator.com/user?id=whilenot-dev in a sibling subthread.

We can say that when a function is curried (i.e. prepared or converted to curried form) then it supports partial application without any further transformation. In fact it happens naturally whenever the function is invoked.

E.g. if we have (lambda (x y z) (+ x y z)) converted into the curried form (lambda (x) (lambda (y) (lambda (z) (+ x y z)))), then while we do not yet have partial application, we can then partially apply just by passing arguments.

That is to say, we are partially applying in the abstract sense; if we pretend we still have a (lambda (x y z) ...) in our abstract semantics, then when we pass a value for x to the curried function, we then have effectively partially applied this abstract function, resulting in something that needs just the (y z) arguments now.

Languages that support implicit partial application erase the difference between f x y and (f x) y. That is to say, the programmer doesn't have to know whether f x y is a call two a function f with two arguments, or whether there is a partial application (f x) which binds the x argument, followed by an application of that resulting function to argument y.

Now if the language substrate is based on one-argument functions, under the hood, so that all functions with two or more arguments are implicitly transformed into one argument functions by currying, then partial application is "free", so to speak. The syntax may let you write f x y to call a function of two arguments, but the implementation is actually doing (f x) y.

Thus currying can support partial application in a similar way to how transformation of a program to CPS (continuation passing style) supports continuations. Partial application is "free" in a curried program similarly to how continuations are "free" in a CPSed program. Under CPS, you already have the return continuation as a hidden argument, so call/cc does nothing more than reveal what is hidden. Under currying, you already have a lambda that takes just one argument, and gives you a lambda that takes the next one.

Re: Currying

#118

Earlier quoted context omitted.

It's not really saying "only expert programmers" though, is it? It's people who know Haskell, which by coincidence happens to be overzealous undergraduates and a certain subset of experienced programmers. FP is a paradigm among many, its basics somewhat predate (or since it's so close, co-date?) more imperative descriptions of computation. That we mostly use and as such mostly teach beginners with procedural language…

It’s true that readability is culture-specific, that if the culture were different and people learned different things then different languages would be more readable. But I still think there are differences between languages in the sense of how much you can understand without knowing the definition of every term. For example, if you’re looking at Lisp code and you don’t know whether an outer term is a function, macr…

> you really don’t understand anything inside it except at the most superficial level

This is true, but:

- the name of the operator is typically a word that you can easily search for in the documentation or on the web, or with "jump to definition" in your editor, if it's something locally defined in the source tree.

- you usually understand the shape of what is inside it. You know what parts of the program you are looking at are arguments to that mysterious operator and which are not. If asked which expression is the third argument of that operator, you can easily find it and know where it begins and ends.

Re: Currying

#119

Earlier quoted context omitted.

No partial application and currying are not both ways of partially applying a function. Partial application is a way of partially applying a function. I have to concur with grandparent and also with user https://news.ycombinator.com/user?id=whilenot-dev in a sibling subthread.

We can say that when a function is curried (i.e. prepared or converted to curried form) then it supports partial application without any further transformation. In fact it happens naturally whenever the function is invoked. E.g. if we have (lambda (x y z) (+ x y z)) converted into the curried form (lambda (x) (lambda (y) (lambda (z) (+ x y z)))), then while we do not yet have partial application, we can then partiall…

I think when calling a curried function it isn't easy to differentiate between a simple function application and a partial (function) application. I would make the assumption that as long as a higher-order function is called, then what we're actually doing is a function transformation and thus a partial application - when we applied all arguments except the last one, then finally, when the last argument of a first-order function (= the partially applied higher-order function) is applied, then this function can not be transformed anymore and is simply just evaluated. I'm sure there's also a difference in lazy evaluated languages and others in there, but this is beyond my competence.

Re: Currying

#120

Earlier quoted context omitted.

Typing fixes this, and anonymous records make it even better. JavaScript lacks typing, and then they decided on a weird signature for the callback...

Unfortunately typing doesn't fix this. The 2nd callback function argument of Array.map is (index:number), and the 2nd argument of Number.parseInt is (radix:number). It's a very nasty issue to debug.

If there were typing, there would be no reason to not use newtypes[0] for radix and index. These values should only be explicitly unwrapped to plain numbers.

It would look like this

    newtype Radix = Radix { radixToWord :: Word }
    newtype Index = Index { indexToWord :: Word }
[0]: https://wiki.haskell.org/Newtype
Post reply on HN