|
| 1 | +# Allow calling subs using method-like syntax |
| 2 | + |
| 3 | +## Preamble |
| 4 | + |
| 5 | + Author: Graham Knop <[email protected]> |
| 6 | + ID: 0033 |
| 7 | + Status: Implemented |
| 8 | + |
| 9 | +## Abstract |
| 10 | + |
| 11 | +Add a syntax for calling lexical methods that is similar to the existing |
| 12 | +method syntax, rather than needing to call them like subs. The new syntax |
| 13 | +`$object->&method(@args)` would be equivalent to sub call to `method`, but |
| 14 | +using a syntax closer to method calls. |
| 15 | + |
| 16 | +## Motivation |
| 17 | + |
| 18 | +When using a lexical sub inside an object class, the subs must be called as |
| 19 | +`method($object, @args)`. This works, but for many people, this feels wrong |
| 20 | +as a way to call something thought of as a private method. This has resulted |
| 21 | +in some people using syntax like `$object->${\&method}(@args)`, or continuing |
| 22 | +to use code refs stored in a lexical variable (`$object->$method(@args)`). |
| 23 | + |
| 24 | +## Rationale |
| 25 | + |
| 26 | +`$object->method(@args)` always does a method lookup, ignoring the local |
| 27 | +context. Changing this would result in a lot of broken code. Changing this |
| 28 | +only for new code (via feature or other lexical effect) would introduce |
| 29 | +confusion and would also require some new (or pessimised) syntax to allow |
| 30 | +the previous behavior. |
| 31 | + |
| 32 | +`$object->&method(@args)` would be an unambiguous way to call the `method` in |
| 33 | +the current scope, whether a lexical or package sub. |
| 34 | + |
| 35 | +A truly private method does not need to do any lookup via `@ISA`, so being |
| 36 | +equivalent to a sub call makes sense. |
| 37 | + |
| 38 | +This also would pair well with |
| 39 | +[PPC0021](ppc0021-optional-chaining-operator.md) to allow optional sub calls |
| 40 | +as part of a chain. |
| 41 | + |
| 42 | +## Specification |
| 43 | + |
| 44 | +`$object->&method(@args)` would behave exactly the same as `&method($object, |
| 45 | +@args)`. `method` would be searched for in the current lexical scope. If not |
| 46 | +found, it would be looked up in the current package. If still not found, the |
| 47 | +current package's `AUTOLOAD` would be called. If `AUTOLOAD` does not exist, an |
| 48 | +error would be issued. The call would ignore prototypes, just as traditional |
| 49 | +method calls and `&subroutine()` calls do. |
| 50 | + |
| 51 | +Methods defined using the `class` syntax would also be callable using this |
| 52 | +syntax. |
| 53 | + |
| 54 | +## Backwards Compatibility |
| 55 | + |
| 56 | +The syntax `->&word` is currently a syntax error, so no backwards |
| 57 | +compatibility issues are forseen. |
| 58 | + |
| 59 | +## Security Implications |
| 60 | + |
| 61 | +As this is purely a syntax feature that is currently a syntax error, no |
| 62 | +security implications are forseen. |
| 63 | + |
| 64 | +## Examples |
| 65 | + |
| 66 | +```perl |
| 67 | +use v5.36; |
| 68 | + |
| 69 | +package Foo { |
| 70 | + sub new ($class) { |
| 71 | + return bless { |
| 72 | + field => 5, |
| 73 | + }, $class; |
| 74 | + } |
| 75 | + |
| 76 | + my sub private ($self) { |
| 77 | + return $self->{field}; |
| 78 | + } |
| 79 | + |
| 80 | + sub _old_school_private ($self) { |
| 81 | + return $self->{field}; |
| 82 | + } |
| 83 | + |
| 84 | + sub public ($self) { |
| 85 | + |
| 86 | + # call via lexical method is allowed |
| 87 | + say "my field: " . $self->&private; |
| 88 | + # exactly equivalent to: |
| 89 | + say "my field: " . private($self); |
| 90 | + |
| 91 | + |
| 92 | + # can also be used to call package methods. But won't look up via @ISA |
| 93 | + say "my field: " . $self->&_old_school_private; |
| 94 | + # exactly equivalent to: |
| 95 | + say "my field: " . _old_school_private($self); |
| 96 | + } |
| 97 | +} |
| 98 | + |
| 99 | +my $foo = Foo->new; |
| 100 | +$foo->public; |
| 101 | + |
| 102 | +# this is an error, as there is no sub named "private" in scope |
| 103 | +$foo->&private; |
| 104 | + |
| 105 | +# this is also an error, as "public" does not exist in the current scope |
| 106 | +$foo->&public; |
| 107 | +``` |
| 108 | + |
| 109 | +## Prototype Implementation |
| 110 | + |
| 111 | +* [Object::Pad::LexicalMethods](https://metacpan.org/pod/Object::Pad::LexicalMethods) |
| 112 | + implements lexical methods and `->&` method calls for |
| 113 | + [Object::Pad](https://metacpan.org/pod/Object::Pad). |
| 114 | + |
| 115 | +## Future Scope |
| 116 | + |
| 117 | +Lexical methods (`my method foo`) are not currently supported under the |
| 118 | +`class` syntax. If they were added, `->&method` would be the preferred way to |
| 119 | +call them. |
| 120 | + |
| 121 | +## Rejected Ideas |
| 122 | + |
| 123 | +Allowing `$object->method` to call lexical subs would break existing code and |
| 124 | +would interfere with the ability to call the actual method. Perl doesn't know |
| 125 | +what type `$object` is, so it can't know when it is appropriate to do method |
| 126 | +lookup and when to call a local sub. Using a different syntax at the call site |
| 127 | +avoids these issues. |
| 128 | + |
| 129 | +## Open Issues |
| 130 | + |
| 131 | +None currently. |
| 132 | + |
| 133 | +## Copyright |
| 134 | + |
| 135 | +Copyright (C) 2025, Graham Knop |
| 136 | + |
| 137 | +This document and code and documentation within it may be used, redistributed |
| 138 | +and/or modified under the same terms as Perl itself. |
0 commit comments