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

Hi,

I was tempted to post this to the "Perl News" section ... but then I figured that, although it was "news to me", it probably wasn't to many of those that peruse this forum. (Actually, I'm not even sure that this *is* "news to me" ... there's an unsettling deja-vu that has me scanning the horizon for eggs heading my way.)
The demo is:
use warnings; use subs qw(non_commutative); sub non_commutative {return 17} $x = 10 * non_commutative; $y = non_commutative * 10; print "$x\n$y\n\n"; $x = 10 + non_commutative; $y = non_commutative + 10; print "$x\n$y\n"; __END__ Outputs: 170 17 27 17
and the output proves, without any shadow of a doubt, that neither multiplication nor addition are commutative.

The thing that amazes me the most is that the subs documentation says simply:

This will predeclare all the subroutine whose names are in the list, allowing you to use them without parentheses even before they're declared.

But that is clearly not so - *including* the parentheses in the above script alters the output (and restores commutativity):
use warnings; use subs qw(non_commutative); sub non_commutative {return 17} $x = 10 * non_commutative(); $y = non_commutative() * 10; print "$x\n$y\n\n"; $x = 10 + non_commutative(); $y = non_commutative() + 10; print "$x\n$y\n"; __END__ Outputs: 170 170 27 27
That's a fairly significant caveat that has been ignored in the subs documentation.
The docs do go on to say:

Unlike pragmas that affect the $^H hints variable, the "use vars" and "use subs" declarations are not BLOCK-scoped. They are thus effective for the entire file in which they appear. You may not rescind such declarations with "no vars" or "no subs".

See "Pragmatic Modules" in perlmodlib and "strict subs" in strict.


Is this explained in either "Pragmatic Modules" or "strict subs" ? ... I wouldn't expect so, though apathy has prevented me from actually checking.

I guess there's a good explanation documented somewhere ?

Cheers,
Rob

UPDATE: Added the ouptuts my demos were producing. Thanks JavaFan.

Replies are listed 'Best First'.
Re: Perl disproves commutativity of Addition/Multiplication
by moritz (Cardinal) on Jan 23, 2012 at 11:29 UTC
    But that is clearly not so - *including* the parentheses in the above script alters the output (and restores commutativity):

    Of course it alters the output. You are allowed to use predeclared subroutines without parenthesis, but the docs don't say that you always get the same result as with parens -- in the case without parens, you need to be aware of the precedence of your expressions.

    In particular non_commutative + 10 parses as non_commutative( +10 ). You meant non_commutative() + 10, but perl can't know that; you'd have to write (non_commutative) + 10 to disambiguate.

    In case you wonder what the *10 is: as a term that's the glob syntax that you might know from *STDOUT.

    The precedence of subroutines without parenthesis is described in perlop as "list operators (rightward)", and looser than + and *.

    Another option is to declare the sub with a prototoype:

    sub non_commutative() { 17 }

    Which is described in perlsub.

      You are allowed to use predeclared subroutines without parenthesis, but the docs don't say that you always get the same result as with parens

      You're right ... being "allowed" to do something doesn't mean anything more than it "won't produce a fatal error". (Got suckered by that one ;-)

      Not that I'm interested in pursuing the matter, but I think the documentation *could* have taken the extra step of warning that removing the parens could cause unexpected results. I see other documentation that takes that extra step, and I (fallaciously) start to believe that if the docs don't mention any traps then there aren't any.

      Thanks to all respondents.

      Cheers,
      Rob

        Then by all means, submit a patch to p5p that improves the documentation.

        In my experience, the Perl 5 hackers are very open to doc improvements, and haven't rejected any of my doc patches. You really should try it.

