Home > Raku > Anonymous slurpers

Anonymous slurpers

I have a script where I’m only interested in the last two lines of its output. That’s easy to handle with Shell::Piping.

px«script.sh ./foo.txt» |» my @a;
my ($second-last-line, $last-line) = @a.tail(2);

That works but is wasteful because it stores lines of text in @a and keeps them around until the Array goes out of scope. Also, any temporary variable is a clear indicator of boilerplate. And we don’t do boilerplate in raku.land.

Declarators are quite powerful because they can take a list and immediately hand it over to infix:<=>. We can even skip values by using anonymous scalars.

my ($, $a, $b) = sub-that-returns-a-list();

I want to do something quite similar. Looking for the last two elements means to skip over any but the last two elements. In subscripts and signatures we use the Whatever * to indicate multiplicity. (Sometimes I don’t get English. Why is “manyness” not a word? o.O)

px«script.sh ./foo.txt» |» my (*, $second-last, $last);

That doesn’t quite work because Rakudo doesn’t expect the whatever star in a declarator list. In fact it doesn’t expect any term in that spot. We can work around that by being explicit. While we are on it we may add an anonymous scalar to the mix.

px«script.sh ./foo.txt» |» my (Whatever, $third-last, $, $last);

Declarators return a List of containers and values. We can use introspection to dissect it.

my \l := my (Whatever, $second-last, $, $last);
say l».&{ .WHAT, .VAR.WHAT, .VAR.name };
# OUTPUT: (((Whatever) Whatever anon) ((Any) Any $second-last) ((Any) Any anon) ((Any) Any $last))

By checking both for type objects and for type object and names in .VAR we can tell *, $ and normal containers apart.

sub infix:<|»>(\l, \r) {
    sub is-whatever($_ is raw) { (.VAR.name eq 'anon' && .WHAT === Whatever) but role :: { method gist { self ?? '*' !! '' } } }
    sub is-anon($_ is raw) { (.VAR.name eq 'anon' && .WHAT === Any) but role :: { method gist { self ?? 'anon' !! '' } } }
    sub is-scalar($_ is raw) { (.VAR ~~ Scalar && .WHAT !=== Whatever && .VAR.name ne 'anon') but role :: { method gist { self ?? 'Scalar' !! '' } } }

    sub is-left-slurpy(\l) { l.head.&is-whatever but role :: { method gist { self ?? 'left-slurpy: yes' !! 'left-slurpy: no' } } }
    sub is-right-slurpy(\l) { l.tail.&is-whatever but role :: { method gist { self ?? 'right-slurpy: yes' !! 'left-slurpy: no' } } }

    say r».&{ .&is-whatever, .&is-anon, .&is-scalar }
    say r.&is-left-slurpy;
    say r.&is-right-slurpy;

42 |» my (W, $, $c);
42 |» my ($d, $e, W);

# OUTPUT: ((*  ) ( anon ) (  Scalar))
          left-slurpy: yes
          left-slurpy: no
          ((  Scalar) (  Scalar) (*  ))
          left-slurpy: no
          right-slurpy: yes

Now I have everything I need to teach Shell::Piping to skip over values.

That Rakudo doesn’t allow the Whatever star in declarator lists feels like a bug.

my @List = my (42, "", Cool, $, $a);
dd @List;
Array @List = [Mu, Mu, Cool, Any, Any]

It is perfectly fine to skip over literals, take type objects and keep containers but for whatever reason it doesn’t like stars. However, what does work, works really well. It does provide us with containers that we can reason about via .VAR. Looks like Santa was onto something.

Categories: Raku
  1. No comments yet.
  1. January 25, 2021 at 19:34

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: