A case against currying
https://emi-h.com/articles/a-case-against-currying.html> 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).
1. Looking at a function call, you can't tell if it's returning data, or a function from some unknown number of arguments to data, without carefully examining both its declaration and its call site
2. Writing a function call, you can accidentally get a function rather than data if you leave off an argument; coupled with pervasive type inference, this can lead to some really tiresome compiler errors
3. Functions which return functions look just like functions which take more arguments and return data (card-carrying functional programmers might argue these are really the same thing, but semantically, they aren't at all - in what sense is make_string_comparator_for_locale "really" a function which takes a locale and a string and returns a function from string to ordering?)
3a. Because of point 3, our codebase has a trivial wrapper to put round functions when your function actually returns a function (so make_string_comparator_for_locale has type like Locale -> Function<string -> string -> order>), so now if you actually want to return a function, there's boilerplate at the return and call sites that wouldn't be there in a less 'concise' language!
I think programming languages have a tendency to pick up cute features that give you a little dopamine kick when you use them, but that aren't actually good for the health of a substantial codebase. I think academic and hobby languages, and so functional languages, are particularly prone to this. I think implicit currying is one of these features.
Functions can be done explicitly written to do this or it can be achieved through compiler optimisation.
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.
(log configuration identifier level format-string arg0 arg1 ... argN)
After each partial application step you can do more and more work narrowing the scope of what you return from subsequent functions. ;; Preprocessing the configuration is possible
;; Imagine all logging is turned off, now you can return a noop
(partial log conf)
;; You can look up the identifier in the configuration to determine what the logger function should look like
(partial log conf id)
;; You could return a noop function if the level is not enabled for the particular id
(partial log config id level)
;; Pre-parsing the format string is now possible
(partial log conf id level "%time - %id")
In many codebases I've seen a large amount of code is literally just to emulate this process with multiple classes, where you're performing work and then caching it somewhere. In simpler cases you can consolidate all of that in a function call and use partial application. Without some heroic work by the compiler you simply cannot do that in an imperative style.There is one situation, however, where Standard ML prefers currying: higher-order functions. To take one example, the type signature of `map` (for mapping over lists) is `val map : ('a -> 'b) -> 'a list -> 'b list`. Because the signature is given in this way, one can "stage" the higher-order function argument and represent the function "increment all elements in the list" as `map (fn n => n + 1)`.
That being said, because of the value restriction [0], currying is less powerful because variables defined using partial application cannot be used polymorphically.
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);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.
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..
https://jonathanwarden.com/implicit-currying-and-folded-appl...
(Side note: if you're reading this Roc devs, could you add a table of contents?)
I'm failing to see how they're not isomorphic.
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?
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.