Live data from Hacker News

A case against currying

emi-h.com

61–70 of 135 posts

Re: A case against currying

#61
post #26
post #5

Earlier quoted context omitted.

That's not what I'm talking about. The article draws a three way distinction between curried style (à la Haskell), tuples and parameter list. I'm talking about the distinction it claims exists between the latter two.

A language which truly treats an argument list as a tuple can support this: args = (a, b, c) f args …and that will have the effect of binding a, b, and c as arguments in the called function. In fact many “scripting” languages, like Javascript and Python, support something close to this using their array type. If you squint, you can see them as languages whose functions take a single argument that is equivalent to an…

Rust definitely should. C++s std::initializer_list is a great tool and you wouldn't need macros for variadic functions anymore.

Re: A case against currying

#62
post #59
post #55

Earlier quoted context omitted.

One slightly contrived example would be if you had a function that returned the point of a set closest to another given point. getClosest :: Set Point -> Point -> Point You could imagine getClosest build a quadtree internally and that tree wouldn't depend on the second argument. I say slightly contrived because I would probably prefer to make the tree explicit if this was important. Another example would be if you we…

I think we need to see a few non-contrived examples, because i think in every case where you might take advantage of currying like this, you actually want to make it explicit, as you say. The flip side of your example is that people see a function signature like getClosest, and think it's fine to call it many times with a set and a point, and now you're building a fresh quadtree on each call. Making the staging expli…

Consider a function like ‘match regex str’. While non-lazy languages may offer an alternate API for pre-compiling the regex to speed up matching, partial evaluation makes that unnecessary.

Re: A case against currying

#63
if you don't find currying essential you haven't done pointfree enough. If you haven't done pointfree enough you haven't picked equational reasoning yet, and it's the thing that holds you back in your ability to read abstractions easily, which in turn guides your arguments on clarity.

Re: A case against currying

#64
post #32

Earlier quoted context omitted.

Well, I totally disagree with this. One of the main benefits of currying is the ability to chain function calls together. For example, in F# this is typically done with the |> operator: let result = input |> foobinade a b |> barbalyze c d Or, if we really want to name our partial function before applying it, we can use the >> operator instead: let f = foobinade a b >> barbalyze c d let result = f input Requiring an e…

You can still do this though: let result = (barbalyze(c, d, $) . foobinade(a, b, $)) input Or if you prefer left-to-right: let result = input |> foobinade(a, b, $) |> barbalyze(c, d, $) Maybe what isn't clear is that this hole operator would bind to the innermost function call, not the whole statement.

This is essentially how Mathematica does it: the sugar `Foo[x,#,z]&` is semantically the same as `Function[{y}, Foo[x,y,z]]`. The `&` syntax essentially controls what hole belongs where.

Re: A case against currying

#65

I completely agree with the points in this article and have come to the same conclusion after using languages that default to unary curried functions. > I'd also love to hear if you know any (dis)advantages of curried functions other than the ones mentioned. I think it fundamentally boils down to the curried style being _implicit_ partial application, whereas a syntax for partial application is _explicit_. And as if…

The functional programming take is that “the result of foobinade-ing an and b” IS “foobinade applied to two of its arguments”. The application is not some syntactic pun or homonym that can refer to two different meanings—those are the same meaning.

Re: A case against currying

#66
post #58
post #55

Earlier quoted context omitted.

One slightly contrived example would be if you had a function that returned the point of a set closest to another given point. getClosest :: Set Point -> Point -> Point You could imagine getClosest build a quadtree internally and that tree wouldn't depend on the second argument. I say slightly contrived because I would probably prefer to make the tree explicit if this was important. Another example would be if you we…

Those are nice examples, thanks. I was imagining you might achieve this optimization by inlining the function. So if you have getClosest(points, p) = findInTree(buildTree(points), p) And call it like myPoints = [...] map (getClosest(myPoints, $)) myPoints Then the compiler might unfold the definition of getClosest and give you map (\p -> findInTree(buildTree(myPoints), p)) myPoints Where it then notices the first par…

I didn't consider inlining but I believe you're correct, you could regain the optimisation for this example since the function is non-recursive and the application is shallow. The GHC optimisation I had in mind is like the opposite of inlining, it factors out a common part out of a lambda expression that doesn't depend on the variable.

I don't believe inlining can take you to the exact same place though. Thinking about explicit INLINE pragmas, I envision that if you were to implement your partial function application sugar you would have to decide whether the output of your sugar is marked INLINE and either way you choose would be a compromise, right? The compromise with Haskell and curried functions today is that the programmer has to consider the order of arguments, it only works in one direction but on the other hand the optimisation is very dependable.

Re: A case against currying

#67

There are good ideas in functional languages that other languages have borrowed, but there are bad ideas too: currying, function call syntax without parentheses, Hindley-Milner type inference, and laziness by default (Haskell) are experiments that new languages shouldn’t copy.

I believe one of the main reasons that F# hasn't never really taken off is that Microsoft isn't afraid to borrow the good parts of F# to C#. (They really should've ported discriminated unions though)

Currently DUs are slated for the next version of c# releasing end of this year. However last I knew they only come boxed which at least to me partly defeats the point of having them (being able to have multiple types inline because of the way they share memory and only have a single size based on compiler optimizations).

Re: A case against currying

#68
post #30

I like currying because it's fun and cool, but found myself nodding along throughout the whole article. I've taken for granted that declaring and using curried functions with nice associativity (i.e., avoiding lots of parentheses) is as ergonomic as partial application syntax gets, but I'm glad to have that assumption challenged. The "hole" syntax for partial application with dollar signs is a really creative alterna…

Clojure CL as well have macros that let you thread results from call to call, but you could argue that's cheating because of how flexible Lisp syntax is.

Re: A case against currying

#69
post #41

One "feature of currying" in Haskell that isn't mentioned in the fine article is that parts of the function may not be dependent on the last argument(s) and only needs to be evaluated once over many application of the last argument(s) which can be very useful when partially applied functions are passed to higher-order functions. Functions can be done explicitly written to do this or it can be achieved through compile…

> explicitly written to do this

In that case I want the signature of "this function pre-computes, then returns another function" and "this function takes two arguments" to be different, to show intent.

> achieved through compiler optimisation

Haskell is different in that its evaluation ordering allows this. But in strict evaluation languages, this is much harder, or even forbidden by language semantics.

Here's what Yaron Minsky (an OCaml guy) has to say:

> starting from scratch, I’d avoid partial application as the default way of building multi-argument functions.

https://discuss.ocaml.org/t/reason-general-function-syntax-d...

Re: A case against currying

#70

Earlier quoted context omitted.

Well, I totally disagree with this. One of the main benefits of currying is the ability to chain function calls together. For example, in F# this is typically done with the |> operator: let result = input |> foobinade a b |> barbalyze c d Or, if we really want to name our partial function before applying it, we can use the >> operator instead: let f = foobinade a b >> barbalyze c d let result = f input Requiring an e…

For pipelines in any language, putting one function call per line often works well. Naming the variables can help readability. It also makes using a debugger easier: let foos = foobinate(a, b, input) let bars = barbakize(c, d, foos) Other languages have method call syntax, which allows some chaining in a way that works well with autocomplete.

> Naming the variables can help readability

It can, or it can't; depending on the situation. Sometimes it just adds weight to the mental model (because now there's another variable in scope).

Post reply on HN