Home > Raku > They returned an empty package

They returned an empty package

I don’t like to solve maths-puzzles. I do like to read other folks solutions thought. You never know where to spot a new idiom. A good way to find them is to look for code that feels unusual.

method normalize (Numeric:D $sum = 1) {
  my $total = self.total or return;
  my $factor = $sum / $total;
  %!pmf.values »*=» $factor;
  self;
}

I have never seen the construct in the first line, at least not in a method. The return is triggered when self.total returns something falsesy, like 0. It protects the 2nd line from a division by zero by returning Nil. Let’s see if that actually works.

$cookie.multiply('Bowl 1', 0);
$cookie.multiply('Bowl 2', 0);
say 'probability it came from Bowl 1: ', $cookie.P('Bowl 1');

# OUTPUT: Attempt to divide by zero when coercing Rational to Str
            in sub MAIN at tbr-pmf.rakumod line 73
            in block <unit> at tbr-pmf.rakumod line 3

Well, it does blow up some place else. This is not an unreasonable scenario either. When I’m around, the likelihood of a cookie to come from bowl1 or bowl2 is indeed 0. Returning to normalize we can check what happens when a method that should return self returns Nil.

class C {
    method foo { Nil }
}

dd C.new.foo.bar;
# OUTPUT: Nil

The cause of this mis-dispatch can be found in src/core.c/Nil.pm6:16:

method FALLBACK(| --> Nil) { }

Nil is the baseclass of Failure and as such will only throw when assigned to or used to gain values from a list. It’s purpose is to revert containers to their default value. In a numeric context it will warn and turn into 0. Eventually it might end up in a division and cause much grief. Dealing with a depressing lack of cookies could be done with a multi-method.

   multi method P ($key where { self.total == 0 } ) {
        0
   }
   multi method P ($key) {
      die "no key '$key' in PMF" unless %!pmf{$key}:exists;
      return %!pmf{$key} / self.total;
   }

This works because self is an implicit part of the signature of a method (sans an explicit invocant).

Another thing I never seen before is the following gist method.

method gist () {
  return gather {
    take '---';
    for %!pmf.keys.sort -> $key {
      take "  «$key» {%!pmf{$key}}";
    }
  }.join("\n");
}

Using take to prefix the resulting list is quite neat. Using a gather-block avoids any nesting that might need flattening afterwards. In a gist-method it’s a bit wasteful though. We like to truncate lists after 100 elements in say. This could be done with a .head(100) after .keys. Or we skip the rather slow gather-block altogether (each take fires a control exception and Rakudo can not optimise those away yet).

method gist () {
  ( |'---', |%!pmf.keys.sort.map: { "  «$_» %!pmf{$_}" } ).head(100).join($?NL)
}

I like to avoid explicit flattening by using Slips. If the map-block gets complicated, this allows to sneak a .hyper in to gain some speed on large lists.

Please keep in mind that cookies are nice and Nil is shifty.

Categories: Raku
  1. No comments yet.
  1. August 9, 2021 at 21:19

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 )

Google photo

You are commenting using your Google 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: