'Putting things' (multi-argument function calls, in this case) 'in-band doesn't make them go away, but it does successfully hide them from your tooling', part 422.
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.
Maybe there could be a rule that parameters have to be named only 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, or something along those lines. But the ergonomics of that might be annoying as well.
let warn_user ~message = ... (* the ~ makes this a named parameter *)
let error = "fatal error!!" in
warn_user ~message:error; (* different names, have to specify both *)
let message = "fatal error!!" in
warn_user ~message; (* same names, elided *)
The elision doesn't always kick in, because sometimes you want the variable to have a different name, but in practice it kicks in a lot, and makes a real difference. In a way, cases when it doesn't kick in are also telling you something, because you're crossing some sort of context boundary where some value is called different things on either side.> 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).
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 explicit "hole" for this defeats the purpose: let f = barbalyze(c, d, foobinade(a, b, $))
let result = f(input)
Or, just as bad, you could give up on partial function application entirely and go with: let result = barbalyze(c, d, foobinade(a, b, input))
Either way, I hope that gives everyone the same "ick" it gives me. 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. let result = input
|> add_prefix_and_suffix("They said '", $, "'!") 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.https://jonathanwarden.com/implicit-currying-and-folded-appl...
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.
def add(x: int, y: int) -> int { return x + y; }
def add3 = add(_, 3);
Or more simply, reusing some built-in functions: def add3 = int.+(_, 3);Today I read this article and after plowing through the article came to where the meat was supposed to be and the points made to my mind are weak. The conclusions. I just "followed" this whole article to lead to .... nothing.
I had another instance of recognition. It is like one of those recipe sites where you page down and down, reading the persons life story and get to a recipe that is .... ineffective.
Then it occurred to me that the problem is not AI generated content. It is ant mill media. Like Hacker News is now, like the Washington Post (thanks there Jeff Bezos), the greed-centric New York Times.
If you have read this far, here is the punch line:
The government generated by an ant mill is completely inept and ineffective. So if you look up and around at the absolute mess, does it look like an ant mill to you?
(Side note: if you're reading this Roc devs, could you add a table of contents?)
The "hole" syntax for partial application with dollar signs is a really creative alternative that seems much nicer. Does anyone know of any languages that actually do it that way? I'd love to try it out and see if it's actually nicer in practice.
And yes, another comment mentioned that Scala supports this syntax!
With the most successful functional programing language Excel, the dataflow is fully exposed. Which makes it easy.
Certain functional programming languages prefer the passing of just one data-item from one function to the next. One parameter in and one parameter out. And for this to work with more values, it needs to use functions as an output. It is unnecessary cognitive burden. And APL programmers would love it.
Let's make an apple pie as an example. You give the apple and butter and flour to the cook. The cursed curry version would be "use knife for cutting, add cutting board, add apple, stand near table, use hand. Bowl, add table, put, flour, mix, cut, knife butter, mixer, put, press, shape, cut_apple." etc..
Functions can be done explicitly written to do this or it can be achieved through compiler optimisation.
It's also a question of whether this is exclusive to a curried definition or if such an optimization may also apply to partial application with a special operator like in the article. I think it could, but the compiler might need to do some extra work?
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.
messe•1h ago
I'm failing to see how they're not isomorphic.
Pay08•1h ago
messe•1h ago
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•1h ago
antonvs•42m ago
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.
emih•1h ago
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
Kambing•1h ago
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•1h ago
recursivecaveat•1h ago
layer8•37m ago
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.
naasking•25m ago
rocqua•17m ago
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.