From 8bfeb77b71045cf37c39397b8ac1c3c3a27c0c5c Mon Sep 17 00:00:00 2001 From: Graham Knop <haarg@haarg.org> Date: Wed, 22 Jan 2025 17:53:23 +0100 Subject: [PATCH 1/7] PPC0033: method-like calls to lexical subs Add a syntax $object->&method(@args) that calls method as a sub, to allow a nicer syntax for private methods. --- ppcs/ppc0033-ampersand-method-calls.md | 129 +++++++++++++++++++++++++ 1 file changed, 129 insertions(+) create mode 100644 ppcs/ppc0033-ampersand-method-calls.md diff --git a/ppcs/ppc0033-ampersand-method-calls.md b/ppcs/ppc0033-ampersand-method-calls.md new file mode 100644 index 0000000..5a599f6 --- /dev/null +++ b/ppcs/ppc0033-ampersand-method-calls.md @@ -0,0 +1,129 @@ +# Allow calling subs using method-like syntax + +## Preamble + + Author: Graham Knop <haarg@haarg.org> + ID: 0033 + Status: Draft + +## Abstract + +Add a syntax for calling lexical methods that is similar to the existing +method syntax, rather than needing to call them like subs. The new syntax +`$object->&method(@args)` would be equivalent to sub call to `method`, but +using a syntax closer to method calls. + +## Motivation + +When using a lexical sub inside an object class, the subs must be called as +`method($object, @args)`. This works, but for many people, this feels wrong +as a way to call something thought of as a private method. This has resulted +in some people using syntax like `$object->${\&method}(@args)`, or continuing +to use code refs stored in a lexical variable. + +## Rationale + +`$object->method(@args)` always does a method lookup, ignoring the local +context. Changing this would result in a lot of broken code. Changing this +only for new code (via feature or other lexical effect) would introduce +confusion and would also require some new (or pessimised) syntax to allow +the previous behavior. + +`$object->&method(@args)` would be an unambiguous way to call the `method` in +the current scope, whether a lexical or package sub. + +A truly private method does not need to do any lookup via `@ISA`, so being +equivalent to a sub call makes sense. + +This also would pair well with +[PPC0021](ppc0021-optional-chaining-operator.md) to allow optional sub calls +as part of a chain. + +## Specification + +`$object->&method(@args)` would behave exactly the same as `&method($object, +@args)`. `method` would be searched for in the current lexical scope. If not +found, it would be looked up in the current package. If still not found, the +current package's `AUTOLOAD` would be called. If `AUTOLOAD` does not exist, an +error would be issued. The call would ignore prototypes, just as traditional +method calls and `&subroutine()` calls do. + +## Backwards Compatibility + +The syntax `->&word` is currently a syntax error, so no backwards +compatibility issues are forseen. + +## Security Implications + +As this is purely a syntax feature that is currently a syntax error, no +security implications are forseen. + +## Examples + +```perl +use v5.36; + +package Foo { + sub new ($class) { + return bless { + field => 5, + }, $class; + } + + my sub private ($self) { + return $self->{field}; + } + + sub _old_school_private ($self) { + return $self->{field}; + } + + sub public ($self) { + + # call via lexical method is allowed + say "my field: " . $self->&private; + # exactly equivalent to: + say "my field: " . private($self); + + + # can also be used to call package methods. But won't lookup via @ISA + say "my field: " . $self->&_old_school_private; + # exactly equivalent to: + say "my field: " . _old_school_private($self); + } +} + +my $foo = Foo->new; +$foo->public; + +# this is an error, as there is no sub named private in scope +$foo->&private; +``` + +## Prototype Implementation + +None currently. + +## Future Scope + +This syntax would also make sense to use for private method calls in the +`class` syntax. + +## Rejected Ideas + +Allowing `$object->method` to call lexical subs would break existing code and +would interfere with the ability to call the actual method. Perl doesn't know +what type `$object` is, so it can't know when it is appropriate to do method +lookup and when to call a local sub. Using a different syntax at the call site +avoids these issues. + +## Open Issues + +None currently. + +## Copyright + +Copyright (C) 2025, Graham Knop + +This document and code and documentation within it may be used, redistributed +and/or modified under the same terms as Perl itself. From a31dd226941f42847fec34c8d997724462078af0 Mon Sep 17 00:00:00 2001 From: Graham Knop <haarg@haarg.org> Date: Tue, 28 Jan 2025 03:38:43 +0100 Subject: [PATCH 2/7] add example of calling a lexical variable --- ppcs/ppc0033-ampersand-method-calls.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ppcs/ppc0033-ampersand-method-calls.md b/ppcs/ppc0033-ampersand-method-calls.md index 5a599f6..90f4f7e 100644 --- a/ppcs/ppc0033-ampersand-method-calls.md +++ b/ppcs/ppc0033-ampersand-method-calls.md @@ -19,7 +19,7 @@ When using a lexical sub inside an object class, the subs must be called as `method($object, @args)`. This works, but for many people, this feels wrong as a way to call something thought of as a private method. This has resulted in some people using syntax like `$object->${\&method}(@args)`, or continuing -to use code refs stored in a lexical variable. +to use code refs stored in a lexical variable (`$object->$method(@args)`). ## Rationale From a6770edcdb5254a5d86562ab025d9a13fd4054ff Mon Sep 17 00:00:00 2001 From: Graham Knop <haarg@haarg.org> Date: Tue, 28 Jan 2025 03:39:11 +0100 Subject: [PATCH 3/7] clarify interactions with "class" syntax Methods under the "class" syntax would also be callable using this syntax. Lexical methods currently don't exist, but they would be callable with this syntax if they were added. --- ppcs/ppc0033-ampersand-method-calls.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/ppcs/ppc0033-ampersand-method-calls.md b/ppcs/ppc0033-ampersand-method-calls.md index 90f4f7e..4d24a03 100644 --- a/ppcs/ppc0033-ampersand-method-calls.md +++ b/ppcs/ppc0033-ampersand-method-calls.md @@ -48,6 +48,9 @@ current package's `AUTOLOAD` would be called. If `AUTOLOAD` does not exist, an error would be issued. The call would ignore prototypes, just as traditional method calls and `&subroutine()` calls do. +Methods defined using the `class` syntax would also be callable using this +syntax. + ## Backwards Compatibility The syntax `->&word` is currently a syntax error, so no backwards @@ -106,8 +109,9 @@ None currently. ## Future Scope -This syntax would also make sense to use for private method calls in the -`class` syntax. +Lexical methods (`my method foo`) are not currently supported under the +`class` syntax. If they were added, `->&method` would be the preferred way to +call them. ## Rejected Ideas From cf907b9a0b0566fe8054a6ebe7ab737198ab08c2 Mon Sep 17 00:00:00 2001 From: Graham Knop <haarg@haarg.org> Date: Tue, 28 Jan 2025 03:40:29 +0100 Subject: [PATCH 4/7] remove lookup as a verb --- ppcs/ppc0033-ampersand-method-calls.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ppcs/ppc0033-ampersand-method-calls.md b/ppcs/ppc0033-ampersand-method-calls.md index 4d24a03..f0f0d67 100644 --- a/ppcs/ppc0033-ampersand-method-calls.md +++ b/ppcs/ppc0033-ampersand-method-calls.md @@ -89,7 +89,7 @@ package Foo { say "my field: " . private($self); - # can also be used to call package methods. But won't lookup via @ISA + # can also be used to call package methods. But won't look up via @ISA say "my field: " . $self->&_old_school_private; # exactly equivalent to: say "my field: " . _old_school_private($self); From b9f90b7f3795ea8d9b86bd860a761147038621af Mon Sep 17 00:00:00 2001 From: Graham Knop <haarg@haarg.org> Date: Tue, 28 Jan 2025 03:52:03 +0100 Subject: [PATCH 5/7] add example of invalid call to public method --- ppcs/ppc0033-ampersand-method-calls.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ppcs/ppc0033-ampersand-method-calls.md b/ppcs/ppc0033-ampersand-method-calls.md index f0f0d67..8af60fe 100644 --- a/ppcs/ppc0033-ampersand-method-calls.md +++ b/ppcs/ppc0033-ampersand-method-calls.md @@ -99,8 +99,11 @@ package Foo { my $foo = Foo->new; $foo->public; -# this is an error, as there is no sub named private in scope +# this is an error, as there is no sub named "private" in scope $foo->&private; + +# this is also an error, as "public" does not exist in the current scope +$foo->&public; ``` ## Prototype Implementation From 664f80fc3e5d2a5d1feff478b73315bd7185d12a Mon Sep 17 00:00:00 2001 From: Graham Knop <haarg@haarg.org> Date: Tue, 28 Jan 2025 03:52:26 +0100 Subject: [PATCH 6/7] add Object::Pad::LexicalMethods as prototype implementation --- ppcs/ppc0033-ampersand-method-calls.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ppcs/ppc0033-ampersand-method-calls.md b/ppcs/ppc0033-ampersand-method-calls.md index 8af60fe..2b27a42 100644 --- a/ppcs/ppc0033-ampersand-method-calls.md +++ b/ppcs/ppc0033-ampersand-method-calls.md @@ -108,7 +108,9 @@ $foo->&public; ## Prototype Implementation -None currently. +* [Object::Pad::LexicalMethods](https://metacpan.org/pod/Object::Pad::LexicalMethods) + implements lexical methods and `->&` method calls for + [Object::Pad](https://metacpan.org/pod/Object::Pad). ## Future Scope From 2f3bd6a41d3919be7ae7350060693c8a310a9190 Mon Sep 17 00:00:00 2001 From: Graham Knop <haarg@haarg.org> Date: Thu, 20 Feb 2025 17:45:06 +0100 Subject: [PATCH 7/7] ampersand method calls is implemented --- ppcs/ppc0033-ampersand-method-calls.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ppcs/ppc0033-ampersand-method-calls.md b/ppcs/ppc0033-ampersand-method-calls.md index 2b27a42..39b4400 100644 --- a/ppcs/ppc0033-ampersand-method-calls.md +++ b/ppcs/ppc0033-ampersand-method-calls.md @@ -4,7 +4,7 @@ Author: Graham Knop <haarg@haarg.org> ID: 0033 - Status: Draft + Status: Implemented ## Abstract