The $obj->&private_method()
syntax has a
few more benefits than just being available.
For a start, it is analogous to the existing $obj->$subref()
syntax.
Which, incidentally, does not perform polymorphic dispatch either.
It is also consistent with the Raku call-a-named-subroutine-as-a-method syntax:
# Raku code...
$obj.&subname();
...which doesn’t do polymorphic method dispatch either
(though that’s a more complex issue in Raku, as subroutines
can also be subject to polymorphic or even multimorphic dispatch,
via the multiple dispatch mechanism).
I agree that the $obj->&private_method()
syntax is unusual,
but that just means it stands out well in code. And it has related prior art
in both Perl and Raku. I think that makes it an excellent choice.
Is not ok to just do the same with lexical methods?
No. Methods in Perl are always called with an arrow.
I strongly oppose allowing them to be called like subroutines.
One of the main reasons we’re adding Corinna in the first place
is that it turned out to be suboptimal to reuse packages as classes.
hashes as objects, and subroutines as methods.
Given this modern understanding of the severe disadvantages of
“punning” constructs, I can’t see how reusing subroutine calls
as method calls could be an acceptable choice for Corinna.
slot
keyword is now field
as well.field
from here on.]
...the issue is that lexical subs don't have access to the object state.
They only can access the object through its public interface.
That's a severe limitation...
You make a very good point there. One that I had not sufficiently considered.
I hope I am not the only one that would expect that closure over the slot $x to work
It would be great if they did.
However, fields are per-object, not per-scope, so allowing subroutines to close over them
would require some fancy footwork inside the implementation. Specifically, any subroutine
closing over a field would have to be implicitly passed the current invocant object
from the nearest surrounding method call on the stack, and internally would have to
use that object to select and access the actual storage for the field.
Of course, Corinna methods are indeed able close over fields, and so already require
such memory-addressing chicanery. But methods are always explicitly passed
their invocants, so they always have the necessary information to achieve
the correct memory look-up for a closed-over field.
I imagine that extending that per-object look-up to lexical subroutines as well would be
quite a bit trickier. The implementors might not be willing — or even able — to do so.
Which brings us full circle back to needing private methods after all. :-(
And our ongoing disagreement on the correct semantics for them
leads me to conclude that the only reasonable approach left is:
field $counter;
my method increment_counter() {
$counter++; # Methods can always close over fields
}
method track_counter () {
...
$self->&increment_counter(); # Lexical method call syntax
...
}
I think that’s sufficiently syntactically distinct to provide reasonable expressiveness,
affordance, and intentionality.
The only issue is that it doesn’t cleanly extend to allow protected methods as well.
(Though, in my personal opinion, that’s a feature, not a bug! ;-)
You see, that’s exactly the problem with private methods.
Your “right” behaviour can’t be right, because it doesn’t conform to the
existing Perl dispatch behaviour of methods, which is: Go to the object’s
actual class, look for a suitably named method, if it exists then call it,
otherwise try recursively in the parent class(es).
So the method call to $b->foo()
should go to the object’s actual class (B
),
look for a suitably named method (B::foo
), check whether it exists (it does),
then attempt call it (KABOOM! “Can’t call private method B::foo() from class A”).
You’re proposing that we extend the method-call semantics to pre-check for
a possible non-polymorphic dispatch in the current class: First check if
the object isa __CLASS__
, if so then check if there is a suitably named
private method in the current class, and call it if there is, otherwise go to
the object’s actual class, look for a suitably named method, if it exists then call it,
otherwise try recursively in the parent class(es).
So now every method call is more expensive and more confusing. It’s a method call, but it
doesn’t always dispatch polymorphically to the bottom of the inheritance hierarchy to call
the most derived method; sometimes it dispatches statically into the middle of the
inheritance hierarchy to a more-ancestral method.
The “right” behaviour you’re suggesting is really the behaviour of a statically dispatched
subroutine within a lexical scope, not that of polymorphically dispatched method within
a class hierarchy. But the syntax you’re using to make the call labels that call as “method”,
not “sub”.
That’s not clear expressiveness, good affordance, or easily inferrable intentionality.
Otherwise you are requiring the B programmer to know about the private methods up the hierarchy class in order to avoid reusing their names.
No, we are requiring all programmers not to attempt to call private methods on objects
from outside those object’s own classes. Which is what the call to $b->foo()
from inside A
is attempting to do.
Once again, this discussion has been extremely helpful to me, Salvador. Thank-you.
You have managed to convince me that Perl shouldn’t have private methods at all.
That anything one might want to do via a private method would be much better done
via a lexical subroutine. That private method calls are confusing, easy to get wrong,
and lacking in expressiveness, affordance, and intentionality. That the mechanism for
private operations within a class should be lexical subroutines, not specially marked
methods. And that the syntax for executing a private operation within a class
should be the standard subroutine-call syntax, not the polymorphic method-call syntax
(or some new syntactic variant thereof).
I am sincerely grateful to you for that insight.
And, to me, when two constructs are very similar but still distinct
that’s precisely when you want them to have distinctly dissimilar syntax.
So that when you’re writing the code you’re forced to think how
to unambiguously express whichever of them you actually intend.
And so that everyone is automatically helped to recognize which of them
was actually intended when they’re subsequently reading that code.
Also, there are several programming languages that use the same syntax for both... Common Lisp/CLOS uses the same syntax for everything. Admittedly all this programming languages have their issues... but I never heard anybody say the overloaded subroutine call syntax was one of them.
I was (a small) part of the C++ design discussions twenty-five years ago,
and I was certainly saying that, even back then.
And Larry Wall once specifically called out Lisp’s complete lack of syntactic affordance
as having “...all the visual appeal of oatmeal with fingernail clippings mixed in.”
I have to admit that for me, they real issue I have with supporting $self->private_method is that I see lots of issues at the implementation level.
I’m sure you’re right. But, like Larry, I’m always in favour of making
the implementors’ lives harder in order to make the developers’ lives easier. ;-)
Of course, Larry had far more right to hold that view than I do:
he’s an implementor himself; I am not.
For instance, what happens when you have...
return $self->foo + $b->foo + $c->foo;
Private methods by their very nature don’t participate in inheritance,
so you should get:
return $self->foo # A::foo() called
+ $b->foo # Can't call private B::foo() outside class B
+ $c->foo; # C::foo() if public, otherwise exception
Whereas, if you had actually wanted the same private A::foo()
called on $b
and $c
, then you'd have written it like so:
my sub foo($self) { ... }
sub bar() {
my $b = B->new();
my $c = C->new();
return foo($self) + foo($b) + foo($c);
}
Because sub calls and method calls are different constructs
with different interfaces and behaviours, whereas lexical variables
and slot variables are the same construct with identical interfaces
and behaviours.
For me, language design is all about balancing the design goals of:
And, to me, using the foo()
syntax for both subroutines and private methods
doesn’t meet those three criteria.
It lacks affordance, because it means that something as straightforward as:
my method foo () {...}
method other {
...
$self->foo();
...
}
...will fail at runtime.
It lacks expressiveness, because I can’t tell just by looking at the following code:
method other {
...
foo();
...
}
...whether or not the call to foo()
will be passed $self
as its first argument.
Instead I have to go somewhere else and look at the definition of foo()
.
And that definition might be before or after the definition of other()
,
and not necessarily even in the same class, or even in the same file.
It also lacks intentionality, because I also can’t tell just by looking
whether the author of that previous code example was intending to call
a subroutine with no argument, or to call a method with one implicit argument.
So the “obvious” version of the call doesn’t work, and I can’t easily tell whether
the less obvious version is correct, or precisely what it’s going to do.
To me, that’s not a good solution.
In fact, at this point, I’d much rather we jettisoned private methods entirely,
and simply used regular lexical subroutines instead:
my sub foo ($self) {...}
method other {
...
foo($self);
...
}
The only problem with that approach is that it doesn’t give us an eventual
path forward on protected methods. (Not that I actually want protected methods either;
I think they’re a misfeature in OO languages, because they inevitably couple
the internals of derived classes to the implementation details of their base classes,
which subverts the fundamental principle of OO encapsulation.)
Of course, while lexical-as-private doesn’t help with protected methods,
neither do the current proposed syntaxes of either method $foo
/$self->$foo()
or my method foo
/$self->&foo()
.
So, if we are one day going to have protected methods, I think we have to
come up with a syntax, and an implementation, for private methods
that extends cleanly and obviously to protected methods as well.
Which is why I originally proposed:
method foo :private ($self) {...}
method other {
...
$self->foo();
...
}
...which then cleanly extends to:
method foo :protected ($self) {...}
method DerivedClass::other {
...
$self->foo();
...
}
One of the deep problems with language design (and, in particular, with the
ongoing design of Perl) is that we can’t just come up with a neat and clever
solution to each individual design problem in isolation. That’s how we paint
ourselves into an inextricable syntactic corner. Or end up with a
Frankenlanguage.
We need to consider not just what would be easy and convenient right now,
but also what is going to make the next five or ten design steps easier,
more convenient, and — above all — more consistent into the future.
That said, I truly appreciate your thoughtful and valuable contributions
to this discussion, Salvador. They are certainly helping me clarify
my own thinking and also, I believe, helping us collectively make
the right choices for Corinna...and for Perl.
No, subs are always looked up at runtime (unless, of course, you’re calling them
in a BEGIN
, CHECK
, UNITCHECK
, INIT
, or END
block).
It has to be runtime because Perl has to support runtime changes
in the implementation of a subroutine, in the form of typeglob reassignments:
local *foo = sub {...};
Prototypes certainly do affect compile-time parsing, but not runtime dispatch.
...it allows one to distinguish method calls from sub calls...but not requiring it
would result in more concise code.
This is the heart of our disagreement, I suspect.
I’m never in favour of trading affordance for conciseness. :-)
Also, the same argument could then be applied to slots and so,
being able to access object slots as regular variables shouldn't be allowed.
To me the argument is that it’s useful to be able to mentally and visually distinguish
between the the public API to a slot (which is a set of polymorphic methods)
and the actual storage for that slot (which is a variable).
Outside its class, a slot is only accessible via its public API; inside the class,
it’s accessible in two distinct ways: via its API or via its variable.
Hence, inside the class, we need two syntaxes for those two different types of access.
And I think that those two syntaxes should visually distinguish the kind of access
you’re getting: unconstrained and monomorphic access via the $slotname
syntax,
versus constrained and polymorphic access via the $self->slotname()
syntax.
And for exactly the same reason I don’t want us to use methodname()
to mean $self->methodname()
. Because it doesn’t allow us to visually or syntactically
distinguish between foo()
(a private method call) and bar()
(a subroutine call).
If methods can be called like subs, then every sub call
now also has to check whether there's a suitable method to call instead.
And should it check for that method before checking for a sub, or after?
Probably after, but one could argue it both ways.
I think there’s a lot of value in being able to visually distinguish method calls from subroutine calls just by their call syntax. In fact, I think there’s a lot of benefit in being able to distinguish public method calls from private ones (and both of them from sub calls too).
That’s why I rather like Graham’s $self->&foo()
suggestion.
Because I no longer have to think about the differences between:
$self->foo(); # Public method call
$self->&bar(); # Private method call
baz(); # Subroutine call
I can tell them apart instantly, simply by the way they look.
Couldn't lexical subs be extended to just work as private methods too...
If they’re not methods, they wouldn’t (and shouldn’t!) have a $self
variable. Which means, as you later suggested, that we’re back to:
my method _print_x () {...}
But methods (even lexical ones) need to be called on an explicit object, so we’re back to calling them like so: $self->_print_x()
And that still doesn’t solve the performance issue. If we allow lexical methods, then we have to extend the standard method call behaviour to pre-check whether we’re in a class block and, if so, to search for any suitable lexical methods in that current scope, before doing the usual class-based method look-up.
That could slow down every method call everywhere in Perl. Though, depending on the implementation, it might merely slow down every method call with every class
block.
Graham’s lexical-method-call proposal (i.e. $self->&_print_x()
) is especially interesting here, because it would also solve that performance issue, by only doing the lexical look-up.
Though, of course, it might introduce its own issues, since Corinna objects should only allow a method calls on actual Corinna methods, not on traditional Perl subroutines-pretending-to-be-methods.
Which means the $self->&_print_x()
syntax would first have to check whether or not it is inside a Corinna class
block, and then switch its lexical look-up accordingly.
That wouldn’t necessarily introduce a performance penalty, however, as the check-and-switch-look-up-mechanism process could (and should!) be performed only once — at compile-time — for each such call.
]]>...why we can't keep the interface and the implementation separate.
I’m certainly in favour that too!
But, as you point out, this clean, safe, and simple approach that we both prefer
does have a non-trivial cost: it requires every single method call with a class
block to precheck a list of private method names. And hence every method call
inside a class becomes slightly more complex and slightly more expensive.
Personally, I think the simplicity and safety are very much worth the cost,
especially if the long-term plan is to support “protected” methods as well,
which will certainly require a similar (albeit even more complex) mechanism.
It’s easy for me to argue for that...because we’re right! ;-)
But I acknowledge that it’s also easy for me to argue for it
because I’m not the one who has to actually implement it.
I had actually objected to this:That's because in my calling code, I could do this:croak "Can't call method" if caller ne __CLASS__;
{ package Class::Im::Using; $object->private_method; }
My understanding — or perhaps just my fervent wish — was that
package and class namespaces would distinct. Hence the use of
__CLASS__
rather than __PACKAGE__
in the test.
In which case, spoofing the package wouldn’t give access to the private method.
They really ought to be distinct. The whole point of Corinna
is to rectify the many issues caused by the current
“A class is a package; a method is a sub.” approach.
Though, of course, if that’s what I meant then I shouldn’t have used caller
in the test.
Or, rather, I should have used the (hypothetical) additional return value provided by
caller
in a list context; the putative twelfth return value that returns the caller’s class:
croak "Can't call method 'do_internal'"
if (caller 1)[12] ne __CLASS__;
Of course, then you might argue that an evil user
need only change their evil code to:
{ class Class::Im::Using { $object->private_method; } }
Except that it was also my fervent wish that, unlike packages,
classes should be closed for modification. :-)
method $foo () {...}
syntax to the PSC. And, yes, it’s true that no OO system prevents author wickedness,
but we can still strive to make it just a little harder to sin. ;-)
One of the issues that came up during those discussions was the best way to provide private methods in Corinna. The current Corinna proposal is that this would be done (like almost everything else in Corinna) via an attribute:
method do_internal :private () {...}
Thereafter, the do_internal()
method can only be called from within
the current class, and is never overridden by derived-class methods when
it is called within its original class.
In other words, the :private
method effectively prepends the following
code to the start of the method:
croak "Can't call method 'do_internal'"
if caller ne __CLASS__;
...and, in addition, causes the compiler to treat any call to that particular method
from within the current class as a fully qualified method call.
That is, within any class C
with a private do_internal()
method, any call
to $self->do_internal()
is treated as a call to $self->C::do_internal()
.
All of which means that there is no way to call a private method from anywhere except within the same class. Which, of course, is the whole point of having private methods in the first place.
But the need to automagically convert any call to a private method into a fully qualified
method call is (to put it mildly) a complication for the compiler.
So the members of the Perl Steering Committee Cor design team suggested that rather than having private methods,
Perl should have lexical methods instead. Specifically, they suggested that instead of:
method do_internal :private () {...}
method do_external () {
$self->do_internal(); # Call private method
...
}
...Perl would provide anonymous methods, which you could place in lexical variables and then call using the existing call-via-reference method call syntax:
my $do_internal = method () {...};
method do_external () {
$self->$do_internal(); # Call lexical method
...
}
That neatly avoids of the challenge of rewriting private methods to check their caller,
or the much greater challenge of rewriting private method calls to be fully qualified.
Instead, it cleverly enforces the “can be called only from the current class” requirement
by making it impossible to refer to the method at all, except in the lexical scope of the
$do_internal
variable.
You could even consider the slightly uglier $self->$do_internal()
call syntax as a win:
because it means that private method calls are explicitly marked as such.
The only downsides are:
The call-via-reference syntax is more obscure and perhaps a little more challenging for less-experienced Perl developers. That might discourage them from using this approach, even for methods that really should be private, thereby penalizing a good OO practice.
Using anonymous methods to implement private methods is structural rather that declarative. That is: we can’t just say what we want to have happen, we have to say how to actually implement it. In practical terms, this means that private method definitions are no longer explicitly marked as being private, and hence are far less searchable.
And that second issue is the real problem here. Because they’re structural, not declarative, lexical methods specified like this are also much easier to “enbug”. For example, if the developer mistakenly wrote something like this:
method other () {
...
$do_internal++;
...
}
Then any call to the $do_external()
method will be fine so long as the
other()
method is never called. But after other()
has been
called, the next call to $do_external()
will throw a weird exception:
Can't locate object method "140362266577153" via class "C"
Of course, we can get around that potential bug by making $do_internal
immutable:
use Const::Fast;
const my $do_internal => method () {...};
but that’s now an extra module load, and even more infrastructure to get right.
(BTW, wouldn’t it be cool if the existing :const
subroutine attribute
could also be applied to scalar variables, in which case we’d just need:
my $do_internal :const = method () {...};
Maybe one day!)
Yet another issue with using anonymous methods as private methods is that they make it easy to subvert the intended encapsulation:
our $do_internal = method () {...};
That’s no longer a private method, because you can now call it from literally anywhere in your code:
$obj->$C::do_internal();
And, even if it were still a my
variable, there’s nothing to prevent a class
from explicitly exporting it:
method circumvent_privacy () { return $do_internal }
As an OO purist, I find that possibility worrying. And as someone who has reviewed a vast amount of real-world Perl OO code over the past few decades, I think that kind of expedient kludge is...inevitable.
So what do we do?
The good folks of the Perl Steering Committee Cor team suggested an approach that does address
some of these issues. They proposed that the syntax for private methods be:
method $do_internal () {...}
method do_external () {
$self->$do_internal(); # Call private method
...
}
That is: when you want to declare a private method, you put a $
at the start of its name.
This form of the method
keyword would then create a (presumably immutable!) lexical
$do_internal
variable, initialized with a reference to an anonymous method.
You could then use the reference in that variable to call the private method.
While it doesn’t solve the problem of evil developers explicitly exporting that variable, it does make the syntax declarative again, and it could solve the issue of accidentally changing the variable (assuming the variable were indeed to be created as immutable).
The only problem is that now we have the method
keyword not just declaring methods,
but also declaring variables.
Which, from the perspective of a language designer, is...less than ideal.
And, from the perspective of someone who actually still teaches Perl,
is much less than ideal.
So, how do we solve that?
Well, Raku solves it by marking private methods not with a $
, but with a !
and then calling them with the !
as well (rather than Raku’s usual .
method call operator):
# In Raku...
method !do_internal () {...} # Private method
method do_external () {
self!do_internal(); # Private method call
...
}
That’s a neat solution, but it probably wouldn’t work for Perl.
Nevertheless, the ideal of prefix-marking private methods and private method calls in some way
might work. For example, we could consider formalizing the “private methods start with an
underscore” convention inside the new class
blocks:
method _do_internal () {...}
method do_external () {
$self->_do_internal();
...
}
In other words, we could specify that any method definition that starts with an
underscore doesn’t get added to the method symbol table. Instead, it implicitly creates
an immutable and invisible lexical scalar within the class. Then, within any class
block,
any call to a method whose name starts with an underscore actually calls the method through the
associated invisible lexical scalar instead.
Or, to put it more simply: it’s precisely the Steering Committee Cor team’s proposal, but: s/\$/_/g
And with the added advantage that the lexical is not otherwise accessible within its scope,
and therefore not accidentally modifiable, or maliciously exportable.
That’s by far the safest alternative I can think of, but I concede that it might still be too magical.
In which case we’d instead need to address just the immediate problem: that we’re contemplating
using the method
declarator to declare something that isn’t actually a method. Specifically,
we’re using method
to declare variables.
And the easiest solution to that problem is simply to define another declarator:
private $do_internal () {...} # Declare private method
method do_external () {
$self->$do_internal(); # Call private method
...
}
Now method
once again only declares named methods, and we can easily explain
that private
declares a lexically scoped variable whose immutable value
is a reference to the anonymous method.
We get very clear declarative labelling of private methods and equally clear labelling of calls to those private methods.
We don’t solve the “naughty folk can export that variable to circumvent privacy” problem, but I suppose we could always just spin that as a feature, rather than a bug. /s
For the moment, however, unless the Steering Committee Cor team is happy with either
the “underscore enforces private” approach or the addition of a private
keyword,
I expect what we’ll actually have to do is to remove private methods entirely
from the first stage of the implementation, and simply keep on pondering
how to get them exactly right.
In the meantime, don’t let anyone ever tell you that language (re-)design is easy. :-)
]]>David was a gentleman and a scholar: a gentle, warm, erudite, funny, clever, and deeply kind man. And one who has made a vast contribution to our Perl and Raku communities over more than quarter of a century.
My most sincere condolences to David's family...and to the countless other colleagues, acquaintances, and admirers around the world who will be mourning him today.
Like so many others, I was proud to call David my friend.
I will miss him profoundly.
I concede that my sub is at this point a well established prior art for prefix declaration modifiers (even though I still think it was a misstep for Perl, syntactically).
Therefore I also concede that common $foo and common method foo would be an acceptable (albeit non-KIM) compromise, and would give us everything we need in Corrina v1, even if we can’t yet agree on the issue of BEGIN-hoisting.
BTW, I’d be even happier with that decision if the slot keyword had been named field instead. Because “field” is another frequently used nomenclature for an instance data variable, and even has history in Perl usage in the form of the use fields pragma. But, far more irresistably, because the agricultural analogy between a “field” (i.e. an individual’s personal allotment of property, reserved for their own use) and a “common” (i.e. a village’s communal allotment of property, whose use is shared by all) is...utterly perfect. ;-)
As for the modifier method foo :before vs method foo :before syntaxes: there I’m far less convinced. But I suspect that, in part, that’s because I am far less convinced that Corinna v1 needs this construct at all. I’d argue that this feature, rather than class data, is actually the one that is “...worth not doing immediately until we see usage patterns in the wild”.
But, if the wider consensus is that we do need method wrappers in Corinna v1, then the keywords for that construct definitely need better names. I’d suggest either the KIM syntax:
method foo :wrap(before) () {...} method foo :wrap(after) () {...} method foo :wrap(around) () {...}...or, if we are indeed giving in to the insidious creep of the <modifier> <keyword> syntax, then:
wrap method foo :before () {...} wrap method foo :after () {...} wrap method foo :around () {...}
Note that I still think these particular features could be deferred but, if they have to go in now, then they ought at least go in eloquently.
And yet I got enormous pushback against the idea of a separate declarator keyword for class data. Because, if slot :common is weird, and my and state are non-intentional,
a separate keyword for class data what we actually need.
I heartily wish that mythical extra keyword could be classslot, because that expresses the concept perfectly, but that triple-s just won’t fly. Maybe commonslot? Or
jointslot?
Of course, if the slot keyword were data or objdata instead, then we could use classdata for declaring class data. But I suppose that would be too easy, eh?
And, even if we do define a separate keyword for class data (now, or in some later evolution of Corinna), we’re still left with the inconsistency of method foo :common, and with having to explain why class data are declared with a keyword, while class methods are declared with an attribute modifier. :-(
Unless, of course we also change to method/classmethod, or even
objmethod/classmethod. ;-)
...since declarations of 'my' and 'state' variables within class blocks seem like they should do what the author means anyway,...
But, once again, that’s the argument for just using package and sub for classes and methods: that they do what the author means anyway.
The problem is that they don’t say what the author intended. And neither do my or state (though, admittedly state gets a lot closer than my).
...how about we don't try and complicate things further until we have experience in practice of where/how class data would even be used in Cor"...
OO languages have been around now for 50 years, and class data for at least 45 of those years. I’ve personally been using class data in OO code in multiple languages for at least 35 years. My guess is that class data would be used in Corrina in all those same places that I’ve used it for the past three decades. Granted, those usages have not been particularly frequent, but class data has been invaluable on the particular occasions I did need it.
...(especially bearing in mind that the rest of the Moose and Moo maintenance teams consider class attributes to largely be a footgun, not just me).
Okay, so we have very different experiences in that regard. I don’t use class data very often, but I find it to be an excellent solution when I do use it.
Fundamentally, from where I'm sitting, overloading *slot* lacks intentionality far worse
Then I think we must have very different conceptions of what intentionality implies. To me, intentionality is the idea that constructs that are fundamentally similar in some way should be specified with a fundamentally similar syntax, and that constructs that differ in some way should be differentiated by a fundamentally distinct syntax.
Object data and class data are both, of course. They are fundamentally similar in being data that is part of the abstract representation provided by a class. They are fundamentally different in being encapsulated differently (per object vs per class).
So, to be declared intentionally, they each need a declarator that is syntactically similar (e.g. slot) and they need a differentiator that is syntactically distinct (e.g. :common, or the absence thereof).
To me, that would be just as much class data as anything else, but if there's a principled distinction...that I've missed I'd be delighted if you could find the time to elaborate on what it is...
...whether a particular my is a class data slot, or just an ordinary lexical variable (perhaps, for example, acting as private shared memory for two closures within the class).
Happy to...
In other words, while it’s perfectly true that class data slots and incidental my variables do both work the same, they each mean something entirely different. They have different roles and purposes, they inhabit different levels of abstraction in a design, they have different degrees of significance and permanence within the code, they are of primary interest to different individuals (the designer vs the implementer), and they need to be easily detectable and distinguishable by the disparate tools that those different individuals might wish to create and employ.
All of which is why they need different keywords too.