Coming off years of C/C++ programming, my mind is organized so that I want to write functions such that most important argument comes first in the argument list. But, in a language like haskell, that’s not always the best way.
Here’s an example from just a moment ago. I was adding haddock documentation to some haskell libraries I had written, when I came across this little utility function I had written a while back:
percent_within_range val low high =
(val-low) / (high-low)
This function takes a value, along with the highest and lowest boundaries of a range. It returns the percentage that the value is above the low of the range. So, passing in 5 0 10 returns 0.5, since 5 is 50% of the way between 0 and 10. 10 0 10 returns 1.0. get it?
Anyway, it was natural for me to pass the val first, since that’s the item I care about when I call the function. In an OO language, percent_within_range might even be a method on val! But, in haskell, this is really the opposite of what you want.
I re-wrote the function this way:
percent_within_range low high val =
(val-low) / (high-low)
… which is just a re-ordering of the arguments. Now, if you are just calling the function, this makes literally no difference. But, in haskell, this ordering makes it easy to define curried versions of the function like this:
pct_100 = percent_within_range 0 100
… or just use it curried directly:
map (percent_within_range 0 100) lst
You can see this style of argument ordering all over haskell (and other functional languages) libraries, and doing something different is an easy way to mark yourself as an fp n00b. I’ve finally become accustomed to writing code this way, and am still finding examples of “backwards” argument lists in code I wrote long ago.
[EDIT: Since all the comments talk about 'flip' and other strategies for rearranging elements, I want to say that: yes, there are plenty of ways to rearrange arguments, if you are faced with a function that does not curry naturally for your needs. That's not the point. The point is, with some thought, you can make occasions where you need such workarounds relatively rare. After all, I've needed to use ((flip map) [1..10]), but I’ve used (map (+1)) quite a bit more often! I was pointing out that usually the best argument order for common currying is not the order that a lot of imperative/OO programmers would choose.]
April 28th, 2007 at 6:44 am
Flip might be a good function to use in such situations; see http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html#v%3Aflip
April 28th, 2007 at 6:47 am
yeah, you can use flip with two args. But, I guess the point is, it’s more idiomatic to just put the args in the order that makes the most common curry-ing easiest.
April 28th, 2007 at 8:41 am
I’ve written a bit about this lately too: one, two. I got a few interesting comments from people smarter than I.
One thing I wondered about was generalising flip to functions with more than two parameters; the best suggestion I’ve seen so far is anonymous functions with lambda.
April 28th, 2007 at 8:50 am
yes, you can arbitrarily rearrange args with (\a b c -> f b c a). All I’m trying to say with this article is that you can often avoid creating situations where you have to do that by just thinking about the argument order a little in the first place.
There is also just something to be said about writing code in a way that’s idiomatic for the language you are using. I think so, anyway.
Most standard haskell functions are written in curry-friendly order, for the most common cases.
April 30th, 2007 at 12:29 am
I wrote some days ago a post [1] about how to use two different strategies : flip arguments and labeled arguments in Ocaml.
[1] : http://theplana.wordpress.com/2007/04/26/curried-function-and-labeled-arguments-in-ocaml/
April 30th, 2007 at 5:42 pm
I remember doing similar things in Forth. Since Forth is a stack based language, the order of arguments can greatly influence the size and complexity of a function.
May 7th, 2007 at 3:45 am
and
I think you have low and high reversed in this copy of your function.
May 7th, 2007 at 6:20 am
@jag: Thanks! I’ve corrected it.