Top
Best
New

Posted by emih 9 hours ago

A case against currying(emi-h.com)
76 points | 99 commentspage 3
calf 3 hours ago|
What's the steelman argument though? Why do languages like Haskell have currying? I feel like that is not set out clearly in the argument.
emih 2 hours ago|
Mathematically it's quite pretty, and it gives you elegant partial application for free (at least if you want to partially apply the first N arguments).
kajaktum 7 hours ago||
I feel like not having currying means your language becomes semantically more complicated because where does lambdas come from?
01HNNWZ0MV43FF 7 hours ago||
I've never ever run into this. I haven't seen currying or partial application since college. Am I the imperative Blub programmer, lol?
messe 8 hours 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 8 hours 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 8 hours 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.
Kambing 3 hours ago||
Apologies, I was focused on the usual pairing in this space and not the more subtle one you're talking about. As others have pointed out, there isn't really semantic a difference between the two. Both approaches to function parameters produce the same effect. The differences are purely in "implementation," either theoretically or in terms of systems-building.

From a theoretical perspective, a tuple expresses the idea of "many things" and a multi-argument parameter list expresses the idea of both "many things" and "function arguments." Thus, from a cleanliness perspective for your definitions, you may want to separate the two, i.e., require function have exactly one argument and then pass a tuple when multiple arguments are required. This theoretical cleanliness does result in concrete gains: writing down a formalism for single-argument functions is decidedly cleaner (in my opinion) than multi-argument functions and implementing a basic interpreter off of this formalism is, subsequently, easier.

From a systems perspective, there is a clear downside in this space. If tuples exist on the heap (as they do for most functional languages), you induce a heap allocation when you want to pass multiple arguments! This pitfall is evident with the semi-common beginner's mistake with OCaml algebraic datatype definitions where the programmer inadvertently wraps the constructor type with parentheses, thereby specifying a constructor of one-argument that is a tuple instead of a multi-argument constructor (see https://stackoverflow.com/questions/67079629/is-a-multiple-a... for more details).

emih 8 hours 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

recursivecaveat 8 hours 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.
layer8 7 hours ago|||
The parameter list forces the individual arguments to be visible at the call site. You cannot separate the packaging of the argument list from invoking the function (barring special syntactic or library support by the language). It also affects how singleton tuples behave in your language.

The article is about programmer ergonomics of a language. Two languages can have substantially different ergonomics even when there is a straightforward mapping between the two.

rocqua 7 hours ago|||
It's not that they are meaningfully different. It's just acknowledging if you really want currying, you can say 'why not just use a single parameter of tuple type'.

Then there's an implication of 'sure, but that doesn't actually help much if it's not standar' and then it's not addressed further.

Pay08 8 hours ago|||
The tuple style can't be curried (in Haskell).
messe 8 hours 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 8 hours 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.
antonvs 7 hours ago|||
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 array. At an internal implementation level this equivalence can be messy, though.

Lower level languages like C and Rust tend not to support this.

Pay08 6 hours ago||
Rust definitely should. C++s std::initializer_list is a great tool and you wouldn't need macros for variadic functions anymore.
naasking 7 hours ago||
Presumably creating a different class for parameter lists allows you to extend it with operations that aren't natural to tuples, like named arguments.
instig007 5 hours ago||
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.
talkingtab 7 hours ago||
[flagged]
emih 7 hours ago||
It's not that serious :)
raincole 7 hours ago||
Could you explain how this comment is relevant?
leoc 8 hours 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.
hrmtst93837 2 hours ago|
[dead]
kubb 7 hours ago||
The article lists two arguments against Currying:

  1) "performance is a bit of a concern"
  2) "curried function types have a weird shape"
2 is followed by single example of how it doesn't work the way the author would expect it to in Haskell.

It's not a strong case in my opinion. Dismissed.

mkprc 7 hours ago|
Prior to this article, I didn't think of currying as being something a person could be "for" or "against." It just is. The fact that a function of multiple inputs can be equivalently thought of as a function of a tuple can be equivalently thought of as a composite of single-input functions that return functions is about cognition, and understanding structure, not code syntax.
kevincox 7 hours ago||
But it is about code syntax. Languages like Haskell make it part of the language by only supporting single-argument functions. So currying is the default behaviour for programmers.

I think you are focusing on the theoretical aspect of partial application and missing the actual argument of the article which having it be the default, implicit way of defining and calling functions isn't a good programming interface.

bbkane 7 hours ago||
Similar to how lambda calculus "just is" (and it's very elegant and useful for math proofs), but nobody writes non-trivial programs in it...
tromp 3 hours ago||
Make that almost nobody.

I wrote a non-trivial lambda program [1] which enumerates proofs in the Calculus of Constructions to demonstrate [2] that BBλ(1850) > Loader's Number.

[1] https://github.com/tromp/AIT/blob/master/fast_growing_and_co...

[2] https://codegolf.stackexchange.com/questions/176966/golf-a-n...

AnimalMuppet 3 hours ago||
I'm a programmer, not a computer scientist. The equivalence is a computer science thing. They are logically equivalent in theoretical computer science. Fine.

They are not equally easy for me to use when I'm writing a program. So from a software engineering perspective, they are very much not the same.