Prototyping Subs as func expr, list As In map?

  • Thread starter Veli-Pekka Tätilä
  • Start date
V

Veli-Pekka Tätilä

Hi,
I think I've noticed a discrepancy about user and built-in functions taking
code refs. Where as, say, List::Util::reduce (prototyped &@) let's one pass
a code ref, map and grep enable one to also pass an expression as the first
argument. So:

both:

map { lc } @list;

and

map lc, @list;

Do the same thing. Conceptually, the expression is passed as a whole and
evaluated lazily later on as though it was a sub-routine, I suppose.

But in this snippet dealing with a user function, trying to use an
expression terminated by a comma throws a syntax error:

reduce { $a * $b } @list;

reduce $a * $b, @list; # Won't compile.

That is:

Type of arg 1 to List::Util::reduce must be block or sub {} (not
multiplication (*))

Apparently the built-ins don't parse quite the same way as the
user-functions prototyped with the & character. This notion is backed up by
the prototype function. Asking it for the map prototype just hands me undef
unless I've typoed:

print prototype 'CORE::map';

Not being able to prototype reduce and my own list functions as

func EXPR, LIST

is only slightly annoying. However, I'd like to ask why this difference
exists. That is, why not interpret the first argument & like map and grep do
it, when it isn't a code ref? From the user functions point of view it
could be indistinguishable from a normal code ref.

The only downside I can see is not being able to use something totally
different from a code ref as the first argument, of a user sub then. I'm not
sure how common that is, though. Maybe Perl could do a heuristic guess as
to whether you ment an expression to be evaluated as a coderef (operands and
operators) or wanted to pass around a simple variable in a user function. A
safer way would be reserving a different prototype character for the
map-like behavior, I suppose. These are just some vague suggestions that
occurred to me as I don't know all that much about Perl parsing.

Speaking of functions in which the next comma in the list has "special
significance", unary list operators come to mind:

print lc 'FOO', 'bar';

lc's argument list is ended by the first comma in prints arguments. Kind of
like, by association, how the first comma in map separates its expression
and list parts.

I suppose working exactly like map or grep might cause additional problems
with some other Perl constructs I have not thought of. Nevertheless, it
would be the expected behavior for me, or maybe I've got unusual
expectations, <grin>. At any rate, I've got a feeling I've overlooked
something essential which would prevent the func EXPR, LIST construct from
working well for user functions. Wonder what that might be or have I
answered my own question already? That is too much ambiguity, the
possibility of breaking old code and serious limitations on the polymorphism
of sub-routine arguments. And all this for a mere minor inconvenience that's
easily fixed by using braces.
 
B

Ben Morrow

Quoth "Veli-Pekka Tätilä said:
Hi,
I think I've noticed a discrepancy about user and built-in functions taking
code refs. Where as, say, List::Util::reduce (prototyped &@) let's one pass
a code ref, map and grep enable one to also pass an expression as the first
argument. So:

both:

map { lc } @list;

and

map lc, @list;

Do the same thing. Conceptually, the expression is passed as a whole and
evaluated lazily later on as though it was a sub-routine, I suppose.

But in this snippet dealing with a user function, trying to use an
expression terminated by a comma throws a syntax error:

reduce { $a * $b } @list;

reduce $a * $b, @list; # Won't compile.

That is:

Type of arg 1 to List::Util::reduce must be block or sub {} (not
multiplication (*))

Apparently the built-ins don't parse quite the same way as the
user-functions prototyped with the & character.

This is correct. There is no way to get a perl sub to parse the way map
does...
This notion is backed up by
the prototype function. Asking it for the map prototype just hands me undef
unless I've typoed:

print prototype 'CORE::map';

....which is why it doesn't have a prototype :).
Not being able to prototype reduce and my own list functions as

func EXPR, LIST

is only slightly annoying. However, I'd like to ask why this difference
exists. That is, why not interpret the first argument & like map and grep do
it, when it isn't a code ref? From the user functions point of view it
could be indistinguishable from a normal code ref.

The only downside I can see is not being able to use something totally
different from a code ref as the first argument, of a user sub then.

I don't understand this: a &-prototyped sub can only accept a coderef.
Thus, if the first arg is not brace-delimited, it is an EXPR instead. I
suspect that map and grep are heavily special-cased in the lexer and
parser, and it would be hard to get user subs to accept EXPR as well.
Speaking of functions in which the next comma in the list has "special
significance", unary list operators come to mind:

print lc 'FOO', 'bar';

lc's argument list is ended by the first comma in prints arguments. Kind of
like, by association, how the first comma in map separates its expression
and list parts.

lc can be emulated precicely, however:

~% perl -le'print prototype "CORE::lc"'
;$

A ($) or (;$) prototype is parsed exactly like builtin unary ops.

Ben
 
V

Veli-Pekka Tätilä

Ben said:
Quoth "Veli-Pekka Tätilä" <[email protected]>:
This is correct. There is no way to get a perl sub to parse the way map
does...
OK, that's what I thought, too. Thanks for confirming.
I don't understand this: a &-prototyped sub can only accept a coderef.
<snip>
In the above passage, I speculated a bit unclearly that: if map-like
user-functions interpreted anything that was not a coderef as though it was
an EXPR and put it inside a sub-routine, you could not pass such functions
any other type of first arguments then. But you would, however, get more
map-like behavior, I suppose. This is easier explained in perl than my
second language English skills, <smile>. Suppose that:

myMapLike EXPR, @list;

would be auto-converted to

myMapLike { EXPR } @list; # For convenience.

provided that:

ref(EXPR) ne 'CODE';
suspect that map and grep are heavily special-cased in the lexer and
parser, and it would be hard to get user subs to accept EXPR as well.
Impractical probably but hwy would it be hard? Couldn't you use the same
processing as for map and grep, when the usage resembles them? But one
would have to design the rest of the cases, too, such as whether this
map-like processing would work for other positional arguments than just the
first. There are probably other issues I've missed.

Hope this clarifies my original post.
 
B

Ben Morrow

Quoth "Veli-Pekka Tätilä said:
OK, that's what I thought, too. Thanks for confirming.

In the above passage, I speculated a bit unclearly that: if map-like
user-functions interpreted anything that was not a coderef as though it was
an EXPR and put it inside a sub-routine, you could not pass such functions
any other type of first arguments then.

And I said, basically, that this was not a problem as a (&@) prototype
is not allowed to accept any other type of first argument.
But you would, however, get more
map-like behavior, I suppose. This is easier explained in perl than my
second language English skills, <smile>. Suppose that:

myMapLike EXPR, @list;

would be auto-converted to

myMapLike { EXPR } @list; # For convenience.

provided that:

ref(EXPR) ne 'CODE';

Or rather, provided EXPR doesn't start with a { (or \&).
Impractical probably but hwy would it be hard? Couldn't you use the same
processing as for map and grep, when the usage resembles them?

Well, I can't really follow all the logic (in particular, I can't see
where the MAPSTART and GREPSTART opcodes are generated), but there is a
whole lot of stuff in toke.c/perly.y that actually starts lexing things
differently immediately after 'map' or 'grep'. Extending that to include
user-defined sub names may be tricky.

Ben
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top