-
Notifications
You must be signed in to change notification settings - Fork 19
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
To Twigil or Not To Twigil #29
Comments
You DEFINITELY should have separate namespaces for slots and parameters. It is normal and good that one should be able to use the same unqualified names for corresponding slots and parameters. However it is done is less important than THAT it is done. |
I believe the best solution aesthetically is something where if you have multiple objects of the current class a method of that class can access each of their members with the same verbosity. So if you have say a |
TBH, I don't think it's productive to worry about Perl's reputation. It's unlikely it will ever change, especially because of such a small change. Memes rarely go away. |
The problem with any syntax that includes the instance variable is that they are generally already valid syntax that isn't reasonable to overload. There hasn't been any discussion about providing access to members in other objects, even of the same class. All current and potential future plans have revolved around using methods for that, possibly with a trust or protection model. |
To be clear, I'm not proposing any specific syntax like the I also want to be clear that I actually consider it a misfeature of common OO systems that there even is a conceptual difference between "self" and "other". I much prefer the conception in functional languages that you just have routines and they have parameters and you treat all the parameters the same. Especially when you're talking about a routine that has multiple parameters of the same type and all the parameters are that type, such as comparison or addition or whatever, so you have With classes the normal thing that everyone expects is that when a class declares a "private" slot or method, that all methods of that same class can access them for every object of that class on equal terms, and it makes no difference whether in a call Methods inside a class are NOT the same as methods of any other class. Today is the first time I heard any notion of Corinna being designed to treat private slots differently as to whether class Foo can see them for $x and not $y. I hope that's not the case, it seems counter to any good design and seems unjustified. |
I suggest a solution could be some unused variant of |
@duncand A difference between twigils and your proposed |
A couple of ideas:
class Foo {
has $x;
method ($x) {
has $x = $x;
# now $x refers to the instance variable
}
} I am not sure this bring-the-slot-into-scope behavior would be too user friendly but it removes the need for a new syntax and it is consistent when how scope is already managed.
class Foo {
has $x;
method ($x) {
${.x} = $x;
}
} Using that in current perl syntax results in an error, so it doesn't conflict with any currently valid syntax. Note also, that Also, this |
@salva the class Point {
slot ($x, $y) :param;
method move ($x, $y) {
slot $x = $x;
slot $y = $y;
}
} That would neatly sidestep the issue, at the expense of overloading the meaning of the |
Leaving aside the twigil issue, why not have a move method that behaves in a relative manner, and a move_to method that does an absolute move? |
@talexb Those are both reasonable, but I needed to have an example which showed the inadvertent data hiding. |
https://docs.raku.org/language/variables is useful for 2 reasons:
If you want this to be successful (and I want to see it successful myself), it needs to tend towards Perl5. This is just what the last 20 years has shown us. Otherwise, you're just risking the reimplementation (or vulnerable to the accusation) of Perl 6/Raku. |
@talexb I believe it's not about solving the presented code. If it were, one could simply use a different name and avoid the lexical trap altogether: class Point {
has ($x, $y) :param;
method move ($new_x, $new_y) {
$x = $new_x;
$y = $new_y;
}
} I think @Ovid is just trying to expose the caveat that "local variables named like instance variables will not work". Even more so, they are an issue that will bite people, specially newcomers, because the '"my" variable $x masks earlier declaration in same scope' warning only happens when you actually declare them in the same scope, not when we declare a variable with the same name on an inner scope:
Because this is already an issue with Perl, I would NOT add twigils or any other form of "magic" workaround unless it's something pervasive throughout the language (which may never happen), or at the very least under regular signatures.
So, until it is fixed in Perl (fsvo "fixed"), I wouldn't try to change this behavior in Corinna at all. I'd add it to the "CAVEATS" section of the docs and move on. Which is to say I agree with @Ovid's final comment that "(...)'m not sure if we really have an issue, other than the fact that when our lexical variables hide instance variables, there's not much we can do to work around this other than providing a :writer:" (or using another name for either variable, or making the instance variables private and providing getters, or ...) |
@Grinnz ~ as an attempt to address your "confused" emoticon, I'd like to just simply put it this way. If Cor can't be discussed or implemented without sticking to familiar Perl 5 concepts, then this is a red flag and might indicate the need to seek a return to the ground state; that being things and stuff that will be familiar to perl 5 programmers - which Cor needs to keep as a potential set of users. Deviating from that is where I've seen all efforts fail - Perl 6 just being the most epic example - in terms of a natural progression of Perl 5 ( don't think this can be disputed ). |
@Ovid @salva By trying to avoid linenoise with twigils, you fall into another big problem: extra code for simple ops. Moreover, in the following example it's really weird to have lexical parameters being instantly overwritten with slots...
Instead you could consider doing this:
Or, depending on what is considered corinnish way of doing things:
The original idea about "lexical slots" could still be used though for when slots might be needed deeper inside the code:
UPD Oh, and BTW: I'm pro-twigil anyway. The idea of attributes/slots looking same way as lexicals never looked good to me from code readability point of view. It's ok for small examples, but in big code with many slots it would require from one to remember all of them. And it's for one class alone. What about many classes in a project? |
None of this has anything to do with Raku/Perl 6. Just because a term has been used by Raku does not mean it owns the term. |
@haarg you're grossly missing my point, but I digress |
Why not something like the following to access slots: class Point {
slot ($x, $y) :param;
method move ($x, $y) {
if ($self->some_condition($x, $y)) {
$self{ x } = $x;
$self{ y } = $y;
...
}
else {
die "The coords are bad, really bad!";
}
}
} gives a differentiation between methods accessed using |
Minor aside for the example I just gave, it makes the following possible: class Point {
slot ($x, $y) :param;
method move ($x, $y) {
$self{ x } = $x;
$self{ y } = $y;
}
# override the get for x for some reason
method x () {
return $self{ x } / 10;
}
} As I assume you'd use a Point class as such: my $point = new Point;
$point->move(5,5);
say $point->x; # 0.5 Edit: Obviously I don't know if this will cause issues with any other parts of Cor such as overrides or accessors or setters, someone with more experience in the spec should see where those issues are though 😅 |
I don't think extended sigils are needed, because a naming custom -- such as is currently done with leading underscores in a lot of packages -- would work just fine. Why not follow that convention and always write
very simple very easy no problem any more. |
I'm not seeing that the override-outer-scopes thing is really a widespread problem. In those rare cases where it is a problem, using |
I agree with @davidnicol, specially since That said, this is yet another workaround. If the developer forgets about this and uses the same name, the "masks earlier declaration" warning will not show up and a hard to detect(?) bug will happen. But (again), since this already happens with signatures and pretty much any other inner block in perl, I think it should be added to a don't-do-this caveat section and that's it. Unless it's something worth changing everywhere. |
Since it's a core feature we're designing right now, it seems like a mistake to design it to require a special workaround when we can just, not do that. |
Could the parser simply throw an error if a lexical variable has the same name as a slot? Maybe not Perlish (taking away a footgun) but it resolves the problem without new syntax. |
This is same performance of signatures and old my $x = shift style, maybe new hands can learn this automatically. If imaging method works like:
Throw a error looks like naturally choice. |
I consider that a very dirty kludge. The only clean solution is that there are separate namespaces for slots from other things so people can use the same nice names for both without conflict. |
Object::Pad does this, because Object::Pad is a playground to experiment with these ideas. A core implementation will definitely not have any special handling for slots beginning with underscores. |
Infact moreover, I put the underscore handling in precisely because of a lack of ability to do twigils there. If we had twigils then that wouldn't be required, as things like has $:name :reader; would already create a reader method named |
This doc section may also be relevant |
I would disagree with this... It's hard to speak for the general public, but I think most people were objecting to the Perl 6 treatment of Perl 5 code as a second-class citizen and the decreased performance all around and different threading model, rather than the fancy new syntax that Perl 6 offered. |
One last comment from me, since I think my signal-to-noise ratio is dropping:
Technically it is lexical, as in "only valid within the area of code in which it was declared" - you can't access the slot from outside the class scope, e.g. another file or after the My concern is that there seems to be a general "instance slots must be easily distinguished from everything else" push from proponents, but I haven't really seen any convincing arguments as to why that's the case.
I see your "incorrect" and raise you one "irrelevant" in return 😸 So as an example, we already have upper-case or prefix-case for I like the way We have other conventions, too - such as the underscore for private methods: synhi is free to apply additional rules on these based on those conventions, despite them not being absolute rules. If someone has a different convention for an underscore prefix, that's fine - but if synhi has a rule for displaying those differently, then no one has to care what the conventions are, just that we wanted some visual distinction, so we have the ability to apply that visual distinction, without necessarily enforcing it. So yes, I stand by my original comment - if you want a twigil convention, there's a clear path: use (as to @nrdvana 's comment: you can count me as one of the people who emphatically does object (hah) specifically to the "fancy new syntax" and general extra complexity of Raku, performance can be improved and threads are nice - I'm sure its proponents enjoy the language greatly, but from a teaching aspect it seems like someone took "line noise" as a challenge, and tried to upgrade to "square noise" instead 😟 ) |
Do we want to support array and hash attributes (sorry, I hate the term ‚slot‘)? |
In context of a limited feature where we're just concerned about access to $self slots and not $other slots, ... I now feel that using a keyword So the normal syntax to access a slot is just In practice probably it would be relatively rare (at most half of all methods) to have parameters with the same names as slots but when this happens one can say Its not quite as terse as a twigil when used, but in the cases where there isn't shadowing the regular $foo is terser still, and the keyword is more self documenting and more self-consistent with the rest of Perl. If we want to introduce twigils to Perl its probably best to do so more comprehensively than just for a single use case as now. |
"At most half of all methods" is relatively rare? I think there's a scale problem in this analysis. I'm not entirely disagreeing, but the primary difference in use case is that slot variables will always be intermingled with lexical variables in every usage of them, whereas package variables are rarely used at the same time as lexicals. |
My point is that sometimes it is better design to give parameters the exact same names as slots and be able to access both, and sometimes it is better design for the parameters to have different names. Having shadowing and the slightly more verbose |
Here's where I think we are. Right now, one of the biggest problems we're having with Corinna is people saying "yes" or "no" without giving concrete explanations or examples of their reasoning. Fortunately in this thread, many people have been giving concrete examples and this has helped a lot. For example, here's a slot with a twigil (twigil not yet decided): has $:x;
method inc ($x) {
$:x += $x;
...
} Here are some of the arguments for and against them. Pros:
Cons:
I can't say that I'm "for" twigils, but so far, that's two strong technical and one strong social argument for them. I see a technical argument against them (I don't know how strong it is) and two rather weak social arguments against them. At the beginning of the discussion, I was leaning away from twigils. I kind of liked them, but I was swayed by the social arguments. Laying out the pros and cons clearly seems to show a strong benefit to using twigils. So if you have a strong argument for or against them, if you can clearly describe it, give an example, and explain if it's a technical argument or not, I'd love to hear it (I'm quite serious about this because I'm happy to put a gun to the head of twigils, but I can't see any reason to do so at this point.) For the following discussion, I assume that a strong technical argument beats a strong social argument. Other solutions proposed are (not an exhaustive list) ... Fake Slot Access has $x;
method inc($x) {
$self{x} += $x;
} Pros:
Cons:
I find this proposal interesting. I do wonder about this case: common $x;
method foo(x) {
$self{x}
# or
$class{x}
} In the above, it would always make it crystal clear that However, there's not been enough discussion of this case for me to have a strong opinion. Update: overloading the meaning of existing syntax and shadowing make this a show-stopper for me. Ignore the issue since shadowing is already known has $:x;
method inc ($x) {
# oops
}
method inc2 ($dx) {
$x += $dx;
} Pros:
Cons:
So we have a strong technical argument for and, in my opinion, two strong and two weak social arguments against. Conclusion: we cannot ignore this issue. Reuse the slot $x;
method inc($x) {
slot $x += $x;
} Pros:
Cons:
A strong technical and strong social argument in favor. Two strong technical (imho) and a weak social argument. Conclusion: tempting, but not as strong as sigils. Warn on shadowing slot $x;
method inc($x) { # warning generated here
...
} Pros:
Cons:
The warning suppression merits some discussion. On the #cor IRC channel, it was suggested that we simply use the existing warning mechanism. Let's look at that. slot $x;
no warnings 'shadowing';
method inc($x) { # warning generated here
...
}
# thirty more methods We have attached the warning, but now we have thirty more methods which might have this warning, but now we no longer see those warnings. We could tell the developer "if you suppress the warning in this way, move the method to the bottom of the class", but the developer might reasonably object to that. So let's try it again. slot $x;
{
no warnings 'shadowing';
method inc($x) { # warning generated here
...
}
}
# thirty more methods Now we've lexically scoped the warning, but it's an ugly hack, similar to the way we used to fake state variables. Further, it's more grunt work the developer has to remember. Corinna is supposed to take that away, not add to it. The grunt work might lead a developer to rename the argument, but in a large method, that would mean finding every instance of So let's try it again. slot $x;
method inc($x) {
no warnings 'shadowing';
...
}
# thirty more methods So that's kinda elegant, but we've put the warning after the variable doing the shadowing. I don't know of any other place in Perl we do something like this. It feels like a hack (and I don't know if it would work). So looking at our original pros/cons liks, I see strong technical and social argument for. One strong technical argument against, and weak social and technical arguments against, but then we have the "suppression" argument. I really like the warnings idea, but I don't think it's working. Other Ideas* Variations of changing how signatures work. class Point {
slot ($x, $y) :param;
method move (slot $x, slot $y) {
}
} Signatures are still experimental and I believe Dave Mitchell is still planning on doing work there. I do not believe we're at liberty to change signatures. |
About cons with twigils, I don't see |
@Ovid's summary of the options here makes me think that any option which prevents shadowing is the best choice, and the one with the twigils has the best huffman coding. However I would also argue that if we're introducing twigils to prevent shadowing here, we should also introduce them in other places to prevent other kinds of shadowing. For example, introduce that within a Corinna class declaration, any Also, conceptually, I see parameters as something that ought to have their own twigils too, because they are quite distinct in function from non-parameter lexicals. In particular, and also compatible with the existing notion of @_ but I'm not basing it just on that, I consider the notion of the entire set of arguments to a routine being a collection-typed value, so its kind of like we have implicit $args which has a slot of its own for each parameter, and then the twigil is referencing it directly. |
@duncand I do not anticipate expanding twigils to Second, we cannot add twigils in signatures because, as I noted above, work on signatures is ongoing. I seriously doubt P5P would look kindly on us unilaterally changing something this fundamental to the language. But as usual, for both of those cases, if you feel there is an argument to be made, we need:
Those would probably best be done in another ticket as this one is about addressing the shadowing issue. |
When I say A better specific example would be Corinna class/static slots, The original question about twigils may have implied that the decision would cover these too, but since it didn't say so explicitly, I'm saying that if twigils are used for object instance slots they should also be used for class static slots. And possibly a different twigil from instance slots so its easier to tell them apart. And a strong reason to do so is the cited reasons in favour of twigils, good at preventing shadowing or confusion. |
@oodler577 I just want to say that negative comments regarding borrowing ideas from Raku are not helpful because they are not technical arguments. Nor, in fact, are they social arguments unless you can explain why Perl borrowing from Python (core OO), C, sed, awk, and many other languages is OK while borrowing from Raku is not. You wrote:
Raku has been the creation of a new language that attempted to fix all of Perl's problems at once while extending the language into heretofore unknown (to Perl) directions. Corinna is:
Comparing Corinna to Raku is comparing a Toyota Corolla to an F-35 stealth fighter. Update: Honestly, there's a hell of a lot more I'd steal from Raku if I could, but the languages have evolved in different directions and much of the brilliance in Raku simply can't be done in Perl. |
IMO, the biggest issue with twigils is that in a lot of cases they are already valid syntax and so, perl would have to support two syntax: the backward compatible one and the new one with twigils (activated, for instance, after For instance, something I do frequently is to prefix debug statements with the process id as in: say "$$:here"; Now, how is this going to be parsed under twigils rules, if say($$ . ':here'); # or...
say(${$:here}); Are there other ambiguities like this one? In summary, I think it is important to consider which specific twigil to pick for attributes and which kind of ambiguities it is going to introduce in the language in order to make a decision. |
Interpolation is known to be a case where it would be ambiguous. Since interpolation of these variables would be a primary goal, one option would be to require punctuation variables to be interpolated as |
Is in fact why I originally suggested At this point I feel a lot of this discussion chain is rehashing old grounds already covered months ago. |
It is, but it was correct that this issue post didn't cover this caveat that has to be dealt with for twigils to work. |
Per the last few comments, it seems to me the biggest issue to resolve to adopt twigils is pick a specific syntax that is presently illegal syntax so that it wouldn't conflict with something that exists now and we can avoid the problematic option of having to use a feature flag to disambiguate. |
I love that one as Toyotas are as reliable as Perl 5 but comparing Raku to an F-35 is unfair! 😉 |
TIme out, here. You wrote an aweful lot to counter and distract from my simple point; which I don't believe was missed. My point is simply this: if you are looking at concepts that are not famiar to journeymen Perl programmers (and indeed offer them as a natural progression), you will lose the people you need to make this successful. To do otherwise, and in particular, this case is to head in the direction of reimplementing Perl 6. |
There are two parts to this argument I haven't seen anyone else make yet, so here in the hope that this isn't at all a repeat. Also, both of these points are made by reference to myself having actually written large amounts of real actual code using Object::Pad. [1]. Dynamic Method GenerationIf twigil syntax exists, then methods that access slots can be dynamically added to classes by MOP access, by way of a my $mref = method :unbound {
$:aslot++;
}; This "unbound method" is not yet useable, but can now be attached to a class by a MOP call, and only at that point we'll resolve the slot name in that class: $metaclass->add_slot( '$:aslot' );
$metaclass->add_method( inc_slot => method :unbound { $:aslot++ } ); If instead, slot variables look identical to regular lexicals, then code of this form cannot be constructed. There would be no way to write the anonymous A mechanism does currently exist in Object::Pad to do this kind of thing, and it is Not Pretty: my $metaslot = $metaclass->add_slot( '$aslot' );
$metaclass->add_method( inc_slot => sub { # we can't use method
my $self = shift; # because we're not method
$metaslot->value($self)++;
}); It turns what would be a simple efficient OP_SLOTPAD inside the method body into a closure capture of the $metaslot instance which invokes an lvalue accessor method on it at runtime, just to fetch the value of that slot. It works, but it's incredibly inefficient and removes most of the entire point of having these slot variables feel variable-like in the first place. Surprising Lazy InitializationOne of the commonly-requested features from Moo(se?) users is "does it support lazy builders?". I have been hesitant to add these to Object::Pad itself for the reasons I explain below, but because people kept asking about it, I created a mechanism to allow 3rd-party attributes, and then created this: https://metacpan.org/pod/Object::Pad::SlotAttr::LazyInit This kind of thing is subtle to use in practice, because "slots look like lexicals". With Moo(se?), all instance data is accessed via accessor methods, and people are culturally used to the fact that methods can run code. They might print spurious warnings, or they might fail outright: use Moo;
package OrangePeeler;
has orange => ( is => 'lazy' );
sub _build_orange {
die "Not on a Tuesday, no" if (localtime)[6] == 2;
warn "Maybe, maybe not" if rand > 0.5;
return Orange->new;
}
sub peel {
my $self = shift;
# might die or warn but that's OK; we expect that
my $orange = $self->orange;
say "Peeling $orange...";
} Whereas, if all your slot variables look just like variables, then this might be considered surprising: use Object::Pad;
class OrangePeeler;
has $orange :LazyInit(_make_orange);
method _make_orange {
die "Not on a Tuesday, no" if (localtime)[6] == 2;
warn "Maybe, maybe not" if rand > 0.5;
return Orange->new;
}
method peel {
# This variable interpolation itself might die or warn
say "Peeling $orange...";
} By using a twigil and making slots "look different to variables", we can disarm this false feeling of safety, by reminding the programmer that these aren't just boring lexical variables, and that fun things may still happen: method peel {
# This interpolation might die, but that's OK because it looks weird
say "Peeling $:orange...";
} |
On Sat, Aug 14, 2021 at 1:45 PM Alexander Hartmaier < ***@***.***> wrote:
Do we want to support array and hash attributes (sorry, I hate the term
‚slot‘)?
In defense of the term "slot," it unambiguously means an instance variable
without implying anything about the implementation of the object.
This is a good thing. And is fully supported by method-style accessors
using current Perl syntax. (For a weak enough definition of "fully" that
denies that potential confusion between accessors and methods that do other
things might somehow be a problem.)
|
Some views, questions and suggestions from a "Normal Perl User". "Java-Style"Why exactly is this "Java-Style" not possible, as mentioned by OP?
Is it because it is not technically feasible to distinguish internal access from setter/getter method? But how is this solved in Java, since they can do it? Can't Perl figure this out behind the curtain?
Arrow DollarCould
My guess is, this is conflicting with some Perl-Golf, but I'm not sure. TwigilsIt seems to me, that twigils are a whole conception, where there are a lot of different characters with different meanings. So as an e.g. Raku-User there is a general concept to learn, which you can then use in different circumstances, by also choosing from different special characters. Its' probably not good design to take just one single character from this concept to just build one Corinna-specific feature? Dot DotI'm sad, that
Well... ok, maybe I'm not sad. Not that beautiful as I imagined :-) |
Java method calls require parenthesis. This makes them distinct syntactically. In perl this would mean overloading existing syntax with a lot of magic based on context, and would prevent many things that should work. Methods generates with
This is existing syntax for a method call. It will call the method name stored in the variable |
hmmm... where have I seen this "context dependant magic" before..? it sounds so familiar, if not to say intimate 😍 |
I don't find this argument too compelling. There are already several mechanism supported by perl allowing one to run custom code when assigning to some variable (tie), or invoking simple operators like addition, stringification, etc. (overload). And well, everything using magic hooks. |
I wasn't sure at first if non-scalar attributes were allowed by this RFC ... apparently they are, but what if they weren't: Could the whole twigil discussion be sidestepped by giving attributes their very own sigil instead? Please note that that wouldn't necessarily be my favorite, though: based on Ovids comment above, and the variants he describes, I'd prefer the twigil variant as the most readable (to me) one of them. Though in the end I'll happily take whatever is chosen since it will be a huge step forward for Perl, anyway, and scratch my biggest itch (besides an "optional chaining" operator - like |
Closing this ticket. We won't do this for v1, but perhaps it will be revisited later. |
In Corinna, you can write this:
But instead of moving the point via differences, what if you wanted to just pass the new x and y values?
You can't do that because the local variables
$x
and$y
hide the instance variables. In Java, you could do this:Corinna's semantics are sufficiently different from Java's that we don't have this option. What are our options?
The twigils might look like this:
While I kind of like like that and it makes it immediately clear that this isn't just a normal local variable, nonetheless, introducing even more punctuation could increase Perl's reputation for line noise. Given that I don't see Java devs complaining too often about confusing local and instance variables, I'm not sure if we really have an issue, other than the fact that when our lexical variables hide instance variables, there's not much we can do to work around this other than providing a
:writer
:The text was updated successfully, but these errors were encountered: