Phil Crow wrote: > class Die; > > has Int $!sides; > > method new( Int $sides = 6 ) { > return self.bless( *, :$sides ); > } > > method roll() returns Int { > return round 1 + rand * $!sides; > }
Just as a side note, this is could be easier written as class Die; has Int $!sides = 6; method roll() { return round 1 + rand * $!sides} There is automatically a constructor available that lets you write Die.new(sides => 20) if you want to deviate from the default. > I copied the idea for this from the excellent beginner's guide PDF in the > Rakudo star distributions. Here is my first question: What exactly are > the arguments of bless? If you know Perl 5, you'll know that its bless function takes a reference as first argument, which is then turned into an object. That's also the case in Perl 6. The * means "create an appropriate candidate for me". In theory you should be able to pass a hash or an array as first argument to bless too, but I doubt that's implemented in Rakudo. The named arguments to bless() are the initial values of the attributes of the new object. > Further, why does the sides attribute > need a leading colon (or does it)? It's a named argument. See for example http://github.com/downloads/perl6/book/2010.08.letter.pdf page 36 (logical page number), section 4.2.5 Named Parameters. > This works fine as is. I have only one question here: Why do I need > to change from $.was-doubles to $!was-doubles when I access the attribute > in instance methods? When I tried to use $.was-doubles, the compiler > threw: Cannot modify readonly value. I'm assuming this is the correct > behavior, but I'd like a bit of clarification. I fear this is becoming an FAQ. The reason is that even if you write 'has $.attrib' in your class, the attribute is actually called $!attrib, and additionally a method of the same name is created for you, which is a read-only accessors. When you write $.attr in ordinary code (ie not in 'has'), it means the same thing as (self.attr). So, long story short, $.attr is a method call, to which you can't assign, unless you take special precautions (like writing has $.attr is rw;). > Finally, I've come to my problem. Recall that the Die class provides > a default number of six sides. Suppose I replace the Die-Pair > constructor call with this: > > my Die-Pair $die-pair = Die-Pair.new(); > > I want to receive the default from the Die constructor for each instance > I construct. But the output tells me this did not work: > > 0 Roll! 2 was doubles? 1 > 1 Roll! 2 was doubles? 1 > 2 Roll! 2 was doubles? 1 > 3 Roll! 2 was doubles? 1 > ... > > What must I do so that my aggregating class can allow the caller to > use the defaults of the aggregated class? My current work around is > to use multi constructors: > > multi method new( Int $sides1, Int $sides2 ) { > my Die $die1 = Die.new( $sides1 ); > my Die $die2 = Die.new( $sides2 ); > return self.bless( *, :$die1, :$die2 ); > } > > multi method new() { > my Die $die1 = Die.new(); > my Die $die2 = Die.new(); > return self.bless( *, :$die1, :$die2 ); > } > > This is what I would need to do in Java. But, I wanted to make the point > that defaults could eliminate the need for this type of overloaded > constructor > in Perl 6. What about method new (Int $sides1? Int $sides2?) { my Die $die1 = defined $sides1 ?? Die.new( $sides1 ) !! Die.new; my Die $die2 = defined $sides2 ?? Die.new( $sides2 ) !! Die.new; return self.bless( *, :$die1, :$die2 ); } It's not ideal, but I think it's a workable solution. If you want to generalize to a list of dice, you might want to do something along these lines: method new(Int $count, *...@defaults) { my @dice = @defaults.map: { Die.new($_) }; # populate all dice for which no number was provided: for ^($count - @defaults) { @dice.push: Die.new(); } } There are other solutions I can think of that avoid checking for definedness of arguments, but they all involve some trickery. Maybe it becomes cleaner if we move the defaults to the attribute declaration: has $!die1 = Die.new; has $!die2 = Die.new; method new (Int $sides1? Int $sides2?) { my %attributes; %attributes<die1> = Die.new($sides1) if defined $sides1; %attributes<die2> = Die.new($sides2) if defined $sides2; # use the hash as list of named arguments return self.bless( *, |%attributes ); } I haven't tested if this works in Rakudo today, but I think in theory it should. The default attribute values should only be used if no value for that attribute is passed to bless. Hope this helps a bit, Moritz