[Short Prog] Week Number Getter - Raku - Type Constraint
Tags: raku
Published: 2021-08-08 11:39:00

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

```perl
multi sub MAIN ( Int $year, Int $month, Int $day ) {
    say Date.new( :$year, :$month, :$day ).week-number();
}
```

  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.
```perl
subset Natural of UInt  where 0 < *;
```
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;
```

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
```perl
multi sub MAIN ( Natural $year, Month $month, Day $day ) {
    say Date.new( :$year, :$month, :$day ).week-number();
}
```

and my full programme looks like
```perl
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();
}
```

## *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:
```perl
sub cutoff-last-two-chars ( Str $i is rw        # warning: side effects
                            where $i.chars > 1 ) {
   ...
```
"$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
```perl
> 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

>
```
  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.

```perl
multi sub MAIN ( Natural \yyyymmdd where yyyymmdd > 10100 ) {
    my $yyyymmdd = yyyymmdd;
    ...
```

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! 🤩