FAQ

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

Search Discussions

Discussion Posts

Previous

Follow ups

Related Discussions

Discussion Navigation
viewthread | post
posts ‹ prev | 2 of 4 | next ›
Discussion Overview
groupperl6-users @
categoriesperl
postedSep 18, '10 at 2:15p
activeSep 18, '10 at 9:35p
posts4
users3
websiteperl6.org

3 users in discussion

Moritz Lenz: 2 posts Phil Crow: 1 post Yary: 1 post

People

Translate

site design / logo © 2021 Grokbase