Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass regex as an argument to subroutine in Perl 6

Tags:

raku

Probably I'm doing something totally wrong, but is there some way to modify and combine regexes using subroutines? The program below wouldn't compile.

sub a(Regex $r1) {
  return rx/ <$r1> a /
}

my regex letter_b { b };

my $new = a(letter_b);
say $new;
like image 416
Eugene Barsky Avatar asked Dec 13 '22 20:12

Eugene Barsky


2 Answers

Looking at the documentation for Regex, it says:

A named regex can be defined with the regex declarator followed by its definition in curly braces. Since any regex does Callable introspection requires referencing via &-sigil.

my regex R { \N };
say &R.^name; # OUTPUT: «Regex␤»

Also, the Grammar documentation (for the concept, not the Type), it explains more about this:

The main ingredient of grammars is named regexes. While the syntax of Perl 6 Regexes is outside the scope of this document, named regexes have a special syntax, similar to subroutine definitions:

my regex number { \d+ [ \. \d+ ]? }

In this case, we have to specify that the regex is lexically scoped using the my keyword, because named regexes are normally used within grammars.

Being named gives us the advantage of being able to easily reuse the regex elsewhere:

say so "32.51" ~~ &number;                                    # OUTPUT: «True␤»
say so "15 + 4.5" ~~ / <number> \s* '+' \s* <number> /        # OUTPUT: «True␤»

So, to pass your named regex as a parameter into your a subroutine, all you have to do is prefix it with an ampersand:

my $new = a(&letter_b);
#           ^
#  This is what you need!

say $new;
say so "ba" ~~ $new;         # OUTPUT: «True␤»
say so "ca" ~~ $new;         # OUTPUT: «False␤»
like image 145
callyalater Avatar answered Dec 16 '22 09:12

callyalater


Starting with @callyalater's conclusion[1]:

my $new = a(&letter_b);
#           ^
#  This is what you need!

In the rest of this answer I address two follow on questions I imagine folk might still have if @callyalater's answer doesn't work for them.

  • Why?

  • What's with the error message?

Why?

Almost all programming languages have named functions you can declare once and then call any number of times. Many support functions as first class values. Raku does. This means raku allows you to refer to an already declared function in two fundamentally distinct ways:

  • To "call" a function, write its name. It will actively do what it's declared to do.

  • To just "refer" to a function as a first class value, prepend a &. The reference will be passively passed on.

Thus:

sub foo { say 42 }
say foo; # 42
say &foo # &foo

Before I can continue, I must note that things get slightly tricky when writing about these things in English or whatever human language. First, by definition in English, if we write anything then we're referring to it! Also, one can't actually "call" foo in English. So the convention is to write just foo when it should be obvious by context whether we mean calling it (in raku, foo) or merely "referring" to it (in raku, &foo).

Unlike other languages, in raku regexes are functions. In the second line of the following code, letter_b is being called, not merely referred to:

my regex letter_b { b }
letter_b

So in my $new = a(letter_b);, the regex letter_b is being called, and the result of that call would be passed to the call of a, whereas in my $new = a(&letter_b);, you're merely passing a reference to letter_b.

What's with the error message?

Too few positionals passed; expected 1 argument but got 0␤

There are several classes of functions in raku. One is Regex. The Regex class is a sub-class of Method:

my regex foo { ... }
say &foo ~~ Method; # True
my method bar { ... }
say &bar ~~ Method; # True

What happens if you call the method bar without an invocant?:

bar # Too few positionals passed; expected 1 argument but got 0 

That's why you get that error message if you call letter_b without an argument/invocant.

(If you declare a method in a grammar, it automatically gets the grammar class/object as its invocant; remember that grammars are classes.)

Footnotes

[1] I often review an answer of mine if someone upvotes it long after I wrote it. Someone just did. If I see a way to improve the collective usefulness of our comments/answers, eg by deleting or editing my question, I do so, sometimes by rewriting it to work as a comment relative to the accepted answer.

like image 40
raiph Avatar answered Dec 16 '22 09:12

raiph