Beefy Boxes and Bandwidth Generously Provided by pair Networks
Syntactic Confectionery Delight
 
PerlMonks  

Re: Near-free function currying in Perl

by Limbic~Region (Chancellor)
on Nov 17, 2004 at 14:30 UTC ( #408416=note: print w/replies, xml ) Need Help??


in reply to Near-free function currying in Perl

tmoertel,
Ok - I have to admit that I don't fully understand currying or a lot of other concepts from functional programming (FP). I have a few questions - some in general and some specific to your implementation.

A common way in Perl to simulate currying is as follows:

sub greeting { my ($verb, $object) = @_; my $sub = defined $object ? sub { print "$verb, $object\n" } : sub { print "$verb, ", shift(), "\n" }; return $sub } my $hello = greeting('Happy Birthday', 'L~R'); $hello->(); my $bye = greeting('goodbye'); $bye->('Cruel World'); __END__ Happy Birthday, L~R goodbye, Cruel World
I am guessing the same in a FP language might look like:
greeting('Hello', 'Good Looking'); # prints immediately 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?

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.

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 Perl, that would be easy because I would use a hash ref and "name" my arguments. I don't see how currying helps in this situation (real currying, not Perl's substitute).

Thanks by the way - I am impressed.

Cheers - L~R

Update: Added a clarification to distinguish between FP currying and the simulation of currying in Perl

Replies are listed 'Best First'.
Re^2: Near-free function currying in Perl
by tmoertel (Chaplain) on Nov 17, 2004 at 16:49 UTC
    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.

Re^2: Near-free function currying in Perl
by herveus (Parson) on Nov 17, 2004 at 15:18 UTC
    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');

    yours,
    Michael
Re^2: Near-free function currying in Perl
by dragonchild (Archbishop) on Nov 17, 2004 at 15:27 UTC
    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.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others having an uproarious good time at the Monastery: (4)
As of 2020-12-03 08:19 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    How often do you use taint mode?





    Results (52 votes). Check out past polls.

    Notices?