Raku signature (Array @r) is not (Array:D)?
Asked Answered
C

3

9

I can not seem to make head or tails of this Raku error message I found while exploring grammars...


Cannot resolve caller report(Array:D); none of these signatures matches:
(Array @r)
(Match $r)

So an Array is not an Array?! How does that work and how do I find out why? Here is the full program and output.

#!/usr/bin/env perl6
use v6;
grammar Integers {
    rule TOP      { ^ .*? <targets> .* $  }
    rule targets  { <integer>+ % ',' }
    token integer { \d+ }
}

multi sub report(Array @r) { say @r.raku }
multi sub report(Match $r) { say $r.raku }

sub MAIN() {
    my $result = Integers.parse(' a 1234 ');
    report($result);
    report(%$result{'targets'});
    report(%$result{'targets'}{'integer'});
}
#`( output:
Match.new(:orig(" a 1234 "), :from(0), :pos(8), :hash(Map.new((:targets(Match.new(:orig(" a 1234 "), :from(3), :pos(8), :hash(Map.new((:integer([Match.new(:orig(" a 1234 "), :from(3), :pos(7))]))))))))))
Match.new(:orig(" a 1234 "), :from(3), :pos(8), :hash(Map.new((:integer([Match.new(:orig(" a 1234 "), :from(3), :pos(7))])))))
Cannot resolve caller report(Array:D); none of these signatures matches:
    (Array @r)
    (Match $r)
  in sub MAIN at /home/hans/Programming/Raku/Parsing/Array_Array.raku line 16
  in block <unit> at /home/hans/Programming/Raku/Parsing/Array_Array.raku line 3
)
Chartography answered 19/1, 2023 at 1:46 Comment(0)
G
8

You appear confused about the functionality of sigils and typing.

When you use the @ sigil, you are implying a Positional constraint.

sub foo(@a) { ... }

will take anything that does the Positional role.

foo( @b );
foo( [1,2,3] );
foo( (1,2,3) );

When you specify a type with a @ in a signature, you're indicating that you want a Positional that only accepts that type as its elements. Now, as a parameter to a subroutine, that severely limits what you can specify, as [1,2,3] is not an array with a constraint on Int, it's an array that happens to be filled with Int values only.

Now back to your example: what does (Array @r) mean? It means that you want a Positional that constrains on Array objects. In simple terms: you want an array that must consist of arrays as its elements.

I don't think that is what you intended?

Now, why does (Array $r) work? Because that indicates you want an object that is an Array as the parameter. This also limits what you want, because then it has to be an Array, and cannot be a List for instance.

sub foo(Array $r) { ... }
foo(@a);         # works
foo( [1,2,3] );  # works
foo( (1,2,3) );  # does *not* work

Finally, I think you want to use the built-in dd instead of building your own report function.

Gulosity answered 19/1, 2023 at 10:7 Comment(2)
So (Array @r) could be described as array of arrays r, or as array of Positionals r? As for why I am re-implementing dd, that's how I am learning the syntax for accessing Match object properties.Chartography
Positional @r would be an array of Positionals, so Array @r is indeed an array of Arrays. :-)Gulosity
C
4

Well, I have a partial answer to my own question. If I change (Array @r) to (Array $r) of if I just drop Array and use (@r).

Per docs.raku.org "If no type is provided by the user Raku assumes the type to be Any". So that explains why (@r) works. It just (Any @r)...I think.

As for why (Array:D) must be coded as (Array $r), I'm still trying to figure that one out. Curiouser and curiouser.

Chartography answered 19/1, 2023 at 2:44 Comment(1)
You could code it as Array:D $r if you wanted. The more generic Array $r will work as well, but is more generic -- it will accept an Array even if it's just an Array type object not an instance.Heaven
W
4

Hi @FyFAIR and welcome to raku. IMO raku is an awesome tool, but there are few guardrails. So please be patient, since when it clicks, it will be worth it ;-)

I have taken the liberty of re-drafting your example the way I would do it:

grammar Integers {
    token TOP      { ^ .*? <targets> .* $  }
    token targets  { <integer>+ % ',' }
    token integer { \d+ }
}

sub MAIN() {
    my $result = Integers.parse(' a 1234 '); 
    dd $result;
    dd $result<targets>;
    dd $result<targets><integer>;    #<== a list of matches with one element
    dd $result<targets><integer>[0];
    say "$result<targets><integer>[0]";
}

Integers $result = Match.new(:orig(" a 1234 "), :from(0), :pos(8), :hash(Map.new((:targets(Match.new(:orig(" a 1234 "), :from(3), :pos(7), :hash(Map.new((:integer([Match.new(:orig(" a 1234 "), :from(3), :pos(7))]))))))))))
Match.new(:orig(" a 1234 "), :from(3), :pos(7), :hash(Map.new((:integer([Match.new(:orig(" a 1234 "), :from(3), :pos(7))])))))
[Match.new(:orig(" a 1234 "), :from(3), :pos(7))]
Match.new(:orig(" a 1234 "), :from(3), :pos(7))
1234

So... here are my remarks:

  • you are very close (!)
  • @lizmat's guidance on dd has served me well
  • use @ as the sigil for @lists unless you want a surprise
  • just use token unless you really need rule
  • types are great for checks and multis ... I get my code functional without types first and then gradually layer them in so I can see if they are doing the job
  • Int @array means that the capture will check for e.g. my Int @array = [1,2,3] which (as lizmat says) is not the same as an array with Int values [1,2,3] (here the type check is delegating the control of what is assigned to the array)
  • no need for perl style derefs like %$
  • <key> is the same as {'key'} but less typing
  • the Match object is very complex and powerful ... dd shows the innards, but this is usually overkill
  • use say and stringify the Match with "" (or .Str) to get the thing you really need
  • use Grammar::Tracer; is you friend
Weathered answered 20/1, 2023 at 21:56 Comment(1)
Thank you for the rewrite and remarks, especially Grammar:Tracer.Chartography

© 2022 - 2024 — McMap. All rights reserved.