Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I take a reference to new?

Suppose I have the following code:

my constant @suits = <Clubs Hearts Spades Diamonds>;
my constant @values = 2..14;

class Card {
    has $.suit;
    has $.value;

    # order is mnemonic of "$value of $suit", i.e. "3 of Clubs"
    multi method new($value, $suit) {
        return self.bless(:$suit, :$value);
    }
}

It defines some suits and some values and what it means to be a card.

Now, to build a deck, I essentially need to take the cross product of the suits and the values and apply that to the constructor.

The naiive approach to do this, would of course be to just iterate with a loop:

my @deck = gather for @values X @suits -> ($v, $c) {
    take Card.new($v, $c);
}

But this is Raku, we have a cross function that can take a function as an optional argument!, so of course I'm gonna do that!

my @deck = cross(@values, @suits, :with(Card.new));
# Unexpected named argument 'with' passed
# in block <unit> at .\example.raku line 36

... wait no. What about this?

my @deck = cross(@values, @suits):with(Card.new);
# Unexpected named argument 'with' passed
# in block <unit> at .\example.raku line 36

Still nothing. Reference maybe?

my @deck = cross(@values, @suits):with(&Card.new);
# ===SORRY!=== Error while compiling D:\Code\Raku/.\example.raku
# Illegally post-declared type:
#    Card used at line 36

I read somewhere I can turn a function into an infix operator with []

my @deck = cross(@values, @suits):with([Card.new]);
# Unexpected named argument 'with' passed
# in block <unit> at .\example.raku line 36

That also doesn't work.

If classes are supposed to just be modules, shouldn't I then be able to pass a function reference?

Also why is it saying 'with' is that's unexpected? If I'm intuiting this right, what it's actually complaining about is the type of the input, rather than the named argument.

like image 575
Electric Coffee Avatar asked Jun 04 '20 18:06

Electric Coffee


Video Answer


1 Answers

The error message is indeed confusing.

The :with parameter expects a Callable. Card.new is not a Callable. If you write it as :with( { Card.new($^number, $^suit) } ), it appears to work.

Note that I did not use $^value, $^suit, because they order differently alphabetically, so would produce the values in the wrong order. See The ^ twigil for more information on that syntax.

The error is LTA, this makes it a little bit better.

To get back to your question: you can find the code object that corresponds to Card.new with ^find_method. However, that will not work, as Card.new actually expects 3 arguments: the invocant (aka self), $value and $suit. Whereas the cross function will only pass the value and the suit.

like image 81
Elizabeth Mattijsen Avatar answered Sep 27 '22 17:09

Elizabeth Mattijsen