Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Put named capture from regex in Subset into a variable in the signature

Tags:

raku

Consider

subset MySubset of Str where * ~~ /^ \d $<interesting> = ( \d+ ) $/;

Now I want to use the subset as a Type in my signature, but put the captured part(s) into a variable via unpacking, kinda like

sub f( MySubset $( :$interesting ) )
{
    say $interesting;
}

f( "12345678" ); # should say 2345678

That's not working of course. Is it even possible to do this?

like image 996
Holli Avatar asked Dec 18 '22 13:12

Holli


2 Answers

Subsignature unpacking is about turning a value into a Capture and matching against that.

class Point {
  has ( $.x, $.y );
}

my ( :$x, :$y ) := Point.new( x => 3, y => 4 ).Capture;

say "[$x,$y]"; # [3,4]

Since a Str doesn't have a public attribute named $.interesting, it won't match.

A subset is just extra code to check a value more completely than you could otherwise do. It does not turn the value into a new type.


It would be more likely to work if you used $<interesting>.

sub f( MySubset )
{
    say $<interesting>;
}

Of course since blocks get their own $/, this also does not work.


While it might be nice to pass information from a subset to a signature, I am not aware of anyway to do it.


As a side note, where already does smart matching so it is an incredibly bad idea to use ~~ inside of it.

This is basically how your subset works:

"12345678" ~~ ( * ~~ /…/ )

In this particular case you could just use .substr

sub f( MySubset $_ )    {
    .substr(1)
}
like image 141
Brad Gilbert Avatar answered May 23 '23 10:05

Brad Gilbert


I can't figure out a way with a subset type, however there is a way - with a little...creativity - to do a match and unpack it in the signature.

Match inherits from Capture, so having one be unpacked in a signature is straightforward - if only we can arrange for there to be a parameter that contains the Match we wish to unpack. One way to do that is to introduce a further parameter with a default. We can't really stop anyone passing to it - though we can make it a pain to do so by using the anonymous named parameter. Thus, if we write this:

sub foo($value, :$ (:$col, :$row) = $value.match(/^$<col>=[<:L>+]$<row>=[\d+]$/)) {
    say $col;
    say $row;
}

And call it as foo("AB23"), the output is:

「AB」
「23」

Finally, we may factor the rule out to a named token, achieving:

‌‌my token colrow { ^$<col>=[<:L>+]$<row>=[\d+]$ }
sub foo($value, :$ (:$col, :$row) = $value.match(&colrow)) {
    say $col;
    say $row;
}
like image 42
Jonathan Worthington Avatar answered May 23 '23 10:05

Jonathan Worthington