Re: Perl disproves commutativity of Addition/Multiplication
by BrowserUk (Patriarch) on Jan 23, 2012 at 11:36 UTC

    All that is happening is that the postfix + 10 and * 10 are being taken as arguments to be passed to the subroutine.

    Kinda like the sub was declared with a ($) prototype:

    use warnings; use subs qw(non_commutative); sub non_commutative { print "?@_?\n"; return 17 } $x = 10 * non_commutative; $y = non_commutative * 10; print "$x\n$y\n\n"; $x = 10 + non_commutative; $y = non_commutative + 10; print "$x\n$y\n"; __END__ c:\test>junk19 ?? ?*main::10? 170 17 ?? ?10? 27 17

    With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
    Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
    "Science is about questioning the status quo. Questioning authority".
    In the absence of evidence, opinion is indistinguishable from prejudice.

    The start of some sanity?

Re: Perl disproves commutativity of Addition/Multiplication
by Anonymous Monk on Jan 23, 2012 at 11:29 UTC

    Your code as posted

    $ perl -MO=Deparse,-p junk use subs ('non_commutative'); sub non_commutative { use warnings; return(17); } use warnings; ($x = (10 * non_commutative())); ($y = non_commutative(*10)); print("$x\n$y\n\n"); ($x = (10 + non_commutative())); ($y = non_commutative(10)); print("$x\n$y\n"); junk syntax OK

    If you want a function that doesn't take arguments, use a prototype of (), as in sub non_commutative() {return 17}

    $ perl -MO=Deparse,-p junk sub non_commutative () { use warnings; return(17); } use warnings; ($x = (10 * non_commutative)); ($y = (non_commutative * 10)); print("$x\n$y\n\n"); ($x = (10 + non_commutative)); ($y = (non_commutative + 10)); print("$x\n$y\n"); junk syntax OK

    or use constant non_commutative => 17;

    $ perl -MO=Deparse,-p junk use constant ('non_commutative', 17); use warnings; ($x = 170); ($y = 170); print("$x\n$y\n\n"); ($x = 27); ($y = 27); print("$x\n$y\n"); junk syntax OK
Re: Perl disproves commutativity of Addition/Multiplication
by Anonymous Monk on Jan 23, 2012 at 11:33 UTC
    The subs pragma is a red herring. Remove it, and the problem still holds.

    The explanation is in the constant pragma, and some book I cannot remember explains that you should use () for constant-style sub calls because non_commutative * 10 parses as non_commutative(*10).

      The subs pragma is a red herring. Remove it, and the problem still holds.

      Quite so.
      If I'm not mistaken, moving the definition of the non_commutative sub to the end of the script *does* create a purpose for loading the 'subs' pragma iff we want to avoid the use of parens - and it would have been better if the scripts I had presented did precisely that:
      use warnings; use subs qw(non_commutative); #necessary $x = 10 * non_commutative; $y = non_commutative * 10; print "$x\n$y\n\n"; $x = 10 + non_commutative; $y = non_commutative + 10; print "$x\n$y\n"; sub non_commutative {return 17} __END__ Outputs: 170 17 27 17
      and
      use warnings; use subs qw(non_commutative); #unnecessary $x = 10 * non_commutative(); $y = non_commutative() * 10; print "$x\n$y\n\n"; $x = 10 + non_commutative(); $y = non_commutative() + 10; print "$x\n$y\n"; sub non_commutative {return 17} __END__ Outputs: 170 170 27 27
      Cheers,
      Rob
Re: Perl disproves commutativity of Addition/Multiplication
by JavaFan (Canon) on Jan 23, 2012 at 14:18 UTC
    the output proves
    What output? Do you think you're that important that the dozens of people reading your post should cut and paste your code and run it to see the output, instead of you spending two seconds actually posting the output?

    The explanation has already been posted, but for everyone reading this: if you're going to post code, and its output matters, post the output as well. Don't expect everyone to either magically deduce what the output will be, or to copy, paste and run it.

      Do you think you're that important that the dozens of people reading your post should cut and paste your code and run it to see the output, instead of you spending two seconds actually posting the output?

      How important I think I am is:
      a) off topic for this forum;
      b) none of your business.

      But thank you for drawing my attention to my omission - and in such a gracious manner.

      Cheers,
      Rob