8

While finalizing my upcoming Raku Advent Calendar post on sigils, I decided to double-check my understanding of the type constraints that sigils create. The docs describe sigil type constraints with the table below:

Based on this table (and my general understanding of how sigils and containers work), I strongly expected this code

my %percent-sigil is List = 1,2;
my @at-sigil is Map = :k<v>;

to throw an error.

Specifically, I expected that is List would attempt to bind the %-sigiled variable to a List, and that this would throw an X::TypeCheck::Binding error – the same error that my %h := 1,2 throws.

But it didn't error. The first line created a List that seemed perfectly ordinary in every way, other than the sigil on its variable. And the second created a seemingly normal Map. Neither of them secretly had Scalar intermediaries, at least as far as I could tell with VAR and similar introspection.

I took a very quick look at the World.nqp source code, and it seems at least plausible that discarding the % type constraint with is List is intended behavior.

So, is this behavior correct/intended? If so, why? And how does that fit in with the type constraints and other guarantees that sigils typically provide?

(I have to admit, seeing an %-sigiled variable that doesn't support Associative indexing kind of shocked me…)

codesections
  • 8,900
  • 16
  • 50

2 Answers2

5

I think this is a grey area, somewhere between DIHWIDT (Docter, It Hurts When I Do This) and an oversight in implementation.

Thing is, you can create your own class and use that in the is trait. Basically, that overrides the type with which the object will be created from the default Hash (for %) and Array (for @ sigils). As long as you provide the interface methods, it (currently) works. For example:

class Foo {
    method AT-KEY($) { 42 }
}
my %h is Foo;
say %h<a>;  # 42

However, if you want to pass such an object as an argument to a sub with a % sigil in the signature, it will fail because the class did not consume the Associatve role:

sub bar(%) { 666 }
say bar(%h);
===SORRY!=== Error while compiling -e
Calling bar(A) will never work with declared signature (%)

I'm not sure why the test for Associative (for the % sigil) and Positional (for @) is not enforced at compile time with the is trait. I would assume it was an oversight, maybe something to be fixed in 6.e.

Elizabeth Mattijsen
  • 25,654
  • 3
  • 75
  • 105
1

Quoting the Parameters and arguments section of the S06 specification/speculation document about the related issue of binding arguments to routine parameters:

Array and hash parameters are simply bound "as is". (Conjectural: future versions ... may do static analysis and forbid assignments to array and hash parameters that can be caught by it. This will, however, only happen with the appropriate use declaration to opt in to that language version.)

Sure enough the Rakudo compiler implemented some rudimentary static analysis (in its AOT compilation optimization pass) that normally (but see footnote 3 in this SO answer) insists on binding @ routine parameters to values that do the Positional role and % ones to Associatives.

I think this was the case from the first official Raku supporting release of Rakudo, in 2016, but regardless, I'm pretty sure the "appropriate use declaration" is any language version declaration, including none. If your/our druthers are static typing for the win for @ and % sigils, and I think they are, then that's presumably very appropriate!


Another source is the IRC logs. A quick search quickly got me nothing.

Hmm. Let's check the blame for the above verbiage so I can find when it was last updated and maybe spot contemporaneous IRC discussion. Oooh.

That is an extraordinary read.

"oversight" isn't the right word.

I don't have time tonight to search the IRC logs to see what led up to that commit, but I daresay it's interesting. The previous text was talking about a PL design I really liked the sound of in terms of immutability, such that code could become increasingly immutable by simply swapping out one kind of scalar container for another. Very nice! But reality is important, and Jonathan switched the verbiage to the implementation reality. The switch toward static typing certainty is welcome, but has it seriously harmed the performance and immutability options? I don't know. Time for me to go to sleep and head off for seasonal family visits. Happy holidays...

raiph
  • 31,607
  • 3
  • 62
  • 111