My original title was "Another context trap", but I changed my mind. Read on.
Easy things are supposed to be easy to write in Perl, right? Let's try this one: write an (efficient) identity subroutine — one that returns the arguments given to it.
Ok. Now you write down your solution and come back to read the rest of it.
…
That was my first attempt:
my $id = sub { return @_; }
And it looked like a good idea at first, as the @_
gets all the sub arguments (whether they be zero, one or many). And it seemingly worked.
$id->() returns () $id->(42) returns (42) $id->(42,42) returns (42,42)
Except that I was not totally honest, and these succeed only when the function is called at list context. If that's not the case, we have:
$c = $id->(42); # 1 gets assigned to $c (from scalar @_) ($a) = $id->(42); # good, 42 gets assigned to $c
The quick solution is to take into account the calling context and write down the code as:
$id = sub { wantarray : @_ ? $_[0] }
The fault of this is a Perl feature that most other programming languages don't have: sensitivity to calling context. Those others are happily relieved of this because they are poorer in semantics than Perl in that aspect.
The identity function needs to be defined from a domain into the same domain: A -> A
. Then, if we consider the non-void contexts Perl 5 provides: list and scalar, we have not one but two identity functions to consider. Namely,
sub { @_ } # for list context, LIST -> LIST sub ($) { $_[0] } # for scalar context, SCALAR -> SCALAR
Note. The sneaky prototype in the last sub definition entered the scene to guarantee the homomorphism between the domain and image, that was not tested in the previous definition. So to be 100% correct, that should do it:
sub { if ( wantarray ) { @_; } else { die "too many arguments" unless @_==1; $_[0]; } }
The trap in this case is more like a fault of all other programming languages that don't have call context sensitivity. Because if they did, like Perl, they would push you up to think harder about some concepts like identity itself.
What Perl 6 will have to do with this? You bet no one is going to draw back and "relieve" Perl 6 of that extra expressiveness her mother language has. On contrary, Perl 6 will have even more call context sensitivity. But IIRC you will be able to define nice multisubs which dispatch on return type and will avoid unintented uses.
Hm, how they would look like? I am not sure. Maybe
multi sub id(Any $x -> Any: ) { $x }