FAQ
Background and Initial Questions

I'm excited to see such great advances in Rakudo and have enjoyed its
fundamental power and beauty. In reading Tim Bunce's blog, I saw a
call for an introduction to Perl 6 for Java programmers. Since Java
programming is a big part of my day job, I decided to see where I could
get with that guide.

Now I have questions and one apparent problem, though I hope that is with
me and not with Rakudo or Perl 6. In addition to my questions, I welcome
any input regarding style, naming conventions, etc. I want to write the
best Perl 6 for my guide.

Allow me an understatement: Java is Object Oriented. So, I think a guide
for the Java inclined should begin with classes. Here is my first class:

class Die;

has Int $!sides;

method new( Int $sides = 6 ) {
return self.bless( *, :$sides );
}

method roll() returns Int {
return round 1 + rand * $!sides;
}

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? In particular, what does the asterisk mean and
do I have other choices for it? Further, why does the sides attribute
need a leading colon (or does it)?

Problems with Default Arguments in Aggregating Constructors

Having made a single Die with my previous example, I set out to make
a pair of dice as would be needed for a game like Backgammon, Monopoly, etc.
This class aggregates two Die objects and rolls them in simulation:

use Die;

class Die-Pair;

has Die $!die1;
has Die $!die2;
has Int $!total = 0;
has Bool $.was-doubles;

method new( Int $sides1?, Int $sides2? ) {
my Die $die1 = Die.new( sides => $sides1 );
my Die $die2 = Die.new( sides => $sides2 );
return self.bless( *, :$die1, :$die2 );
}

method roll() returns Int {
my Int $roll1 = $!die1.roll();
my Int $roll2 = $!die2.roll();
$!total = $roll1 + $roll2;
$!was-doubles = ( $roll1 == $roll2 );

return $!total;
}

my Die-Pair $die-pair = Die-Pair.new( 6, 6 );

for 0..12 -> $i {
my $roll = $die-pair.roll();
my $was-doubles = $die-pair.was-doubles();

say "$i Roll! $roll was doubles? $was-doubles";
}

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.

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.

Thanks for reading,

Phil

Search Discussions

  • Moritz Lenz at Sep 18, 2010 at 3:04 pm

    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
  • Yary at Sep 18, 2010 at 6:31 pm
    <bikeshead>
    return round 1 + rand * $!sides;

    Might be good to refer to http://perlcabal.org/syn/S32/Containers.html
    for a more idiomatic way of picking an integer, eg "return
    (1..$sides).pick"

    A "roll" method was just added, though I don't think its implemented
    yet. If you're only getting one value then "pick" and "roll" are the
    same.

    </bikeshead>
    -y
  • Moritz Lenz at Sep 18, 2010 at 9:35 pm

    yary wrote:
    A "roll" method was just added, though I don't think its implemented
    yet.
    It is. .roll is what .pick(:replace) used to be, so it was "just" a
    matter of splitting up the existing methods

    $ ./perl6 -e 'say ~(1..6).roll(10)'
    3 1 5 6 5 6 5 4 2 5

    (works in Rakudo HEAD, and will work in the 2010.09 release).

    Cheers,
    Moritz

Related Discussions

Discussion Navigation
viewthread | post
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