How currying leads to elegant, readable code
fr.umio.us
fr.umio.us
For instance, my company writes a lot of code in a proprietary language and the other day I was writing a function to generate random strings, given a length and an "alphabet" string from which to choose characters.
I chose the ordering:
randomString alphabet length = ...
so that if partial function application were added to the language, one could simply write the functions randomHex and randomBase32 as: randomHex = randomString "0123456789ABCDEF"
randomBase32 = randomString "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567"
The alternative ordering would allow me to easily write functions that generate fixed length strings and take their alphabets as inputs: random10Char = randomString 10
random4Char = randomString 4
This ordering is much less useful in general, so I chose to make the alphabet the first parameter.On a side note, our language also has some time series extensions for dataflow programming. There's an API for efficiently adding new data flows based on something similar to template specialization, but the API is unwieldy and the source of much confusion. So, I wrote a wrapper API that uses reflection to expose the same functionality as partial function application / Curried functions over time series.
Oddly though, when writing haskell code, I don't worry about it much. Once I find myself needing to `flip` a function before using it, I have determined experimentally that the original parameter order I picked was not the best one, and I then go change the function, and let the type checker walk me through fixing any necessary call sites (which also verifies if the new parameter order is better or not).
It seems like it would be easy enough to have a special "placeholder" keyword that means "these parameters will be the parameters of the curried function". To take his example:
var jp = formatNames2('John', 'Paul');
That might be written like this instead: var jp = formatNames2('John', 'Paul', _);
And then if you want to curry the second parameter instead of the third, it's easy: var jp = formatNames2('John', _, 'Jones');
Or maybe they do and I just haven't heard of it?Arc/Anarki: [format-names2 "John" _ "Jones"] which expands to (fn (_) (format-names2 "John" _ "Jones"))
Clojure: #(format-names2 "John" % "Jones") which expands to (fn (%) (format names2 "John" % "Jones"))
Then for a function of two arguments that curried the middle one:
Anarki: [format-names2 _1 "Paul" _2] which expands to (fn (_1 _2) (format-names2 _1 "Paul" _2))
Clojure: #(format-names2 %1 "Paul" %2) which expands to (fn [%1 %2] (format-names2 %1 "Paul" %2))
[0] http://clojure.org/reader [1] https://github.com/arclanguage/anarki/blob/master/CHANGES/ma...
The better argument is from syntax. You can think of partial function application as shorthand for lambdas.
someFunc A B C = ...
myFunc = someFunc "earlyParam"
is shorthand for someFunc A B C = ...
myFunc = \B C -> someFunc "earlyParam" B C
Now, what syntax would you use for arbitrary order partial function application that's both elegant and more terse than just writing the lambda? (Also, if a compiler performs inlining to optimize partial function application, it's just as easy to perform the same optimizations for lambdas when the same information is available at compile time.)The space and time performance argument is weaker:
If you have a language that compiles to native code, your calling convention passes everything on the stack (parameters on the right first), all parameters are the same size (as is the case for OCaml and other languages that pass everything as either a small integer or a pointer to a boxed value) and you only allow partial function application starting at the left parameter, then the partial function application native stubs for partially applying up to 10 parameters only requires 10 stubs. It's easy to include these stubs in your standard runtime, and the performance is very good. If the calling convention requires the callee to clean up the stack (Pascal/Windows Syscall calling convention), then the stubs don't even need to modify the return address, just copy the return address N words up the stack and copy in the partially applied parameters and then jump to the original function entry point.
On the other hand, if you allow arbitrary Currying order, then the number of stubs explodes combinatorially and you're going to have to generate each stub in the compilation unit that needs it, maybe with a few common stubs in the standard runtime library.
Of course, calling conventions with different parameter sizes and passing some parameters in registers complicates things, but you still wind up with a much simpler situation if you only allow partial application starting at one end of the parameter list.
The syntax argument is more compelling.
Secondly, this is merely a syntactic item you're looking for. It's trivial to achieve this end in any language with first class anonymous functions. I'm not claiming that syntax isn't important, but instead just pointing out that it really is a trivial transformation to include if it were valuable enough.
def randomString(alphabet, length):
....
randomHex = partialApply(randomString, alphabet="0123456789ABCDEF")
randomBase32 = partialApply(randomString, "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567")
random10Char = partialApply(randomString, length=10)
random4Char = partialApply(randomString, length=4)
randomHexByte = partialApply(randomString, "0123456789ABCDEF", 2)
Comes in handy occasionally.(So `functools.partial(foo, b=3)(a=1, b=2)` doesn't throw an error, and is equivalent to calling `foo(a=1, b=2)`)
I think this, along with the whole article, is the best explanation of currying I've seen on the whole damn internet.
http://www.uncarved.com/blog/not_currying.mrk
Which the author freely admits:
(Some will insist that what we're doing is more properly called "partial application", and that "currying" should be reserved for the cases where the resulting functions take one parameter, each resolving to a separate new function until all the required parameters have been supplied. They can please feel free to keep on insisting.)
I feel that although the distinction can be very important, it's much less so for Javascript. And the implementation in Ramda can be used that way any time you choose:
var total = reduce(add)(0); // or
var sum = reduce(add)(0)(numbers)
works just as well in Ramda as var total = reduce(add, 0); // or
var sum = reduce(add, 0, numbers)
So, as I said, feel free to keep on insisting. :-)Start at 26mins. You don't have flip. You lose control over partial application. It isn't curry. You're right. It is an important distinction :)
-- all morally identical
a -> b -> c -> d
a -> (b -> c -> d)
a -> (b -> (c -> d))
(a, b) -> c -> d
(a, b, c) -> d
((a, b), c) -> d
(a, (b, c)) -> d
However, so long as you have parameter order (we're not allowed to commute our tuples, just reassociate nested tuples like ((a, b), c) <-> (a, (b, c)) and so on) then flip still morally works flip (someFn :: (a, b, c) -> d)
:: (b, a, c) -> d
:: (b, a) -> c -> d
:: b -> a -> (c -> d)
The same story holds for partial evaluation. Of course, there have to be far more edge cases to handle all this tuple twiddling.---
Really, I think it's pretty meaningful to blur the distinction between "curry (once)" and "curry repeatedly along with tuple reassociation". The later forms an equivalence class of function types which all have the same application behavior.
(a, b) -> c
are exactly equivalent to the things of type a -> (b -> c)
An adjunction (F, G) occurs when the things of type (F a -> c) are equivalent to things of type (a -> G c), so if we pick F x = (x, b) and G x = (b -> x) then we see that currying is an adjunction between (,b) and (b ->). var getIncompleteTaskSummaries = function(membername) {
return fetchData().then(function(data) {
var incompleteTasks = [];
for (i = 0; i < data.tasks.length; ++i) {
var task = data.tasks[i];
if (task.username == membername && !task.complete) {
incompleteTasks.push({id: task.id, dueDate: task.dueDate, title: task.title, priority: task.priority});
}
}
incompleteTasks.sort(function(a, b) {
return a.getTime() - b.getTime();
});
return incompleteTasks;
});
}
?Apologies if that's wrong, i barely know JavaScript.
That code is not so great, and a bit '90s, but it's not appalling. The horrifically verbose non-currying code in the article isn't something i see any need to write. It looks like an outrageous strawman to me.
My code here could definitely improved with some functional idioms for the looping, picking, and sorting, but i wouldn't reach for currying here.
The article really didn't get into the fact that these blocks of code that were listed inline would ideally represent reusable functions. That these functions would best be defined separately and only included by reference would have cluttered the article, but leaving them out does make it feel a bit like a strawman, I agree.
The breakdown of a task into these smaller functions is something I discussed more fully in the previous article: http://fr.umio.us/why-ramda/
(defn get-imcomplete-task-summaries [membername]
(.then (fetch-data)
(fn [data]
(sort-by :time
(for [task (:tasks data)
:when (= membername (:username task))
:when (not (:complete task))]
task ;; immutable, no need to copy
)))))
It makes me a little sad that functional programming has come to mean map/reduce/filter for lots of people, rather than avoiding mutation which is far more interesting.http://smallcultfollowing.com/babysteps/blog/2014/05/13/focu...
I have no idea if this is true, but it's going to be really interesting to see that idea tried.
Readability is in the eye of the beholder. A tidy functional equivalent might look something like this (in a made-up syntax with typical functional idioms):
getIncompleteTaskSummaries membername =
fetchData
>>> tasks
>>> filter (task => username task == membername and not complete task)
>>> map (subsetkeys ["id", "dueDate", "title", "priority"])
>>> sortwithkeyfn getTime
Compare that with the explicit loops and tests above: var getIncompleteTaskSummaries = function(membername) {
return fetchData().then(function(data) {
var incompleteTasks = [];
for (i = 0; i < data.tasks.length; ++i) {
var task = data.tasks[i];
if (task.username == membername && !task.complete) {
incompleteTasks.push({id: task.id, dueDate: task.dueDate, title: task.title, priority: task.priority});
}
}
incompleteTasks.sort(function(a, b) {
return a.getTime() - b.getTime();
});
return incompleteTasks;
});
}
In practice, functions like filter, map, subsetkeys and sortwithkeyfn would probably be in your standard library and immediately familiar to anyone used to programming in such a language, so the only interesting details are the interesting details: which tasks you want to pick out, how you want to summarise them, and how you want the results ordered. Depending on what else is going on, you might even extract some or all of those three details into named one-liner functions to make the main algorithm more self-documenting and/or to allow those concepts to be reused elsewhere.The functional version as shown reduces 10 substantial lines with a lot of temporary variables and deep nesting to 6 total lines that each have a clear, self-contained purpose and that collectively systematically transform your input to your output. To my eyes, that style is far more readable and maintainable than manually reinventing fundamental algorithms like filter and map and camouflaging the interesting details within three or four levels of context.
Edited to add: Often the thing that makes functional style awkward is when a language that wasn’t designed with that style in mind adopts some of the useful concepts but without the elegant syntax and idioms to match. For example, we have somewhat unwieldy notation for defining quick local functions in JavaScript, lambdas that work as long as you can do everything on one line in Python, and the horrific lambda expression notation in modern C++. But I don’t think it’s helpful to conflate this with functional programming style itself, any more than it’s helpful to conflate Java’s verbosity with static type systems.
"you might even extract some or all of those three details into named one-liner functions"
No. Replacing familiar operations with unfamiliar ones (adding a level of indirection) doesn't add readability or make it "more self-documenting" than pure boilerplate code that everyone's seen many times before and will see again.
But these sort of arguments never reach a conclusion. The way to test it would be to teach beginners and see which one is easier to teach. That's a bit beyond a casual internet discussion, though. :-)
FWIW, I'm in the anti-boilerplate camp.
var result = somefunc(a,b) // object var otherResult = somefunc(a) // function
It gets even more confusing if overloading of function names for different parameters or different number of parameters is allowed.
So elegant: maybe. Readable: no. Depends on the programming language.
Basically, it seems to me like trying to fix something that isn't broken.
The purpose of currying is to improve reuse and chaining of functions, resulting in easier to read and more elegant code.
The minute you step into the standard industry things change a little bit. I once maintained an amazing piece of javascript that did dynamic server requests and rendered graph data, processed user input, changed views, and make lasting configuration changes to the database. The first 30 or so lines of code was simply setting up the functional haskellish environment for javascript, and the last 40 lines + 40 lines of HTML put everything else together. It was terse, compact, elegant, and impossible to maintain or make wider changes after the original author had moved on. It was tightly contained into one piece of the code, so not something that will be seen on the mainline easily. Going in and fixing any tiny little bug forced any unfortunate developer to immediately learn Chinese. Someone was tasked to re-write it so the rest of the team could maintain it effectively.
I like ruby/python a lot because it meets in the middle fairly easily, and doesn't require contortions to the natural intent of the language to get rid of cruft.
I was talking about currying and FP in nicer languages.
In a previous article (http://fr.umio.us/why-ramda/) I discuss more thoroughly how I would tend to use this currying, namely to build up collections of useful functions, often in points-free style, so that major parts of the application would end up as (ideally well-named) functions, each which serve as pipelines of these smaller functions, passing data through the system.
I do find this significantly more readable, but _de gustibus non est disputandum_.
No, because I use Array.prototype.filter(), Array.prototype.map(), and Array.prototype.reduce() to accomplish these things...
Extreme point free style does not work in javascript, AT ALL!!!! Two reasons:
1) Find the bug: the table sort is glitchy. Is the bug in this suspicious line, or somewhere else? (Real line of code in a codebase I inherited, and yes there is a bug in it)
var sorted = rows.sort(_.unsplat(_.pipeline(
_.partial(_.map, _, accessor),
_.splat(comparator))));
2) I use React, so I can get away with a lot of really functional stuff since React is well suited for functional javascript. React is a great functional abstraction, but under the abstraction is imperative code. Imperative code means you need a debugger. When you write in a point free style, there's nowhere to place a breakpoint!On the other side, I tend to work with a few well-tested functions over and over and end up with fewer problems to debug when I work in a functional paradigm, points-free or not.
On rare occasions, when I need to debug something that is difficult because it's points-free I simply add some explicit parameters, either temporarily to enable debugging or permanently.
Points-free is nice, but should never be an overriding goal.
I think that using the .bind() method on functions is the right approach. It will lead to less surprising behaviour in the context of javascript.
The code is a little more confusing at first glance. The developers who replaced the code with curried functions didn't inform the whole team what they were doing. There were a lot of wtfs until they clarified. Now we can read it, and I know I'll be looking for places for partial applications.
As Currying and functional programming a popular thing now, I was thinking what my current codebase would look like in C# if I were writing code in a more functional way.
So, obviously C# isn't a functional languageat all but using Func<T>, and maybe some delegates would enable this easily.
I'm not sure this would turn my code to more elegant or readable.
What would I benefit from ?
The .NET platform of course has F#, which is a dedicated functional language. Some day I'll spend some time learning this one... perhaps.
However, I find lambdas with the extra parameters repeated more readable than partial application.
var jp = function(last){
return formatNames2('John', 'Paul', last);
} var jp = function(last) {
return formatNames2('John', 'Paul', last);
};
var je = function(last) {
return formatNames2('James', 'Earl', last);
};
var lh = function(last) {
return formatNames2('Lee', 'Harvey', last);
};
var c = function(middle, last) {
return formatNames('Clarabelle', middle, last);
};
But I find this cleaner: var jp = formatNames2('John', 'Paul'};
var je = formatNames2('James', 'Earl'};
var lh = formatNames2('Lee', 'Harvey'};
var c = formatNames('Clarabelle'};If anything, it does show that one useful thing about partial application is that you can write a generic function (formatNames) and then make it more specific (formatNames("John")) without repeating yourself.
Using fors and ifs:
var origin = Point(0, 0);
var R = 100;
var result = [];
for (var i : array) {
tmp = distance(array[i], origin);
if (tmp>R) {
result.append(tmp);
}
}
Without currying (nobody would write it that way): var origin = Point(0, 0);
var R = 100;
result = filter( map(array, function (x) { return distance(x, origin); }),
function (x) { return greater(x, R);} );
With currying it almost reads like SQL: var origin = Point(0, 0);
var R = 100;
result = filter( map(array, distance(origin)), greater(R) );
It's useful for functions that you predict will be used many times with one of the arguments the same. Yes, it's just syntactic sugar, but it sometimes makes code clearer (especially when literal function syntax is as verbose as in javascript).I just want to note that in Ramda, because of it's strong insistence on a data-last API (which makes the currying easier), this would be written in this order:
result = filter(greater(R), map(distance(origin), array));
But it's precisely the same idea.