Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can Raku guarantee that a pattern match is exhaustive (at compile time)?

Consider the following toy code:

my $age-check = do given 18 { 
    when $_ > 18 { 'old enough' }
    when $_ < 18 { 'too young'  }
};

say "The user is $age-check"  # OUTPUT: «The user is False»

This code contains a bug (not handling the case where the input is exactly 18) that results in a runtime error. Is there any way to catch this error at compile time by requiring the given block to exhaustively match? It seems like there should be a way to use a CHECK phaser or something similar to require the match to be exhaustive, but I'm not quite sure how I'd go about doing so.

(I know you could catch the bug earlier during runtime with a default case that throws an error, but that's not what I'm asking about. If Raku doesn't have a way to enforce exhaustive matches at compile time, that's not a big drawback to the language – but it can be a helpful feature to have.)

like image 685
codesections Avatar asked Aug 18 '20 12:08

codesections


Video Answer


1 Answers

While it's possible you could write a module to enforce this stuff (especially once RakuAST comes up) by mucking with given statements, it's going to be very tricky and really only possible for basic numerical operations.

The semantics of given/when basically are

given $foo {         # topicalize $foo
    when $bar1 { … } #   if    $foo ~~ $bar1
    when $bar2 { … } #   elsif $foo ~~ $bar2
    when $bar3 { … } #   elsif $foo ~~ $bar3
    default    { … } #   else 
}

If your conditions are complex things like .is-prime and * %% 2, or even non-deterministic (smartmatching against a mutable object), it will either be difficult or impossible to know for sure.

If you want to enforce this level of strict behavior for certain subsets of conditions, you might be able to do a workaround similar to:

sub is-exhaustive(@conditions) { 
   ... # complex algorithm to determine that the conditions are exhaustive
}

my %foo = 
  less-than-eighteen => *  < 18,
  more-than-eighteen => *  > 18,
  exactly-eighteen   => * == 18;

CHECK die unless is-exhaustive %foo.values;

given $bar {
  when %foo<less-than-eighteen> { ... }
  when %foo<more-than-eighteen> { ... }
  when %foo<exactly-eighteen>   { ... }
}
like image 82
user0721090601 Avatar answered Sep 28 '22 16:09

user0721090601