Perl's "Package::->method()" (colon-colon-arrow) notation
Asked Answered
W

1

7

I was reading the documentation for the attributes module and came across a method call notation I've never seen:

use attributes ();
attributes::->import(__PACKAGE__, \$x, 'Bent');

The doc did not provide an explanation for this syntax.

On investigation, it seems Package::->method() is equivalent to Package->method() and 'Package'->method().

It also seems equivalent to Package::method('Package'), provided the method is not inherited.

Question 0: Is there any practical reason for using Package::->method() notation?


Edit: I found if you have a constant with the same name as a package, Package::->method() calls the package method, whereas Package->method() calls the method on the constant.

use constant Foo => bless {}, 'Bar'; # or just => 'Bar'

print Foo::->method(); # prints 'Foo method'
print 'Foo'->method(); # prints 'Foo method'
print Foo->method();   # prints 'Bar method'

package Foo;
sub method { 'Foo method' }
package Bar;
sub method { 'Bar method' }

Strangely, with use warnings, this script will give a "Bareword 'Foo::' refers to nonexistent package" warning but will execute it anyway, which just confuses me more.

Even more strangely, adding the line Foo::method(); before the Foo::->method() line gets rid of the "Bareword" warning.

Question 1: Can someone please explain what's going on?

For posterity, this is Perl v5.38.0.

Winou answered 5/9, 2023 at 16:53 Comment(0)
M
9

Package::->method is a (very slightly) safer way of writing Package->method.


This is answered by the documentation. How does Perl parse unquoted bare words? is also relevant.

Foo:: is equivalent to "Foo", except it also checks if namespace Foo exists.

Foo also means "Foo", but only if it doesn't mean anything else. It's exempt from strictures when before ->, but there's otherwise nothing special about Foo here. So if you have a sub called Foo, and it will call that sub. If you have a constant named Foo, it will be used.

In summary,

  • Foo::->method always means "Foo"->method.
  • Foo->method usually means "Foo"->method.
  • Foo->method could also mean Foo()->method.

From that, we derive the following reasons for using Foo:::

  • By using Foo::, you avoid calling a sub by accident. (This includes constants.)

    The chances of this happening are incredibly small in practice. And it's something that would be found in testing, one hopes. But given the rarity of occurrences, it could be confusing were it to ever happen.

  • By using Foo::, you signal that it's not a sub call.

    But that's already the presumption when you see something of the form Foo->method. It's more important to signal when it is a sub call (for example, by using Foo()->method).

  • By using Foo::, you get better diagnostics if you forget to load the module.

    You get

    Bareword "Foo::" refers to nonexistent package at ...
    Can't locate object method "method" via package "Foo" (perhaps you forgot to load "Foo"?)` at ...
    

    instead of

    Can't locate object method "method" via package "Foo" (perhaps you forgot to load "Foo"?)` at ...
    

    Did I say better? That's just noisier to me.

Myrtie answered 5/9, 2023 at 17:12 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.