Earlier quoted context omitted.
> Unless you have the signatures of foobinade and foobinadd memorized, you have no way to tell that f is a curried function and g is an actual result. Yes, but the exact FP idea here is that this distinction is meaningless; that curried functions are "actual results". Or rather, you never have a result that isn't a function; `0` and `lambda: 0` (in Python syntax) are the same thing. It does, of course, turn out that…
It’s not at all clear or the same to the new reader of the code.
A case against currying
131–135 of 135 posts
Re: A case against currying
#132What's the steelman argument though? Why do languages like Haskell have currying? I feel like that is not set out clearly in the argument.
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).
More plausible is that Haskell designers recognized that Currying is a fundamental phenomenon of the lambda calculus so it needed some kind of primitive syntax for it. I'm not an expert but that is the most reasonable supposition for a rationale to start with. One can then argue if the syntax is good or not, but to do away with currying entirely is changing the premise of recognizing fundamental properties of Turing-complete functional programming language paradigms. It's not about prettiness, it's about the science.
Re: A case against currying
#133From the article: length = foldr (+) 0 . map (const 1) length2d = foldr (+) 0 . map length -- and the proposed syntax the author calls more readable: length = foldr((+), 0, $) . map(const(1), $) length2d = foldr((+), 0, $) . map(length, $) But I don't understand. Why does the author think it's confusing to partially apply foldr and map but not (+), (.), const, and length? Applied consistently: length = (.)(foldr((+)(…
> Why does the author think it's confusing to partially apply foldr and map but not (+), (.), const, and length? Neither of these functions is being partially applied in the code you cited. While the placeholder $ can be used to indicate all "free slots" of a function, you would only have to use it in partial applications of that function. (Analogous to how in mathematics the abstract index notation for tensors[0] is…
That's incorrect. Here's the definition of function compose:
(f . g) x = f (g x)
So in `foldr (+) 0 . map (const 1)`, the author gives `f = foldr (+) 0` and `g = map (const 1)` but doesn't supply `x`. That's a partial application. Similarly for const: const x y = x
Even if I concede length and (+), these two are partially applied.> you would only have to use it in partial applications of that function.
So why not const and (.)? If they're allowed to curry, why not foldr and map?
Re: A case against currying
#134Earlier quoted context omitted.
> Why does the author think it's confusing to partially apply foldr and map but not (+), (.), const, and length? Neither of these functions is being partially applied in the code you cited. While the placeholder $ can be used to indicate all "free slots" of a function, you would only have to use it in partial applications of that function. (Analogous to how in mathematics the abstract index notation for tensors[0] is…
> - Function composition was being used as a binary operator between two functions. You just replaced the infix notation with prefix notation. That's incorrect. Here's the definition of function compose: (f . g) x = f (g x) So in `foldr (+) 0 . map (const 1)`, the author gives `f = foldr (+) 0` and `g = map (const 1)` but doesn't supply `x`. That's a partial application. Similarly for const: const x y = x Even if I c…
It's not, and the authors doesn't have to. Using your definitions, `f . g` is a function of a single free argument (whether you call it x or y or whatever). Partial application occurs when you want to fix some parameters but not all of them. In the present case, however, there is only one parameter (x) and it's not fixed, so there's no partial application to speak of and one can omit the parameter as usual.
Put differently: (.) is a binary operator on functions, and length = f . g from the OP is an equation of functions. You can certainly write it down pointwise, i.e. length(x) = (f . g)(x), but that doesn't tell you anything you didn't know already, and it is unrelated to the case of partial application that the author is discussing.
Re: A case against currying
#135Earlier quoted context omitted.
> Simplicity: Every function takes exactly one input and produces exactly one output. No exceptions. If you didn’t care about the input or output, you used Unit, and we made special syntax for that. Seems like a disaster to use s-expressions for a language like that. I love s-expressions but they only make sense for variadic languages. The entire point of them is to quickly delimit how many arguments are passed. In s…
S-expressions are more like the tupled argument form, but better. (f x y z) Is equivalent to: (f . (x . (y . (z . ()))) Every function takes one argument - a list. Lists make partial application simpler than with tuples (at least Haskell style tuples), because we don't need to define a new form for each N-sized tuple. Eg, in Haskell you'd need: partial2 : (((a, b) -> z), a) -> (b -> z) partial3 : (((a, b, c) -> z), a…
That's one way to look at it, but the major difference is that one can also pass a a list as one argument, as in `(f x y z)` and `(f (list x y z)` are not the same. The thing with tuples is that a tuple of one datum is the very same as that datum itself and the same is true with the currying situation. `(f x) y` and `f x y` are truly one and the same in Haskell and Ocaml, just as `f(x, y)` and `let val a = (x,y) in f x` are one and the same in SML. This is not the case in Rust where `f(x,y)`, a function called with two arguments, and `f((x,y))`, a function called with one argument that is a tuple of two arguments are two different things.
There is also a difference in Scheme between returning a single value that is a list containing multiple values, and actually returning multiple values, In Rust however there is no difference between returning two values and returning a pair of two values. So Rust functions actually do properly take multiple arguments but always return a single one which may or may not be a tuple. In SML, Haskell and OCaml all functions technically take only one argument and return one value.