Beefy Boxes and Bandwidth Generously Provided by pair Networks
Do you know where your variables are?
 
PerlMonks  

Re^2: Near-free function currying in Perl

by tmoertel (Chaplain)
on Nov 17, 2004 at 16:49 UTC ( #408465=note: print w/replies, xml ) Need Help??


in reply to Re: Near-free function currying in Perl
in thread Near-free function currying in Perl

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.

Log In?
Username:
Password:

What's my password?
Create A New User
Node Status?
node history
Node Type: note [id://408465]
help
Chatterbox?
and the web crawler heard nothing...

How do I use this? | Other CB clients
Other Users?
Others chilling in the Monastery: (7)
As of 2020-11-24 21:05 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found

    Notices?