DEV Community

Myoungjin Jeon
Myoungjin Jeon

Posted on

[Short Prog] Week Number Getter - Raku - Type Constraint

my previous original programme to show week number of given date looks like

multi sub MAIN ( Int $year, Int $month, Int $day ) {
    say Date.new( :$year, :$month, :$day ).week-number();
}
Enter fullscreen mode Exit fullscreen mode

If we don't use any data type in the parameter, you need to make sure all variables are suitable for the function. if there are many functions, it might be frustrating!!
It is safe to believe that type checking at "entrance" of a programme (which especially needs I/O or user input) is a great way to make the routines inside the function is easy to implement.
But wait... are year, month and day Integer exactly??

USING subset

raku provide a way to make your own type from existing one.

subset Natural of UInt  where 0 < *;
Enter fullscreen mode Exit fullscreen mode

We just create a Natural number type from existing UInt (Unsigned Int)

Year is okay with just Natural type. but let's make others.

subset Month of Natural where * < 13;
subset Day of Natural   where * < 32;
Enter fullscreen mode Exit fullscreen mode

Does it sound correct? some might say "day is depends on the Month value".
but which is sadly up to your routines inside the function or Library which you are using.

So now our new MAIN function looks like

multi sub MAIN ( Natural $year, Month $month, Day $day ) {
    say Date.new( :$year, :$month, :$day ).week-number();
}
Enter fullscreen mode Exit fullscreen mode

and my full programme looks like

subset Natural of UInt  where 0 < *;
subset Month of Natural where * < 13;
subset Day of Natural   where * < 32;

sub cutoff-last-two-chars ( Str $i is rw        # warning: side effects
                            where $i.chars > 1 ) {

    my $last-two-chars := $i.substr-rw( *-2, * );
    my $ret = $last-two-chars;
    $last-two-chars = ""; # remove them from the string
    return $ret;
}

multi sub MAIN ( Natural \yyyymmdd ) {
    my $yyyymmdd = yyyymmdd;
    my UInt ( $year, $month, $day );
    $day   = (cutoff-last-two-chars $yyyymmdd).UInt;
    $month = (cutoff-last-two-chars $yyyymmdd).UInt;
    $year  = $yyyymmdd.UInt;

    samewith( $year, $month, $day );
}

multi sub MAIN ( Natural $year, Month $month, Day $day ) {
    say Date.new( :$year, :$month, :$day ).week-number();
}
Enter fullscreen mode Exit fullscreen mode

One more thing : Don't be too strict

the way to get last two digit from user input has been slightly changed. but look at the signature:

sub cutoff-last-two-chars ( Str $i is rw        # warning: side effects
                            where $i.chars > 1 ) {
   ...
Enter fullscreen mode Exit fullscreen mode

"$i.chars > 1" check the $i has two or more than 2 characters so that we can get expected result from a string. however this could be resulted in runtime exception:
let's check in Raku's REPL

> my $test = "804";
804
> cutoff-last-two-chars($test)
04
> cutoff-last-two-chars($test)
Constraint type check failed in binding to parameter '$i'; expected anonymous constraint to be met but got Str ("8")
  in sub cutoff-last-two-chars at <unknown file> line 1
  in block <unit> at <unknown file> line 1

>
Enter fullscreen mode Exit fullscreen mode

you can see the errors which is unexpected because we only wanted to make the function perfect and safe but we also make our "programme itself" unsafe in the end.

You can check each variable before calling cutoff-last-two-chars to make the programme detect problem and finish it nicely. but.. this make programme only more complicated.
So there is another solution for this. As I mentioned before, check user input at the entrance of the programme.

multi sub MAIN ( Natural \yyyymmdd where yyyymmdd > 10100 ) {
    my $yyyymmdd = yyyymmdd;
    ...
Enter fullscreen mode Exit fullscreen mode

and you can remove the constraint from the cutoff-last-two-digits function or keep. It depends on your decision. but IMHO in script language world, less constraints means more performance!
So, please don't be too strict!

That's all for today! 🤩

Discussion (0)