Setting private attributes during object build
I wrote my first perl6 over the weekend, needing some help on #perl6. And now after finishing some lunchtime thoughts I wanted to post here on my main sticking point. If one wants to set a private attribute, one must define a submethod BUILD. If one wants to use any argument in the constructor other than a public attribute (positional OR named other than an attribute name), one must define a method new( ... ). And if one wants to do both, then the initialization code must be spread between method new ( ... ) and submethod BUILD. One fix posited on #perl6 was a blessall method that would act like bless, but also allow setting private attributes. That would be a solution... but... how about going all the way and allowing bless to set private attributes? I wasn't looking when the decisions were made about bless, and I can understand an argument about not letting private attributes leak out. On the other hand, if it's OK for a new blessall, why not for bless itself instead? -y
Re: Setting private attributes during object build
Why must we use 'submethod BUILD' instead of 'method BUILD'? Is it some sort of chicken-and-egg dilemma?
Re: Setting private attributes during object build
Jonathan Lang (): Why must we use 'submethod BUILD' instead of 'method BUILD'? Is it some sort of chicken-and-egg dilemma? Philosophically, BUILD submethods are submethods because they do infrastructural stuff that is very tied to the internals of the class. Submethods are internal methods rather than external methods (like accessors, for example). Submethods don't inherit; each BUILD is specific to the class it finds itself in and not its deriving classes. The only sense in which it's akin to a chicken-and-egg dilemma is that a BUILD submethod assumes that it can access the class's attributes, but not that it can use accessors and other external methods. // Carl
Re: Setting private attributes during object build
On Wed, Feb 1, 2012 at 3:24 PM, Jonathan Lang datawea...@gmail.com wrote: Why must we use 'submethod BUILD' instead of 'method BUILD'? Is it some sort of chicken-and-egg dilemma? from S12: Submethods are for declaring infrastructural methods that shouldn't be inherited by subclasses, such as initializers ... only the methods are visible to derived classes via inheritance. A submethod is called only when a method call is dispatched directly to the current class. It isn't chicken-and-egg, it's safety. The BUILD submethod doesn't get used for anything other than the class it is defined in, even if that class is derived from. -y
Re: Setting private attributes during object build
Jonathan Lang (), yary (): Why must we use 'submethod BUILD' instead of 'method BUILD'? Is it some sort of chicken-and-egg dilemma? from S12: Submethods are for declaring infrastructural methods that shouldn't be inherited by subclasses, such as initializers ... only the methods are visible to derived classes via inheritance. A submethod is called only when a method call is dispatched directly to the current class. It isn't chicken-and-egg, it's safety. The BUILD submethod doesn't get used for anything other than the class it is defined in, even if that class is derived from. When I first used submethods, I thought of them as just subroutine-method hybrids: they are a little like subs because they don't inherit. They are a little like methods because they take an invocant. I didn't much care about the infrastructural bit, mostly using them as a kind of private methods. (Nowadays I use private methods as private methods.) :-) Getting back to the topic of the original post: I think blessall is a bad name for what's proposed, and I don't see a fantastically large need for that functionality. What's wrong with just defining a BUILD submethod in the class? I also don't see a problem of having to divide initialization code between .new and .BUILD. They have different purposes -- .new is outwards-facing, receiving arguments. .BUILD is infrastructural and inwards-facing, building up (as the name suggests) your attributes. If you need to modify both these behaviors, you override both. // Carl
Re: Setting private attributes during object build
On Wed, Feb 1, 2012 at 5:41 PM, Carl Mäsak cma...@gmail.com wrote: ... Getting back to the topic of the original post: I think blessall is a bad name for what's proposed, and I don't see a fantastically large need for that functionality. What's wrong with just defining a BUILD submethod in the class? Limiting settable attributes inside new feels arbitrary. It frustrated me as a beginner. I don't have an opinion on blessall as a concept or as a name; I do like it as a solution to having to put object init code in different blocks. I also don't see a problem of having to divide initialization code between .new and .BUILD. They have different purposes -- .new is outwards-facing, receiving arguments. .BUILD is infrastructural and inwards-facing, building up (as the name suggests) your attributes. If you need to modify both these behaviors, you override both. // Carl new faces outwards but it cannot help but play inwards when it calls bless. (And if a method new does not call bless, then it isn't a constructor.) -y
Re: Setting private attributes during object build
Looking back at my paltry code, what I ended up doing was having a BUILD submethod that just listed all my attributes, private and public, and an empty block, essentially turning bless into blessall. Which makes submethod BUILD looks like boilerplate, a magic invocation, repeated in my classes. Not very elegant looking. Not horrible, just not as good as it could be IMHO.
Re: Setting private attributes during object build
On 02/01/2012 11:41 PM, Carl Mäsak wrote: Getting back to the topic of the original post: I think blessall is a bad name for what's proposed, and I don't see a fantastically large need for that functionality. What's wrong with just defining a BUILD submethod in the class? The current approach is violating the DRY principle. When you write a .new method that wants to initialize private attributes, you have to repeat all their names again in the signature of your BUILD submethod: class A { has ($!x, $!y, $!z); method new($x, $y, $z) { self.bless(*, :$x, :$y, :$z) } submethod BUILD(:$!x, :$!y, :$!z) { } # is this repetition really needed? } It also means that private attributes are less convenient to work with than those with accessors, which IMHO is a not signal in the right direction. Cheers, Moritz
Re: Setting private attributes during object build
Yary wrote: If one wants to use any argument in the constructor other than a public attribute (positional OR named other than an attribute name), one must define a method new( ... ). Huh? I know I've been out of the loop lately, but this seems fundamentally wrong. Constructor args certainly shouldn't be restricted to just the names of public attributes. And one definitely shouldn't have to redeclare new() just to allow for more sophisticated initialization. The whole point of having BUILD() is to separate allocation concerns from initialization concerns. S12 has an example that explicitly contradicts this constraint that constructor args have to be public attribute names: submethod BUILD ($arg) { $.attr = $arg; } Either I'm missing the point here (which is entirely possible ;-) or something isn't right in the current semantics of object initialization. Damian
Re: Setting private attributes during object build
Moritz clarified: In BUILD, the object isn't yet fully constructed, and thus using $.attr (which is really a virtual method call in disguise) is wrong. STD and niecza already catch that at compile time, and I'm currently trying to make rakudo catch it too (branch 'has-self' on github). Agreed. That example should certainly use $!attr instead. More to the point, objects are constructed by .new calling .bless, which in turn calls BUILDALL which finally calls BUILD. Since .bless only takes named arguments (besides the candidate to be blessed), how could BUILDALL know how to call BUILD with positional arguments? Agreed. That example should be: submethod BUILD( :$arg ) {...} Finally Rakudo, Niecza and Pugs all agree that the default .new and thus .BUILD only take named arguments, and while that's not authoritative, it is a strong indicator that the example above contradicts the compiler writer's understanding of object initialization system. Agreed. I dearly want BUILD to take only named args. My point was that I don't want the named arguments that BUILD can take to be restricted to only the names of public attributes...which was, I thought, yary's complaint when writing: If one wants to use any argument in the constructor other than a public attribute (positional OR named other than an attribute name), one must define a method new( ... ). If the complaint is that yary wanted to pass positional args to a constructor, then I have no problem with having to write one's own non-standard new() method to achieve that. If anything, we should make it even harder than that. ;-) But if one can't pass arbitrary named values to the standard new() and have one (or more!) BUILD methods correctly use them, like so: class Date { has Num $!seconds_from_epoch; multi submethod BUILD (Str :$date) { $!seconds_from_epoch = _date_from_str($date); } multi submethod BUILD (Int :$year, Int :$month, Int :$day) { $!seconds_from_epoch = _date_from_YMD($year, $month, $day); } # etc. } and then be able to call: my $today= Date.new(date = 'Feb 2, 2012'); my $tomorrow = Date.new(year = 2012, month = 2, day = 3); then we've failed to make Perl 6 even as usable as C++, which is a tragedy. I sounds like I simply misunderstood yary's problem, but I'd be very glad to be reassured that's the case. :-) Damian
Re: Setting private attributes during object build
On 2 February 2012 17:40, Damian Conway dam...@conway.org wrote: I sounds like I simply misunderstood yary's problem, but I'd be very glad to be reassured that's the case. :-) AIUI, yary is talking about the default BUILD submethod that is generated if you don't provide your own (or maybe its the behaviour of BUILDALL when it can't find a BUILD submethod) which sets any public attributes from corresponding named arguments, but doesn't set private atrributes.
Re: Setting private attributes during object build
On 02/02/2012 07:40 AM, Damian Conway wrote: My point was that I don't want the named arguments that BUILD can take to be restricted to only the names of public attributes...which was, I thought, yary's complaint when writing: No, the complaint was that when you write self.bless(*, |%named) in your method 'new', then you have to list each private attribute as submethod BUILD(:$!a, :$!b, ...) { } in order for them to be bound to their values. This is necessary since the change that private attributes cannot be initialized from the default 'new' constructor, because it's 'bless' (or the 'BUILDALL' that bless calls) that distinguishes private and public attributes, not 'new'