Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

Re: Near-free function currying in Perl

by BrowserUk (Pope)
on Nov 17, 2004 at 05:42 UTC ( #408321=note: print w/replies, xml ) Need Help??


in reply to Near-free function currying in Perl

Sorry Tom, but I'm still not getting it!

Your premise starts by saying that this:

$app_server = AppServer->new( logger => curry( \&log_to_handle, *STDERR, "app-server" ), ... );

is ...take[ing] the cost down another notch and also to make[ing] clear our intent", when compared with:

$app_server = AppServer->new( logger => sub { log_to_handle( *STDERR, "app-server", @_ ) }, ... );

But I simply do not see that is true.

Apart from that calling a function log_to_handle() is documenting how the function works, not what the function does; I would either reject, or modify any application server module that required me to give it function for it to call every time it wanted to log a message.

It is just so much easier and more efficient to allow me to configure the heading and destination filehandle as individual parameters at instantiation.

All your curry() is doing, is complicating a very simple piece of code, in order to justify the introduction of the need for autocurrying.

But there is another, more serious problem with the idea of autocurrying.

If AppServer is a class, as implied by the ->new(...) instantiation, then it would be normal practice for me to be able to instantiate two instances. If I pass in a coderef to each instance, then each instance can store those as instance data and will call the appropriate logger function automatically.

But if instantiating an instance of AppServer class is going to blythly export curried functions into the caller's namespace, then there is going to be a conflict between those exported for use with the first instance, and those for the second etc.

I also wonder how this is going to work with OO code where the first parameter is $self?

Namespace pollution will abound, to create lots of autocurrying functions that in all honesty, I cannot remember using a module that might lend itself to their use.

Your example aside, which seems somewhat contrived anyway, I cannot think of a single instance of ever needing or wanting to constantise one or more parameters that I had to pass on many calls to a function.


Examine what is said, not who speaks.
"Efficiency is intelligent laziness." -David Dunham
"Think for yourself!" - Abigail        "Time is a poor substitute for thought"--theorbtwo
"Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon

Replies are listed 'Best First'.
Re^2: Near-free function currying in Perl
by tmoertel (Chaplain) on Nov 17, 2004 at 14:06 UTC
    Thanks for your feedback! (I especially appreciate feedback from you because you take the time to explain your concerns, and you don't dismiss things offhand. When you say that you don't see the value of something, I know that it's not because you aren't trying. Thanks!)

    Let me see if I an address your concerns.

    But I simply do not see that (currying is less expensive than creating an anonymous wrapper function).
    Cost is in the eye of the be-payer, and so I won't tell you that you're wrong. However, I would like to suggest that your cost model would be different if you were familiar with currying.

    Later on, you write this:

    I cannot think of a single instance of ever needing or wanting to constantise one or more parameters that I had to pass on many calls to a function.
    This suggests that currying is indeed a foreign concept to you. When you see currying, then, the cost naturally goes up because it doesn't seem as nearly straightforward as "simply" writing anonymous-subroutine wrapper.

    But let me argue that currying is the easier of the two to understand (once currying is familiar). Currying does only one thing: binds arguments to values. Anonymous wrapper subs, on the other hand, can do anything. So, using the code below as an example, you must look closely at the anonymous-subroutine version to ensure that all it's doing is binding the first two arguments:

    sub { log_to_handle( *STDERR, "app-server", @_ ) } curry( \&log_to_handle, *STDERR, "app-server" ) log_to_handle_c( *STDERR, "app-server" );
    The second version, by definition, can only bind the first two arguments of log_to_handle. There is no other possibility. Please consider that this constraint reduces the cost of understanding the second w.r.t. the first. (Again, assuming that you have already paid the price to understand currying.) And the third version is even simpler (once it is understood that the _c suffix is notation that implies currying.) It is identical in meaning to the second but shorter and closer to the true currying that some other programming languages offer.
    All your curry() is doing, is complicating a very simple piece of code, in order to justify the introduction of the need for autocurrying.
    No doubt about it, my example sucks as a motivating example for currying. I doubt that anybody who doesn't already value currying is going to bother reading beyond the contrived back-story. (Lesson for me: Invest more time in selecting examples that are simple, illustrative, and yet realistic.)
    I would either reject, or modify any application server module that required me to give it function for it to call every time it wanted to log a message.
    Please don't read too much into the back-story. Don't focus on the obviously contrived "ooh, we have an application server that we must provide with a logging callback." Instead, see it as "how do we glue the function we want of use into the hole we want to fill, when their shapes are different?"
    But there is another, more serious problem with the idea of autocurrying.

    (I)f instantiating an instance of AppServer class is going to blythly export curried functions into the caller's namespace, ...

    It isn't. Here's the situation:
    #!/usr/bin/perl use warnings; use strict; use AppServer; use AutoCurry ':all'; sub log_to_handle { ... } my $appserver = AppServer->new( logger => log_to_handle_c( *STDERR, "app server" ) );
    Only log_to_handle_c will be created, and it will be stored in the same namespace as the original log_to_handle (main::). Further, it will happen only once, after the application code is compiled but before it starts executing.

    Thanks again for providing a wealth of feedback. I hope that I was able to address some of your concerns. Please don't hesitate to follow up, and I will do my best to respond.

    Cheers,
    Tom

      A perl noob (but veteran news writer) re your self-deprecated "motivating example":

      On the one hand (++), you give the reader a warning, "It's a simple function for the sake of our example, but let's imagine that it's complex and would be costly to rewrite." Good; 'cuz it leads to the inference that your basics will be more easily absorbed using a "simple" example.

      To head off critiques such as some already offered here, though, you might want to offer a much more complex function as an example of why currying is desireable -- perhaps even before the existing one.

      For example, you may have a specialized function like this: ((sorry way too new to write that)). Now, suppose new management wants you to write a package that does essentially the same thing, but with only a few possible inputs. But rather than do it with a complex example (that will make it harder to focus on currying), let's pretent the original function is:....

      Re the writing: splendidly clear and concise. This noob had difficulty only with one phrase... and that was simple lack of background... most of which you subsequently supplied. Well done!

      Will be watching the tread.

Re^2: Near-free function currying in Perl
by fergal (Chaplain) on Nov 17, 2004 at 12:22 UTC
    I think you've got things reversed. log_to_handle() is not part of the framework, it's a function that Tom already has lying around. The framework does it's logging by running a coderef you provide and passing in the log message as the signle argument.
    # deep inside the framework &$logger("frobnication successful");

    Tom's problem is that his log_to_handle() function is expecting several arguments including a filehandle and so he can't reuse in the framework. He would need it to be

    # deep inside the framework &$logger($LOGHANDLE, "frobnication successful");

    Currying allows Tom to take log_to_handle and turn it into a function that already knows what handle to use and only needs a single argument. One way to do that is

    my $logger = sub { my $msg = shift; log_to_handle($MYHANDLE, $msg); };
    and then pass $logger into the framework. Of course that's a lot more typing than we'd like so with AutoCurry you can just do
    my $logger = log_to_handle_c($MYHANDLE);
    the _c at the end means that rather than running the log_to_handle function we want a new coderef which will be suitable for the framework.

      Do you know of a module that takes a callback for logging purposes?


      Examine what is said, not who speaks.
      "Efficiency is intelligent laziness." -David Dunham
      "Think for yourself!" - Abigail        "Time is a poor substitute for thought"--theorbtwo
      "Memory, processor, disk in that order on the hardware side. Algorithm, algorithm, algorithm on the code side." - tachyon
        I can't think of one off hand that uses it for logging but why not?

        You can't just pass a filehandle because then you cannot do interesting things with a message before logging it or maybe decide not to log it at all (unless you tie the filehandle but please don't go there).

        The only other way that has the same flexibility is to pass in a logging object, which has a log() method.

        When it comes down to it, a closure and an object with one method are pretty much equivalent, they both store a state and they both have a behaviour which can be invoked. The difference is that it's a lot easier to construct a closure on the fly whereas an object needs an entire class.

        The Tk framework is full of things that take a callback. Tk programmers have to write lots of closures. I used to do loads of Tk work (back in the 2oth century :-) and looking back, I frequently found myself currying functions although I didn't have a name for it at the time.

        Frameworks in functional languages like lisp and Haskell do this too because it's easy to do for them as currying is built in and is the "natural" way to do this for their programmers.

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others musing on the Monastery: (7)
As of 2020-12-03 08:21 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?
    How often do you use taint mode?





    Results (53 votes). Check out past polls.

    Notices?