http://qs321.pair.com?node_id=228384

diotalevi has asked for the wisdom of the Perl Monks concerning the following question:

So I was wondering about this earlier in the week and I've asked the CB occasionally but no one has taken it up yet. If I write $method = $object -> can( 'method' ) I'd expect that to be used like $method -> ( @arguments ) instead of $object -> $method ( @arguments ). The current implementation allows you to do totally lame-brained things like calling methods from one object on another $object2 -> $method, using it as a class method ref($object) -> $method or not in OO at all $method -> ().

So this means that $method should be a closure to whatever it's a method for instead of just a code reference. @_ would need to get the object/class somehow to keep the current OO-argument passing behaviour. So why doesn't it work like this? I look at can() and see a question about what that object/class can do. The other part though - the code reference is much more general and doesn't quite answer the same question.

Replies are listed 'Best First'.
Re: Why isn't ->can() curried? (power)
by tye (Sage) on Jan 20, 2003 at 18:21 UTC

    Because that would be less powerful and slower. Each call to can() would have to create a closure but, in 90% of the cases, it would be almost immediately destroyed.

    With the current implementation you can get what you want rather easily. [ This is shown elsewhere in this thead, but please don't override UNIVERSAL::can() as you'll just break other code in some module that is used by some module... that you used. Instead, provide that functionality via a different method name. ] But if your implementation were the default then there would be no easy way to get the current information.

    It would no longer work to say     $o1->can($m) == $o2->can($m) to see if two objects are using the same implementation of a method, for example.

                    - tye
Re: Why isn't ->can() curried?
by Elian (Parson) on Jan 20, 2003 at 18:18 UTC
    It isn't curried becausel perl 5 doesn't do currying. That makes things a little tough...

    On a more general note, perl 5's OO support is really loose, and very minimalistic. Whether that's a good or bad thing is a matter for a different argument, but it is, nonetheless, true. What you're looking to do, while useful, steps away from that level of minimalism.

    It's certainly a reasonable thing to ponder for perl 6, but I'd say at this point perl 5 is awfully far along in its development to change core behaviour like that. And if you were going to do it, you'd really want a full second layer on top of the current base, rather than adding in bits and pieces. While that'd certainly be a good thing, it does mean a bit of extra up-front thought to build that layer so it has some internal consistency.

    Having said that, you can always write a module to handle this... :)

Re: Why isn't ->can() curried?
by broquaint (Abbot) on Jan 20, 2003 at 17:17 UTC
    Would the behaviour be something like this
    use strict; use warnings; { my $old_can = *UNIVERSAL::can{CODE}; no warnings 'redefine'; *UNIVERSAL::can = sub { my($class,$method) = @_; my $ret = $old_can->($class, $method); return sub { warn "$class->$ret( @_ )\n"; $class->$ret( @_ ) }; }; } sub foo::bar { print "got: @_[1 .. $#_]\n"; } my $m = foo->can("bar"); $m->(qw(ichi ni san shi)); __output__ foo->CODE(0x81080f8)( ichi ni san shi ) got: ichi ni san shi
    Or something else perhaps?
    HTH

    _________
    broquaint

        It's far, far too late to make this the default behaviour. This'd be a matter for a new method in UNIVERSAL (which has its own issues) or a middle OO layer.
Re: Why isn't ->can() curried?
by Anonymous Monk on Jan 20, 2003 at 18:33 UTC
    When I have wanted to use can for anything other than the truth value I have generally wanted to either verify which function implements it (which your suggestion breaks) or else I have wanted to wrap the existing implementation with another and it is not certain that I want to necessarily use the same object. Indeed I might locate can with the class and then feed it real objects later.

    I don't want to do either often, but on the rare occasions when I do, your suggestion is less convenient than the current behaviour.

    But more than that, the vast majority of the time when people want to check can they just want to check truth. Why create a closure just to see it get thrown away?

    But if you really want it, just create another method to do what you want:

    sub UNIVERSAL::bind_meth { my ($self, $meth, @args) = @_; my $func = $self->can($meth); if (defined($func)) { return sub {$func->($self, @args, @_)}; } else { return undef; } }
    And now you can curry a method, and curry a few arguments as well while you are at currying favour. And I still get the can that I know and like.

      This is what comes of spending time with other languages. *grin* In this case it's E which has a home page at http://www.erights.org. It had never occured to me that people would want to compare the code references from ->can. When is that useful?


      Seeking Green geeks in Minnesota

        One use is to verify that a subclass has actually implemented an abstract interface.
Re: Why isn't ->can() curried?
by chromatic (Archbishop) on Jan 20, 2003 at 17:33 UTC

    How do you tell, programatically, whether the function is intended to be called as a method or a function?

      You wouldn't. But then I think it'd be pretty daft to call a method as a function seeing as how the implementing code is expecting an object as the first parameter.


      Seeking Green geeks in Minnesota

        Paint me daft, then, I do it all the time in tests. :)

        Perhaps any extra arguments to can() could be bound to the subref returned. It's a nice idea; it just seems really hard to get super-scary magic right. Here's some untested code that seems somewhat useful:

        use Scalar::Util 'blessed'; sub curried_can { my ($thingie, $func) = splice( @_, 0, 2 ); my $sub = UNIVERSAL::can( $thingie, $func ); # $thingie cannot $func return unless $sub; # first argument is an object return sub { $thingie->$func( @_ ) } if blessed $thingie; # first argument is a class name, no arguments return $sub unless @_; # first argument is a class name, curried arguments return sub { $sub->( @_ ) }; }
Re: Why isn't ->can() curried?
by hardburn (Abbot) on Jan 20, 2003 at 17:04 UTC

    The current implementation allows you to do totally lame-brained things . . .

    Those things things are allowed anyway. Remember: Perl allows you to do anything you want. There is no such thing as truly private data in Perl the same way it is in Java (for example). Perl's way of doing private data is to make it inconvieant to access with a little comment that says "anybody who uses this deserves what they get" :)

      But since this isn't about private data... Anyhow I've got no problem with people who can go grab or alter the object in the method. I just think the object should be there. Right now it's not. This is a completely different concern.


      Seeking Green geeks in Minnesota