L~R, thanks
for dropping by! You have some great questions, ones that I think
a lot of people share. I'll do my best to answer them.
greeting('Woah'); # Waits
...
greeting('Nelly'); # prints now that it has both arguments
How does it know that I am finishing off one and not
starting another? Or if the 3rd time I called greeting() I did it with
two arguments - what then?
Actually, currying doesn't work that way. Each of the calls above
would result in a partial application of the greeting
function and result in a new function that, if given the final
argument, would emit the final result. Currying doesn't rely upon a
stateful function that accumulates arguments until it is satisfied.
Each call is independent. I explain more on this later.
Regarding how the language knows when the final argument is
supplied, "real" currying is typically paired with a type system that
can infer when all arguments have been satisfied. To use Haskell as
an example, the greeting function would be written like so:
greeting verb object = putStrLn (verb ++ ", " ++ object)
From this, Haskell can infer the following type:
greeting :: String -> String -> IO ()
It says, "greeting takes a string and returns a function that takes
another string and returns an IO action." (The notation
a->b means "function from type a to type
b" and is right-associative.) This is more obvious
from the fully parenthesized type:
greeting :: String -> ( String -> IO () )
Thus the following are all the same:
greeting "Hello" "World"
(greeting "Hello") "World"
((greeting) "Hello") "World"
Let's take a look at the types of the intermediary expressions:
greeting "Hello" "World" :: IO ()
greeting "Hello" :: String -> IO ()
greeting :: String -> String -> IO ()
So, to answer your first question, Haskell knows what to do
at each step because it keeps track of the types. If you
supply both arguments, the result is an IO action –
"Hello, World" is printed. If you supply only the first
argument, the result is a function that takes a string
and returns an IO action. And if you supply zero arguments,
the result is a function that takes a string and returns
a function than takes a string and returns an IO action.
(The reality is a bit more complex. The short of it
is that the intermediate functions are optimized away
when they're not needed.)
The next question is with your statement:
We can't quite make it all the way because
functions in Perl can accept varying numbers of arguments, and thus
it's hard for us to determine reliably when currying is implied by
analyzing function calls.
Prototypes, as evil as they are, do allow for optional arguments
or you could always count @_. I don't see why this is a
problem.
It's not a problem per se, but rather a design decision.
Perl lets us write functions with variable numbers of arguments, and
it offers wonderful argument-splicing call semantics. Why create a
Perl "currying" that doesn't let us take advantage of these strengths?
I want to be able to curry functions like the following, where there
is no ahead-of-time notion of how the arguments will be used:
sub foo { print "@_" }
What this means, however, is that we're not really currying.
What we're doing is a single-shot argument binding. But it's close
enough to true currying for most practical intents and purposes.
My final question for now is an apparent flaw in the
design. Currying, as I understand it, only works if I don't have the
last x arguments. What if I have the middle argument and the entire
function requires 5?
In most languages with native currying, higher-order functions are
also available. They let you manipulate other functions to reorder
arguments (among other things). Nevertheless, in a world with
pervasive currying, most people design their functions so that the
arguments most likely to be specialized come first. Typically, what
you want to specialize on will be one of the first two arguments,
and getting to them is idiomatic.
In Haskell, for example, the higher-order function flip
flips the order of first two arguments of its target function. This
lets you specialize the second argument and leave the first alone:
(-) 2 1 ==> 1
flip (-) 2 1 ==> -1
dec1 = flip (-) 1
dec1 2 ==> 1
(Note: Most experienced Haskell programmers would use the operator section
(- 1) instead of creating dec1.)
Thanks again for your comments. I hope I was able to answer
your questions. Please let me know if any of my explanations
aren't clear.
Cheers, Tom
P.S. Writing a library of zip and
fold functions for Tool::Box is what motivated me to
create AutoCurry! These functions really shine when low-cost
currying is available.
| [reply] [d/l] [select] |
Howdy!
greeting('Hello', 'Good Looking'); # prints immediately
greeting('Woah'); # Waits
...
greeting('Nelly'); # prints now that it has both arguments
I don't think so. greeting('Woah') would, as far as I can tell, yield a
new function that takes a single argument, so it would have to be used in
a context where that other argument will appear.
I'd expect it to be analagous to greeting('Whoa')->('Nelly');
| [reply] |
If I understand correctly, you can still curry with named parameters. You just have to add a little more intelligence. Basically, currying means that the function call was incomplete - we need more parameters before we are satisfied. It's easy to do that with positional - did I get enough stuff. But, using something like Params::Validate, you could build currying for named parameters.
Of course, the trick is making sure you have a good API for the developer to specify currying. I suspect the best way is to just define the API, then have the function automatically curry whenever a required argument is missing.
Being right, does not endow the right to be rude; politeness costs nothing. Being unknowing, is not the same as being stupid. Expressing a contrary opinion, whether to the individual or the group, is more often a sign of deeper thought than of cantankerous belligerence. Do not mistake your goals as the only goals; your opinion as the only opinion; your confidence as correctness. Saying you know better is not the same as explaining you know better.
| [reply] |