NHacker Next
  • new
  • past
  • show
  • ask
  • show
  • jobs
  • submit
A Case Against Currying (emi-h.com)
paldepind2 10 minutes ago [-]
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 often the case, being explicit is clearer. If you see something like

    let f = foobinade a b
in a curried language then you don't immediately know if `f` is the result of foobinading `a` and `b` or if `f` is `foobinade` partially applied to some of its arguments. Without currying you'd either write

    let f = foobinade(a, b)
or

    let f = foobinade(a, b, $) // (using the syntax in the blog post)
and now it's immediately explicitly clear which of the two cases we're in.

This clarity not only helps humans, it also help compilers give better error messages. In a curried languages, if a function is mistakenly applied to too few arguments then the compiler can't always immediately detect the error. For instance, if `foobinate` takes 3 arguments, then `let f = foobinade a b` doesn't give rise to any errors, whereas a compiler can immediately detect the error in `let f = foobinade(a, b)`.

A syntax for partial application offers the same practical benefits of currying without the downsides (albeit loosing some of the theoretical simplicity).

jwarden 5 minutes ago [-]
Here’s an article I wrote a while ago about a hypothetical language feature I call “folded application”, that makes parameter-list style and folded style equivalent.

https://jonathanwarden.com/implicit-currying-and-folded-appl...

recursivecaveat 38 minutes ago [-]
Currying was recently removed from Coalton: https://coalton-lang.github.io/20260312-coalton0p2/#fixed-ar...
emih 19 minutes ago [-]
Thanks for sharing, interesting to see that people writing functional languages also experience the same issues in practice. And they give some reasons I didn't think about.
Pay08 33 minutes ago [-]
I'm biased here since the easy currying is by far my favourite feature in Haskell (it always bothers me that I have to explicitly create a lamba in Lisps) but the arguments in the article don't convince me, what with the synctactic overhead for the "tuple style".
lukev 27 minutes ago [-]
I'd got a step further and say that in business software, named parameters are preferable for all but the smallest functions.

Using curried OR tuple arg lists requires remembering the name of an argument by its position. This saves room on the screen but is mental overhead.

The fact is that arguments do always have names anyway and you always have to know what they are.

layer8 1 minutes ago [-]
I want to agree, but there is the tension that in business code, what you pass as arguments is very often already named like the parameter, so having to indicate the parameter name in the call leads to a lot of redundancy. And if you’re using types judiciously, the types are typically also different, hence (in a statically-typed language) a reduced risk of passing the wrong parameter.

Maybe there could be a rule that parameters have to be named if their type doesn’t already disambiguate them, and if there isn’t some concordance between the naming in the argument expression and the parameter. But the ergonomics of that might be annoying as well.

layer8 13 minutes ago [-]
I completely agree. Giving the first parameter of a function special treatment only makes sense in a limited subset of cases, while forcing an artificial asymmetry in the general case that I find unergonomic.
leoc 23 minutes ago [-]
Right. Currying as the default means of passing arguments in functional languages is a gimmick, a hack in the derogatory sense. It's low-level and anti-declarative.
messe 41 minutes ago [-]
What benefit does drawing a distinction between parameter list and single-parameter tuple style bring?

I'm failing to see how they're not isomorphic.

Kambing 28 minutes ago [-]
They are isomorphic in the strong sense that their logical interpretations are identical. Applying Curry-Howard, a function type is an implication, so a curried function with type A -> B -> C is equivalent to an implication that says "If A, then if B, then C." Likewise, a tuple is a conjunction, so a non-curried function with type (A, B) -> C is equivalent to the logic statement (A /\ B) -> C, i.e., "If A and B then C." Both logical statements are equivalent, i.e., have the same truth tables.

However, as the article outlines, there are differences (both positive and negative) to using functions with these types. Curried functions allow for partial application, leading to elegant definitions, e.g., in Haskell, we can define a function that sums over lists as sum = foldl (+) 0 where we leave out foldl's final list argument, giving us a function expecting a list that performs the behavior we expect. However, this style of programming can lead to weird games and unweildy code because of the positional nature of curried functions, e.g., having to use function combinators such as Haskell's flip function (with type (A -> B -> C) -> B -> A -> C) to juggle arguments you do not want to fill to the end of the parameter list.

messe 23 minutes ago [-]
Please see my other comment below, and maybe re-read the article. I'm not asking what the difference is between curried and non-curried. The article draws a three way distinction, while I'm asking why two of them should be considered distinct, and not the pair you're referring to.
recursivecaveat 27 minutes ago [-]
Probably just that having parameter-lists as a specific special feature makes them distinct from tuple types. So you may end up with packing/unpacking features to convert between them, and a function being generic over its number of parameters is distinct from it being generic over its input types. On the other hand you can more easily do stuff like named args or default values.
emih 32 minutes ago [-]
That's a fair point, they are all isomorphic.

The distinction is mostly semantic so you could say they are the same. But I thought it makes sense to emphasize that the former is a feature of function types, and the latter is still technically single-parameter.

I suppose one real difference is that you cannot feed a tuple into a parameter list function. Like:

fn do_something(name: &str, age: u32) { ... }

let person = ("Alice", 40);

do_something(person); // doesn't compile

Pay08 37 minutes ago [-]
The tuple style can't be curried (in Haskell).
messe 34 minutes ago [-]
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.

disconcision 21 minutes ago [-]
all three are isomorphic. but in some languages if you define a function via something like `function myFun(x: Int, y: Bool) = ...` and also have some value `let a: (Int, Bool) = (1, true)` it doesn't mean you can call `myFun(a)`. because a parameter list is treated by the language as a different kind of construct than a tuple.
Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact
Rendered at 14:24:40 GMT+0000 (Coordinated Universal Time) with Vercel.