in reply to coderefs and (&) prototypes

What confuses me is that, in Prototypes, it is suggested that a function prototyped (&@) behaves like grep. However, whereas one calls
grep &$code, @list
to find all the elements $_ of @list for which $code->($_) is true, for a hypothetical mygrep (&@), one would call
mygrep \&$code, @list
to achieve the same goal.

ikegami pointed out yet another subtlety, which is that grep cannot accept a ‘calculated’ first argument. Thus,

sub mygrep (&@) { my ( $code, @list ) = @_; return grep &$code, @list +} sub gt { my ( $test ) = @_; return sub { $_ > $test } } grep &{ gt 1 }, qw/1 2 3 1 4/; => () mygrep \&{ gt 1 }, qw/1 2 3 1 4/; => ( 2, 3, 4 )
(While I'm at it, ikegami also pointed out that my first example could be written as grep $code->(), @list, which I find completely bizarre.)

UPDATE: Actually, having just run the code, it seems to me that the grep and mygrep invocations actually return the same thing (namely, (2, 3, 4)), so I must have misunderstood. Can anyone (like ikegami :-) ) clarify for me?

Replies are listed 'Best First'.
Re^2: coderefs and (&) prototypes
by ikegami (Patriarch) on Jul 28, 2009 at 01:42 UTC

    The execution of the argument passed to grep is deferred until after grep is called, and it is called once for every following argument.

    sub func {} my $x; sub foo { ++$x } $x = 0; func foo(), 1,2,3; print "$x\n"; # 1 $x = 0; grep foo(), 1,2,3; print "$x\n"; # 3

    grep and map don't have a prototype since this ability can't be prototyped.

    You seem to think there's something special about &cb when passed to grep. It's not.

    grep &cb, ... === grep { &cb } ... grep &cb(), ... === grep { &cb() } ... grep $cb->(), ... === grep { $cb->() } ... grep /.../, ... === grep { /.../ } ... grep substr($_, 2), ... === grep { substr($_, 2) } ...

    (minus the extra scope).

    &cb means "call non-builtin cb without changing or localising @_", whether it's the first arg of grep or otherwise.

    grep cannot accept a ‘calculated’ first argument. [...] Can anyone (like ikegami :-) ) clarify for me?

    Using map since it's easier to visualize,

    sub mymap (&@) { my ( $code, @list ) = @_; map &$code, @list } sub get_cb { return sub { uc } } print(( mymap \&{ &get_cb }, qw( a b c ) ), "\n"); print(( map &get_cb, qw( a b c ) ), "\n"); print(( map get_cb, qw( a b c ) ), "\n"); print(( map get_cb(), qw( a b c ) ), "\n");
    ABC CODE(0x1829a54)CODE(0x1829a54)CODE(0x1829a54) CODE(0x1829a54)CODE(0x1829a54)CODE(0x1829a54) CODE(0x1829a54)CODE(0x1829a54)CODE(0x1829a54)

    Just can't do it. The first arg of map and grep is never evaluated before map or grep is called.

      Thanks very much for the clarification. I keep forgetting that the sub passed to map operates on $_ rather than receiving its argument in @_, so I'm always writing something like map &f, qw/1 2 3/ and wondering why I don't get (f(1), f(2), f(3)).

      For your second example, though, when I tested it, the code

      print(( map &{ get_cb() }, qw/a b c/), "\n");
      printed ABC, just as mymap did. Am I still missing something?

        sub mymap (&@) { my ( $code, @list ) = @_; map &$code, @list } my @cbs; sub reset_cbs { @cbs = ( sub { uc }, sub { lc } ); } sub get_cb { push @cbs, shift(@cbs); return $cbs[0]; } reset_cbs(); print(( mymap \&{ get_cb() }, qw( a b c d ) ), "|"); print(( mymap \&{ get_cb() }, qw( a b c d ) ), "\n"); reset_cbs(); print(( map &{ get_cb() }, qw( a b c d ) ), "|"); print(( map &{ get_cb() }, qw( a b c d ) ), "\n");
        abcd|ABCD aBcD|aBcD
Re^2: coderefs and (&) prototypes
by LanX (Saint) on Jul 27, 2009 at 23:42 UTC
    What confuses me is that, in Prototypes, it is suggested that a function prototyped (&@) behaves like grep.

    The answer is simple! IMHO perlsub is wrong at this place and you proved it! 8)

    Congratulations we found the second error in this perldoc chapter within two weeks!

    Should be noted that CORE::map has no prototype to be copied, it has hidden magic behavior:

    print undef==prototype "CORE::map" # prints 1

    IMHO if there was a possibility to express the behavior with prototypes, perl would use it internally!

    Cheers Rolf