Re: Setting private attributes during object build

2012-02-02 Thread Damian Conway
My thanks to Kris and Moritz for reassuring me that
the Perl 6 initialization semantics remain sane. :-)

In response to yary's original observations, the extra work required to
achieve non-standard semantics doesn't seem unreasonable to me.
Especially as, should one need to do it regularly, one could just create
a role (possibly parametric) to handle all the boilerplate
and then mix just it into any class that requires it.

Damian


Re: Setting private attributes during object build

2012-02-02 Thread yary
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...

Actually, that *was* one of my complaints, but I was mistaken on that point.

~/rakudo $ perl6
 class A{has $.b; has $!c; submethod BUILD(:$b,:$c,:$x){say b=$b c=$c x=$x}}
 A.new(b=4,c=5,x=6)
b=4 c=5 x=6


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.

Agreed on that too.

You could break my post down into a few distinct complaints. Moritz
distilled the one that matters the most to me, and I'll quote his
first post in full-

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.

And then Moritz expands on that a bit in a later post.


Damian:
The whole point of having BUILD() is to separate allocation
 concerns from initialization concerns.

Here's where I am late to the conversation, I hadn't known that
distinction. S12 doesn't talk about the why of BUILD/BUILDALL, at
least not that detail. If BUILD is for allocation, and new is for
initialization, then hiding private attributes from bless is forcing
the programmer to use BUILD for initialization which wasn't the
intent.

S12 says this about BUILD: Whether you write your own BUILD or not,
at the end of the BUILD, any default attribute values are implicitly
copied into any attributes that haven't otherwise been initialized.
Note that the default BUILD will only initialize public attributes;
...

And that's good, because otherwise your private attributes would not
be totally hidden.

you must write your own BUILD (as above) in order to present private
attributes as part of your initialization API.

And that's not so good, because it forces BUILD to be used for
initialization, and precludes initializing private attributes anywhere
else, like a bless called from the new method.

I don't propose having blessall/new bless set attributes directly,
it will still have to call BUILDALL, so derived classes will still
work properly. If we change bless to present private attributes to
BUILDALL, then the filtering out of private attributes would move to
the default new instead.

For example, to be clear, what we have now:

 class plain{has $.b; has $!c; method say{say b=$!b 
 c=$!c}};plain.new(b=4,c=5).say
use of uninitialized value of type Any in string context
b=4 c=

 class cust{has $.b; has $!c; method 
 new(:$b,:$c){self.bless(*,b=$b,c=$c)};method say{say b=$!b c=$!c}}; 
 cust.new(b=4,c=5).say
use of uninitialized value of type Any in string context
b=4 c=

What I'd like to see:

 class plain{has $.b; has $!c; method say{say b=$!b 
 c=$!c}};plain.new(b=4,c=5).say
use of uninitialized value of type Any in string context
b=4 c=

 class cust{has $.b; has $!c; method 
 new(:$b,:$c){self.bless(*,b=$b,c=$c)};method say{say b=$!b c=$!c}}; 
 cust.new(b=4,c=5).say
b=4 c=5

-y


Re: Setting private attributes during object build

2012-02-02 Thread yary
I think I get this better now.

Currently:
Default new passes its capture (named args) to bless. Bless passes
capture (all args) to the default BUILDALLBUILD. Default BUILD
initializes only public attributes.

My thought:
Default new passes only named args matching public attributes to
bless. Bless passes those to the default BUILDALLBUILD. Default BUILD
initializes both public  private attributes presented to it by
bless.


Re: Setting private attributes during object build

2012-02-02 Thread Damian Conway
yary wrote:

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:

The other way of looking at this is that redefining the new() is about
changing the constructor interface, whereas defining the BUILD() is
about changing the initialization behaviour.

So the current approach is violating DRY in order to preserve the more
important principle of Separation of Concerns. After all, it doesn't seem
unreasonable that, if you want to modify two behaviours, you have to
rewrite two components to do it.

On the other hand, rather than adding a blessall() alternative,
perhaps we could say that, if the class explicitly redefines new(), then
any call to bless() within that redefined new() will accept both public
and private attributes for auto-initialization...under the theory that,
by redefining new() the class implementor is taking direct
responsibility for the construction...and is willing to live with the
dire consequences if they mess it up.

Incidentally, blessall() seems a dubious name to me, given that we
already have BUILDALL() and CREATEALL(), where the '-ALL' suffix means
something quite different from what the '-all' would mean at the end of
blessall().

Better still, on the principle that abnormal behaviour should always be
explicitly marked and lexically predeclared, perhaps a pragma would
be appropriate:

class A {
has ($!x, $!y, $!z);

method new($x, $y, $z) {
no strict :autoinit;
self.bless(*, :$x, :$y, :$z)
}
}


The whole point of having BUILD() is to separate allocation
 concerns from initialization concerns.

 Here's where I am late to the conversation, I hadn't known that
 distinction. S12 doesn't talk about the why of BUILD/BUILDALL, at
 least not that detail. If BUILD is for allocation, and new is for
 initialization, then hiding private attributes from bless is forcing
 the programmer to use BUILD for initialization which wasn't the
 intent.

You have it the wrong way round: new() is for allocation; BUILD() is for
initialization. From a design point-of-view, BUILD() probably should
have been called INIT(), but that keyword was already taken.


 you must write your own BUILD (as above) in order to present private
 attributes as part of your initialization API.

 And that's not so good, because it forces BUILD to be used for
 initialization,

...which is precisely what it's supposed to be for. :-)

Damian


Re: Setting private attributes during object build

2012-02-02 Thread Martin D Kealey
 Damian:
  The whole point of having BUILD() is to separate allocation
  concerns from initialization concerns.

On Thu, 2 Feb 2012, yary wrote:
 And that's not so good, because it forces BUILD to be used for
 initialization, and precludes initializing private attributes anywhere
 else, like a bless called from the new method.

Err, isn't that the point: BUILD is for initialization, and bless is for
allocation?

Except that bless also does pass-through for to the initializers, which
confuses the mental model. Well, if it's going to do that, perhaps it could
take just one parcel parameter?

(I do find the notion of an inheritable constructor a bit odd, upside-down
even. However one of the things I *like* about Perl5 is that a class's
object factory doesn't have to be called new; instead it can have a name
that's natural to the domain of the object: File.open, Socket.listen,
Process.run, Timer.start, etc. And object factories can be instance
methods too: IO.dup, Socket.accept, String.split, etc.)

The idea of BUILDALL taking care of the hierarchy and each BUILD just
plucking out the named args it needs is quite elegant, but there will
invariably be corner cases where it doesn't fit, particularly if one mixes
in unrelated classes that conflict on their parameter names.

And yet, overriding BUILDALL in an inheritable fashion also seems wrong.

One could revert to the Perl5 and C++ way of doing things: explicitly
calling up the constructor chain. But then how do you make it clear that
BUILDALL is NOT required?

Is there an answer to this conundrum?

-Martin


Re: Setting private attributes during object build

2012-02-01 Thread Jonathan Lang
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

2012-02-01 Thread Carl Mäsak
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

2012-02-01 Thread yary
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

2012-02-01 Thread Carl Mäsak
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

2012-02-01 Thread yary
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

2012-02-01 Thread yary
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

2012-02-01 Thread Moritz Lenz
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

2012-02-01 Thread Damian Conway
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

2012-02-01 Thread Damian Conway
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

2012-02-01 Thread Kris Shannon
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

2012-02-01 Thread Moritz Lenz
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'