Search code examples
rolerakumultimethod

Why does a Perl 6 Str do the Positional role, and how can I change []?


I'm playing around with a positional interface for strings. I'm aware of How can I slice a string like Python does in Perl 6?, but I was curious if I could make this thing work just for giggles.

I came up with this example. Reading positions is fine, but I don't know how to set up the multi to handle an assignment:

multi postcircumfix:<[ ]> ( Str:D $s, Int:D $n --> Str ) {
    $s.substr: $n, 1
    }
multi postcircumfix:<[ ]> ( Str:D $s, Range:D $r --> Str ) {
    $s.substr: $r.min, $r.max - $r.min + 1
    }
multi postcircumfix:<[ ]> ( Str:D $s, List:D $i --> List ) {
    map( { $s.substr: $_, 1 }, @$i ).list
    }

multi postcircumfix:<[ ]> ( Str:D $s, Int:D $n, *@a --> Str ) is rw {
    put "Calling rw version";
    }


my $string = 'The quick, purple butterfly';

{ # Works
my $single = $string[0];
say $single;
}

{ # Works
my $substring = $string[5..9];
say $substring;
}

{ # Works
my $substring = $string[1,3,5,7];
say $substring;
}

{ # NOPE!
$string[2] = 'Perl';
say $string;
}

The last one doesn't work:

T
uick,
(h   u c)
Index out of range. Is: 2, should be in 0..0
  in block <unit> at substring.p6 line 36

Actually thrown at:
  in block <unit> at substring.p6 line 36

I didn't think it would work, though. I don't know what signature or traits it should have to do what I want to do.

Why does the [] operator work on a Str?

$ perl6
> "some string"[0]
some string

The docs mostly imply that the [] works on things that do the Positional roles and that those things are in list like things. From the [] docs in operators:

Universal interface for positional access to zero or more elements of a @container, a.k.a. "array indexing operator".

But a Str surprisingly does the necessary role even though it's not an @container (as far as I know):

> "some string".does( 'Positional' )
True

Is there a way to test that something is an @container?

Is there a way to get something to list all of its roles?

Now, knowing that a string can respond to the [], how can I figure out what signature will match that? I want to know the right signature to use to define my own version to write to this string through [].


Solution

  • One way to achieve this, is by augmenting the Str class, since you really only need to override the AT-POS method (which Str normally inherits from Any):

    use MONKEY;
    augment class Str {
        method AT-POS($a) {
            self.substr($a,1);
        }
    }
    say "abcde"[3];     # d
    say "abcde"[^3];    # (a b c)
    

    More information can be found here: https://docs.raku.org/language/subscripts#Methods_to_implement_for_positional_subscripting