The interesting thing about &
is that you can generate new syntax with it, provided it's in the initial position:
- sub try (&@) {
- my($try,$catch) = @_;
- eval { &$try };
- if ($@) {
- local $_ = $@;
- &$catch;
- }
- }
- sub catch (&) { $_[0] }
- try {
- die "phooey";
- } catch {
- /phooey/ and print "unphooey\n";
- };
That prints "unphooey"
. (Yes, there are still unresolved issues having to do with visibility of @_
. I'm ignoring that question for the moment. (But note that if we make @_
lexically scoped, those anonymous subroutines can act like closures... (Gee, is this sounding a little Lispish? (Never mind.))))
And here's a reimplementation of the Perl grep
operator:
- sub mygrep (&@) {
- my $code = shift;
- my @result;
- foreach $_ (@_) {
- push(@result, $_) if &$code;
- }
- @result;
- }
Some folks would prefer full alphanumeric prototypes. Alphanumerics have been intentionally left out of prototypes for the express purpose of someday in the future adding named, formal parameters. The current mechanism's main goal is to let module writers provide better diagnostics for module users. Larry feels the notation quite understandable to Perl programmers, and that it will not intrude greatly upon the meat of the module, nor make it harder to read. The line noise is visually encapsulated into a small pill that's easy to swallow.
If you try to use an alphanumeric sequence in a prototype you will generate an optional warning - "Illegal character in prototype...". Unfortunately earlier versions of Perl allowed the prototype to be used as long as its prefix was a valid prototype. The warning may be upgraded to a fatal error in a future version of Perl once the majority of offending code is fixed.
It's probably best to prototype new functions, not retrofit prototyping into older ones. That's because you must be especially careful about silent impositions of differing list versus scalar contexts. For example, if you decide that a function should take just one parameter, like this:
and someone has been calling it with an array or expression returning a list:
- func(@foo);
- func( split /:/ );
Then you've just supplied an automatic scalar
in front of their argument, which can be more than a bit surprising. The old @foo
which used to hold one thing doesn't get passed in. Instead, func()
now gets passed in a 1
; that is, the number of elements in @foo
. And the split
gets called in scalar context so it starts scribbling on your @_
parameter list. Ouch!
This is all very powerful, of course, and should be used only in moderation to make the world a better place.