That was unclear. 'existing/inherited method of the same name' would have been more clear.
Ovid and I have both suffered from the whole "having more passion than social skills" failure mode over the course of this process and clarity about the technical points is important; as such, I apologise to anybody who read that sentence the way I didn't mean and didn't realise was readable thereinto until after I'd already hit Submit.
]]>Maybe it was, but we want to achieve something that's both good -and- going to feel natural to existing developers. This does not at all mean I'm not overall in the wrong by using it as a precedent, mind.
(raku's making the sub keyword fully lexical is still something I look upon with envy, and I hope if I call you spoiled in that regard it will be taken in that spirit ;)
> even if we can’t yet agree on the issue of BEGIN-hoisting.
It occurs to me that I've failed to explicitly say something I consider important:
I don't actually entirely object to the idea of beginlifting, what bothers me is doing so for *parts* of the 'class' block so you have to remember which keywords are beginlifted and which aren't.
While I am not fully convinced that it's a good idea, beginlifting the *entire* 'class Foo { ... }' construct would avoid all of my inconsistency complaints and I suspect also achieve most (all? answers on a postcard) of the desired goals. I could happily live with named classes being beginlifted - I just don't think that 'some parts of a class block are beginlifted, please memorise which' is particularly a service to our users. Either do it for the whole thing or don't do it at all, please, is basically where I'm at here.
> BTW, I’d be even happier with that decision if the slot keyword had been named field instead
I have never at all liked 'slot' as a keyword and while I'm quite comfortable with our theft-from-raku of 'has' in most current code, I actually really rather like 'field' as an alternative since it feels much more in keeping with 'common' linguistically even before we get to the (admittedly glorious) analogy. To the point where, much though I love 'has', I'm not sure if 'field' plus 'common' isn't actually strictly better than 'has'.
> But, if the wider consensus is that we do need method wrappers in Corinna v1
I fear the discussion around this has become unfortunately confused.
The initial argument was people saying "method modifiers are useful and important to us" versus Ovid saying "a method composed in from a role should not be allowed to access the existing method of a superclass at all" ... and these two extreme positions set the terms for the debate. Initially people argued for having some way to achieve that goal, were told that there wouldn't be any way at all to achieve that goal because it was bad and wrong to ever want to, and the result was a design that provided no such way followed by sufficient pushback that the maximalist approach to achieving said goal got added to the design instead, perhaps unwisely.
I believe that the majority of people involved simply want *some* way for a method composed in from a role to decorate/advise the method of the same name from the superclass, and that while raku grade redispatching might be rather nice, simply permitting '$self->next::method' to work in role provided methods would likely be quite sufficient.
In fact, it strikes me that assuming that *is* permitted, both before and around methods become trivial and the one annoying case that is the after modifier - i.e. running code after the call to the 'real' method while maintaining context for its return value - is already solved by LeoNerd's defer implementation and so can be handled by the perl RFC process without needing to directly impact the design of Corinna itself.
So, personally, provided roles aren't explicitly forbidden from providing modifier-like functionality, I would be entire comfortable with descoping modifier-like *sugar* from v1.
]]>It also seems to me that the existance (and IMO elegance) of'my sub' is a good argument for 'common method' at the very least. "Scoping a thing" and "the thing" seem like they should be allowed to be considered separate syntax.
]]>mst: the more I think about it, the more 'modify method foo :before' and 'common $foo' do go with 'common method foo' actually seem pretty ok to me
mst: just let's un-begin-lift everything for consistency while we're there so only 'slot' needs a weird initialisation order
LeoNerd: I'm still finding I like the braces in Object::Pad's has $slot { init-expr here };
mst: yeah, so do I, but I also feel like people are going to -want- to type '=' and it may be worth letting them ergonomics-wise
mst: maybe that's the wrong choice
mst: going for a block so it looks like the separate thing that it actually is definitely has advantages
So something like:
class Foo :isa(Something) {
my $internal = 0;
common $state; # MOP-visible but otherwise private
slot $data :accessor { 0 }; # instance variable
common method foo (...) { ... } # class method
method bar (...) { ... } # instance method
# modifier applied to superclass method
modify method baz :before (...) { ... }
}
seems like it maximises intentionality.
(I continue to like Ovid's choice of 'common' to indicate class data/methods are accessible to instances as well as straight-up class calls)
Assuming we accept that there's no reason to beginlift 'method' in this style then - other than the fact that role composition will have to happen at the end of the block (which was always stevan's dream anyway) - the ordering of everything is extremely clear.
I especially like that this way '=' always means what you expect it to mean, happens exactly when the variable's initialisation statement is hit, and then the block for 'slot' gives a nice hint it *specifically* will get initialized differently.
Curious what people think.
]]>Sorry, I completely forgot to comment on your explanation of perceived differences, largely because your position on that now makes complete sense so I didn't really have much to add.
It does strike me that if we're talking about 'common $var' (even if under another name) being visible through the MOP for testing and debugging and general escape hatch purposes, then yes, that makes the case for it being its own thing much stronger.
I was under the possibly mistaken impression that there wasn't consensus for making private-ish things visible through the MOP at all so hadn't factored that possibility in.
]]>To be clear, that has never been the argument.
The argument has *always* been that 'my' variables could be used for many of the purposes of class data and that that means that the decision as to whether to add a specific class data feature in the first version would have less of a cost.
>> trying to avoid finding we've painted ourselves into a corner
Ovid bolded this comment of mine for a related reason, but the whole point is that the -relatively- low cost of not formalising class data -yet- may mean it's worth not doing so immediately until we see usage patterns in the wild, precisely to -avoid- the inevitable risk of corner pointing that's inherent to codifying any particular feature for the first version.
]]>Why not just use 'common' in that case?
If we have 'common $foo;' for class data that fits with 'common method foo' for class methods, in the same way that 'my $foo' and 'my sub foo' exist for lexical declarations currently.
My point that overloading 'slot' seems to me to be *less* clear than using 'my' would indeed be obviated by having a separate keyword that was actually clear.
Similarly, I wondered on IRC if we should have 'modify foo :before' rather than 'method foo :before' and got pushback from ovid because that would mean "we add five new keywords instead of four" which I found equally unconvincing.
(ovid did later mention 'modify method foo :before' which if anything expresses the author's intentions even better, but unless I misunderstood he didn't seem to feel that obviated the 5 vs. 4 argument, sadly)
>> ...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.
I think I was insufficiently clear there, *that* wasn't the argument I was trying to make - the argument I was trying to make was that 'my' should work no matter what we decide about class data (which, to my mind, strongly suggests -not- begin lifting fragments of the class block and instead having things work linearly - a bunch of IRC based pushback has been significantly over working around the fact that -bits- being begin lifted cause confusion by forcibly begin lifting -more- bits seems like going in the wrong direction)
No matter how we resolve this though, given that our experience does at least intersect at the first part of
> those usages have not been particularly frequent, but class data has been invaluable on the particular occasions I did need it
I still think that waiting a bit *might* be better than shoving it into version 1, though that's perhaps as much 'fear of backcompat traps' as anything else and it could certainly be argued I'm being overly paranoid.
Though as another possibility, given it's an infrequently used feature, one might even suggest that "making it possible to add a 'common' keyword" would be an excellent test of an extension system. Both Moo and Moose have extensions that provide a class_has keyword, and even if I prefer to avoid using them, I'm very much in favour of it being possible for them to exist.
Either way though, 'slot :common' seems like the worst possible option to me because of it making the 'slot' keyword doubly special, and I think 'common $foo' honestly sounds pretty good.
tl;dr - I think the only point where we meaningfully disagree about 'common $foo' (or an alternative named keyword) is whether it's *definitely* a good idea to have such a thing in core in the first version.
]]>Yes, what extensibility Moo *does* have has been used in ways I've not been entirely happy about by some authors, but I can't see a world in which "make it impossible to extend at all" would be an improvement.
However if you have specific worries in mind I'd be very much interested to hear them.
]]>2) This child of two Eng. Lit. graduates dearly loves your proposed alternatives for *him* but fears how much fun they might not be for people for whom english isn't a first language
I express no specific opinion thereby, but I do think both datapoints will be worth bearing in mind in the process of developing an opinion to express.
]]>> And I agree that, if methods weren't BEGIN-lifted, my would be (technically) sufficient for class data.
My experience in practice is that sufficient code is written in that style -already- (package-level variables, has declarations, etc.) that we've fairly thoroughly proven that wrapping BEGIN {} around a (usually temporary) inline declaration is sufficient.
The need to be careful when using things with use or BEGIN is absolutely a bit of a wart on occasion but it's one users are going to run into anyway, and once you're using phasers, well, needing to use phasers is not honestly a major issue to people IME (and if we were to try to improve that, I think a core RFC that stands alone would be the right place to do so rather than Corinna - PEVANS/LeoNerd already has one in to provide BEGIN for compile time initialisation of 'my' variables, for example).
> And reusing my for class data also doesn't solve the problem of having to manually code every damn accessor for every damn class data slot.
Overall, I've found that when they're not purely for private storage, such data is best farmed out to a shared instance variable (often itself an object) that might be defaulted but is a first class concept in its own right.
This simplifies testing and also multi-tenant type stuff but honestly also it just gets really handy to, rather than having a single global X, having a default global X that's then trivial to override in general.
An example of this would be people using two copies of the same DBIx::Class::Schema object to run migration style ETL work, in situations where Class::DBI's use of class data would've rendered it painful at best - it's probably true to say that I implemented that feature more out of aesthetic preference than anything else, but it turned out to be extremely useful to early users and has only continued to prove its practical advantages since.
So I'm not convinced 'writing accessors for class data' is itself, directly, even a feature - writing a class data container where a single object contains such and using handles would tend to be what I'd default to just because it's saved me more pain than it's cost me lines of cost so many times at this point.
> We already have the slot keyword
We do, and I love it, and it is the one thing that is necessarily weird, in that 'slot $x = 3;' has the '= 3' part run at a *very* different moment to the RHS of any other such expression (but that part's great overall) ... but concomitantly adding onto this a behaviour of "oh, actually, if you add the :common attribute, this new and special piece of syntax operates in a *second* new ans special way" does not, honestly, seem an improvement.
The previous discussions on IRC and github were never about "there should never be a specific syntax for class data", they were always about "this specific syntax for class data is going to make 'slot' even weirder than it necessarily is to provide the good parts and since declarations of 'my' and 'state' variables within class blocks seem like they should do what the author means anyway, 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" (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).
Fundamentally, from where I'm sitting, overloading *slot* lacks intentionality far worse, and at least the use of 'my' and 'state' in the way I describe in the github comments fit how perl already works rather than piling special case atop special case in a design we're trying to keep to a "don't paint yourself into the corner" version 1.
Please consider this stanza of my response to be the one I consider truly important, since it's IMO the least subjective.
> Because software like PPI or PPR, which would be the basis of those tools, simply won't be able to tell 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).
To me, that would be just as much class data as anything else, but if there's a principled distinction between 'class scoped variable closed over by methods' and 'class scoped variable considered as class data' that you have in mind that I've missed I'd be delighted if you could find the time to elaborate on what it is because having read the above sentence of yours multiple times I'm damned if I can see an effective way to draw such a line.
]]>Note that this has been discussed at some length by various other contributors already both in github issues and on the IRC channel and it's a bit of a shame none of us knew there was also a back channel discussion going on that might suddenly obviate the previous consensus - so to avoid that happening again, here's a link to my first comment in the current discussion thread about class data.
I'd very much appreciate if anything anybody intends to be canonical goes there as well (though if people with blogs.perl.org accounts who find github's ToS unacceptable comment only here, I'm sure we can add a link to those comments from the GH issue to keep everything together)
]]>Good luck and godsspeed.
-- mst
]]>Given all of the code you showed, the behaviour you were describing ... simply isn't something perl does. Which means, basically, you'd ended up in a position of looking for your car keys under the streetlamp rather than where you'd dropped them, which is a common failure mode of everybody when debugging.
I believe the last thing I said before you left was "honestly I still think you're just confused about the data" and ... if I was seeing the behaviour you were seeing, I'd be assuming I was confused about the data. Because 99% of the time it turns out that's the problem (e.g. I've long since lost count of the number of times a seemingly incomprehensible problem has turned out to be that I connected my test script to the wrong database).
If you can get the warn Dumper logs we suggested and come back we can have a shot at being able to help you figure out where the problem is, as opposed to where it isn't - but when debugging, knowing where the problem isn't is often more than half the battle and since that was the only thing I could be reasonably sure of under the circumstances, that was the advice I gave.
The log of the conversation was posted at https://perlmonks.org/?node_id=11103270 - "you're confused about *something*" isn't meant to say being confused is wrong, being confused is the natural state of debugging (and, at least in my case, having a moment when I think "shit, I'm an idiot" is the natural end point). And when I immediately followed that up by "I'd probably apply warn + Data::Dumper" I really did mean the "I'd" - in that in a situation where behaviour seems to be impossible, my default expectation is I'm confused so I add more warn statements until I realise which of my assumptions were wrong.
I'm sorry it came across as an attack rather than the intended "debugging weird problems be like that" and I'll try and phrase it more like "given what you describe is (almost) impossible, the odds are good that we're all missing a data point" in future.
-- mst
]]>The only way to make Moose and Mouse play nice together is, in fact, to go via Moo - if you make an empty Moo role consuming a Mouse role, the Moo role now works with Moose. The Mouse one does not.
Also, consider installing Class::XSAccessor to speed up Moo.
Toby, any chance I can help you get your modules into the moose github org or somewhere else so we can help you with this stuff?
(note that the Moo community is already aware of the problem and were planning to adopt Type::Tiny if tobyink wanted us to, so the current problem is NOT a reason to migrate away from Type::Tiny unless you're trying to support bleadperl rather than actual releases)
-- mst
]]>Jeffrey: Nah, I read it as a regex-like thing and got the right answer immediately - a *multiple* of two would obviously require "aa"A/"aa" or similar, no? I think perhaps it might be an interesting exercise for you to show a few more "intuition defying" examples to a few perl hackers some time and see whether it truly defies intuition in general or merely *an* intuition, and we have a different one.
-- mst
]]>