Earlier quoted context omitted.
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.
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…
A case against currying
61–70 of 135 posts
Re: A case against currying
#62Earlier quoted context omitted.
One slightly contrived example would be if you had a function that returned the point of a set closest to another given point. getClosest :: Set Point -> Point -> Point You could imagine getClosest build a quadtree internally and that tree wouldn't depend on the second argument. I say slightly contrived because I would probably prefer to make the tree explicit if this was important. Another example would be if you we…
I think we need to see a few non-contrived examples, because i think in every case where you might take advantage of currying like this, you actually want to make it explicit, as you say. The flip side of your example is that people see a function signature like getClosest, and think it's fine to call it many times with a set and a point, and now you're building a fresh quadtree on each call. Making the staging expli…
Re: A case against currying
#63Re: A case against currying
#64Earlier quoted context omitted.
Well, I totally disagree with this. One of the main benefits of currying is the ability to chain function calls together. For example, in F# this is typically done with the |> operator: 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 e…
You can still do this though: 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.
Re: A case against currying
#65I 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…
Re: A case against currying
#66Earlier quoted context omitted.
One slightly contrived example would be if you had a function that returned the point of a set closest to another given point. getClosest :: Set Point -> Point -> Point You could imagine getClosest build a quadtree internally and that tree wouldn't depend on the second argument. I say slightly contrived because I would probably prefer to make the tree explicit if this was important. Another example would be if you we…
Those are nice examples, thanks. I was imagining you might achieve this optimization by inlining the function. So if you have getClosest(points, p) = findInTree(buildTree(points), p) And call it like myPoints = [...] map (getClosest(myPoints, $)) myPoints Then the compiler might unfold the definition of getClosest and give you map (\p -> findInTree(buildTree(myPoints), p)) myPoints Where it then notices the first par…
I don't believe inlining can take you to the exact same place though. Thinking about explicit INLINE pragmas, I envision that if you were to implement your partial function application sugar you would have to decide whether the output of your sugar is marked INLINE and either way you choose would be a compromise, right? The compromise with Haskell and curried functions today is that the programmer has to consider the order of arguments, it only works in one direction but on the other hand the optimisation is very dependable.
Re: A case against currying
#67There are good ideas in functional languages that other languages have borrowed, but there are bad ideas too: currying, function call syntax without parentheses, Hindley-Milner type inference, and laziness by default (Haskell) are experiments that new languages shouldn’t copy.
I believe one of the main reasons that F# hasn't never really taken off is that Microsoft isn't afraid to borrow the good parts of F# to C#. (They really should've ported discriminated unions though)
Re: A case against currying
#68I like currying because it's fun and cool, but found myself nodding along throughout the whole article. I've taken for granted that declaring and using curried functions with nice associativity (i.e., avoiding lots of parentheses) is as ergonomic as partial application syntax gets, but I'm glad to have that assumption challenged. The "hole" syntax for partial application with dollar signs is a really creative alterna…
Re: A case against currying
#69One "feature of currying" in Haskell that isn't mentioned in the fine article is that parts of the function may not be dependent on the last argument(s) and only needs to be evaluated once over many application of the last argument(s) which can be very useful when partially applied functions are passed to higher-order functions. Functions can be done explicitly written to do this or it can be achieved through compile…
In that case I want the signature of "this function pre-computes, then returns another function" and "this function takes two arguments" to be different, to show intent.
> achieved through compiler optimisation
Haskell is different in that its evaluation ordering allows this. But in strict evaluation languages, this is much harder, or even forbidden by language semantics.
Here's what Yaron Minsky (an OCaml guy) has to say:
> starting from scratch, I’d avoid partial application as the default way of building multi-argument functions.
https://discuss.ocaml.org/t/reason-general-function-syntax-d...
Re: A case against currying
#70Earlier quoted context omitted.
Well, I totally disagree with this. One of the main benefits of currying is the ability to chain function calls together. For example, in F# this is typically done with the |> operator: 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 e…
For pipelines in any language, putting one function call per line often works well. Naming the variables can help readability. It also makes using a debugger easier: 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.
It can, or it can't; depending on the situation. Sometimes it just adds weight to the mental model (because now there's another variable in scope).