(How) does raku do class synonyms? - oop

I have
class Length is Measure export { ... }
I want synonyms that differ only in that the class name is different, I have tried this:
class Distance is Length is export {}
class Breadth is Length is export {}
class Width is Length is export {}
class Height is Length is export {}
class Depth is Length is export {}
This kind of works in that $distance ~~ Length, but I also want $length ~~ Distance.
Some kind or coercion would be desirable - eg $length.Distance ~~ Distance to discourage operations like $width = $height + $depth (ie you cannot always add lengths that point along different axes).
Maybe some class kind of := name binding, or a shorthand way to coerce NxN?
Any advice most gratefully received...

This is not yet an answer, but might grow into one.
Is the following heading in useful directions for you? I don't necessarily mean for you to use composition instead of inheritance (role), or direct aliasing (constant), or mixins (but), or a dynamic type constraint (where). The code below is just a quick way to prototype until you provide feedback.
role Measurement {}
role Height does Measurement {}
role Width does Measurement {}
constant Breadth = Width;
say Width; # (Width)
say Breadth; # (Width)
say ::<Breadth>:kv; # (Breadth (Width))
say Breadth ~~ Width; # True
say Width ~~ Breadth; # True
multi infix:<+>
(::L Measurement \l,
Measurement \r where * !~~ L)
{ fail }
say (42 but Width) + (99 but Breadth); # 141
say (42 but Width) + (99 but Height); # Failed...

I'd probably need to see a few more usage cases to be hundred percent sure what the best approach is, but at least with respect to ensuring that all length
class A { 
method ACCEPTS($other) { $other.isa: A }
}
class B is A { }
class C is A { }
class D is A { }
my $b = B.new;
my $c = C.new;
my $d = D.new;
say $b ~~ D; # True
say $d ~~ C; # True
say $c ~~ B; # True
Because the ACCEPTS is defined on A here, that is what will be used for matching against B/C/D. By default, ACCEPTS smart matches using isa($?CLASS), which is why it returns false, but now it'll lock it into using A always.
Another way is to set the class to an alias. This is normally good for when you want to shorten the name of a longer class you imported from a module, but it will work here too:
class A { ... }
our \B = A; my $b = B.new;
our \C = A; my $c = C.new;
our \D = A; my $d = D.new;
say $b ~~ A & C & D; # True
say $b.WHAT; # A
say $b.WHAT; # A
say $b.WHAT; # A
You'll notice that all these claim to be A because they are. All we've done is give another way to access A. This means that not only does B pass for D or A, it's indistinguishable from them because it's identical to them (down to where it's stored in memory).

Related

IIFE alternatives in Raku

In Ruby I can group together some lines of code like so with a begin block:
x = begin
puts "Hi!"
a = 2
b = 3
a + b
end
puts x # 5
it's immediately evaluated and its value is the last value of the block (a + b here) (Javascripters do a similar thing with IIFEs)
What are the ways to do this in Raku? Is there anything smoother than:
my $x = ({
say "Hi!";
my $a = 2;
my $b = 3;
$a + $b;
})();
say $x; # 5
Insert a do in front of the block. This tells Raku to:
Immediately do whatever follows the do on its right hand side;
Return the value to the do's left hand side:
my $x = do {
put "Hi!";
my $a = 2;
my $b = 3;
$a + $b;
}
That said, one rarely needs to use do.
Instead, there are many other IIFE forms in Raku that just work naturally without fuss. I'll mention just two because they're used extensively in Raku code:
with whatever { .foo } else { .bar }
You might think I'm being silly, but those are two IIFEs. They form lexical scopes, have parameter lists, bind from arguments, the works. Loads of Raku constructs work like that.
In the above case where I haven't written an explicit parameter list, this isn't obvious. The fact that .foo is called on whatever if whatever is defined, and .bar is called on it if it isn't, is both implicit and due to the particular IIFE calling behavior of with.
See also if, while, given, and many, many more.
What's going on becomes more obvious if you introduce an explicit parameter list with ->:
for whatever -> $a, $b { say $a + $b }
That iterates whatever, binding two consecutive elements from it to $a and $b, until whatever is empty. If it has an odd number of elements, one might write:
for whatever -> $a, $b? { say $a + $b }
And so on.
Bottom line: a huge number of occurrences of {...} in Raku are IIFEs, even if they don't look like it. But if they're immediately after an =, Raku defaults to assuming you want to assign the lambda rather than immediately executing it, so you need to insert a do in that particular case.
Welcome to Raku!
my $x = BEGIN {
say "Hi!";
my $a = 2;
my $b = 3;
$a + $b;
}
I guess the common ancestry of Raku and Ruby shows :-)
Also note that to create a constant, you can also use constant:
my constant $x = do {
say "Hi!";
my $a = 2;
my $b = 3;
$a + $b;
}
If you can have a single statement, you can leave off the braces:
my $x = BEGIN 2 + 3;
or:
my constant $x = 2 + 3;
Regarding blocks: if they are in sink context (similar to "void" context in some languages), then they will execute just like that:
{
say "Executing block";
}
No need to explicitely call it: it will be called for you :-)

Rational numbers in Raku

I am using Raku for some computations, because it has nice numeric types. However, I have an issue with using '.raku'
say (1/6+1/6).raku
#<1/3>
We obtain this. However,
say (1/10+1/10).raku
#0.2
Is it a bug? I expected <1/5>. What happens?
In Raku, 0.2 constructs a Rat, and thus produces the very same result as writing 1/5 (which will be constant folded) or <1/5> (the literal form). You only get floating point in the case of specifying an exponent (for example, 2e-1).
The .raku (formerly known as the .perl) method's job is to produce something that will roundtrip and produce the same value if EVAL'd. In the case of 1/5, that can be exactly represented as a decimal, so it will produce 0.2. It only resorts to the fractional representation when a decimal form would not round-trip.
You can always recover the numerator and denominator using the .numerator and .denominator methods to format as you wish. Additionally .nude method returns a list of the numerator and denominator, which one can join with a / if wanted:
say (1/6+1/6).nude.join("/"); # 1/3
say (1/10+1/10).nude.join("/"); # 1/5
Hi #milou123 I was also a bit surprised that raku reverts to decimal representation - I can see that some contexts - such as teaching fractional arithmetic would benefit from having a "keep as rat" mode. Having said that, ultimately it makes sense that there is only one way to .raku something and that decimal is the default representation.
Of course, with raku, you can also just change the language a bit. In this case, I have invented a new '→' postfix operator...
multi postfix:<→> ( Rat:D $r ) { $r.nude.join("/") }
say (1/5+1/5)→; # 2/5
I am not smart enough to work out if the built in 'raku' method can be overridden in a similar way, would be keen to see advice on how to do that concisely...
try this in Julia:
julia> 1 // 10 + 1 // 10
1//5
julia> typeof(1 // 10 + 1 // 10)
Rational{Int64}
julia> 1 // 2 + 1 // 3
5//6
julia> typeof(1 // 2 + 1 // 3)
Rational{Int64}
in the Rat.pm6 implemention, we can only call .raku method on Rat type to get the expected format:
multi method raku(Rat:D: --> Str:D) {
if $!denominator == 1 {
$!numerator ~ '.0'
}
else {
my $d = $!denominator;
unless $d == 0 {
$d = $d div 5 while $d %% 5;
$d = $d div 2 while $d %% 2;
}
if $d == 1 and (my $b := self.base(10,*)).Numeric === self {
$b;
}
else {
'<' ~ $!numerator ~ '/' ~ $!denominator ~ '>'
}
}
}

(Identifier) terms vs. constants vs. null signature routines

Identifier terms are defined in the documentation alongside constants, with pretty much the same use case, although terms compute their value in run time while constants get it in compile time. Potentially, that could make terms use global variables, but that's action at a distance and ugly, so I guess that's not their use case.
OTOH, they could be simply routines with null signature:
sub term:<þor> { "Is mighty" }
sub Þor { "Is mighty" }
say þor, Þor;
But you can already define routines with null signature. You can sabe, however, the error when you write:
say Þor ~ Þor;
Which would produce a many positionals passed; expected 0 arguments but got 1, unlike the term. That seems however a bit farfetched and you can save the trouble by just adding () at the end.
Another possible use case is defying the rules of normal identifiers
sub term:<✔> { True }
say ✔; # True
Are there any other use cases I have missed?
Making zero-argument subs work as terms will break the possibility to post-declare subs, since finding a sub after having parsed usages of it would require re-parsing of earlier code (which the perl 6 language refuses to do, "one-pass parsing" and all that) if the sub takes no arguments.
Terms are useful in combination with the ternary operator:
$ perl6 -e 'sub a() { "foo" }; say 42 ?? a !! 666'
===SORRY!=== Error while compiling -e
Your !! was gobbled by the expression in the middle; please parenthesize
$ perl6 -e 'sub term:<a> { "foo" }; say 42 ?? a !! 666'
foo
Constants are basically terms. So of course they are grouped together.
constant foo = 12;
say foo;
constant term:<bar> = 36;
say bar;
There is a slight difference because term:<…> works by modifying the parser. So it takes precedence.
constant fubar = 38;
constant term:<fubar> = 45;
say fubar; # 45
The above will print 45 regardless of which constant definition comes first.
Since term:<…> takes precedence the only way to get at the other value is to use ::<fubar> to directly access the symbol table.
say ::<fubar>; # 38
say ::<term:<fubar>>; # 45
There are two main use-cases for term:<…>.
One is to get a subroutine to be parsed similarly to a constant or sigilless variable.
sub fubar () { 'fubar'.comb.roll }
# say( fubar( prefix:<~>( 4 ) ) );
say fubar ~ 4; # ERROR
sub term:<fubar> () { 'fubar'.comb.roll }
# say( infix:<~>( fubar, 4 ) );
say fubar ~ 4;
The other is to have a constant or sigiless variable be something other than an a normal identifier.
my \✔ = True; # ERROR: Malformed my
my \term:<✔> = True;
say ✔;
Of course both use-cases can be combined.
sub term:<✔> () { True }
Perl 5 allows subroutines to have an empty prototype (different than a signature) which will alter how it gets parsed. The main purpose of prototypes in Perl 5 is to alter how the code gets parsed.
use v5;
sub fubar () { ord [split('','fubar')]->[rand 5] }
# say( fubar() + 4 );
say fubar + 4; # infix +
use v5;
sub fubar { ord [split('','fubar')]->[rand 5] }
# say( fubar( +4 ) );
say fubar + 4; # prefix +
Perl 6 doesn't use signatures the way Perl 5 uses prototypes. The main way to alter how Perl 6 parses code is by using the namespace.
use v6;
sub fubar ( $_ ) { .comb.roll }
sub term:<fubar> () { 'fubar'.comb.roll }
say fubar( 'zoo' ); # `z` or `o` (`o` is twice as likely)
say fubar; # `f` or `u` or `b` or `a` or `r`
sub prefix:<✔> ( $_ ) { "checked $_" }
say ✔ 'under the bed'; # checked under the bed
Note that Perl 5 doesn't really have constants, they are just subroutines with an empty prototype.
use v5;
use constant foo => 12;
use v5;
sub foo () { 12 } # ditto
(This became less true after 5.16)
As far as I know all of the other uses of prototypes have been superseded by design decisions in Perl 6.
use v5;
sub foo (&$) { $_[0]->($_[1]) }
say foo { 100 + $_[0] } 5; # 105;
That block is seen as a sub lambda because of the prototype of the foo subroutine.
use v6;
# sub foo ( &f, $v ) { f $v }
sub foo { #_[0].( #_[1] ) }
say foo { 100 + #_[0] }, 5; # 105
In Perl 6 a block is seen as a lambda if a term is expected. So there is no need to alter the parser with a feature like a prototype.
You are asking for exactly one use of prototypes to be brought back even though there is already a feature that covers that use-case.
Doing so would be a special-case. Part of the design ethos of Perl 6 is to limit the number of special-cases.
Other versions of Perl had a wide variety of special-cases, and it isn't always easy to remember them all.
Don't get me wrong; the special-cases in Perl 5 are useful, but Perl 6 has for the most part made them general-cases.

Is there a 'clamp' method/sub for ranges/Num etc in Raku (i.e. Perl6)?

Is there a 'clamp' or equivalent method or sub in Perl6?
eg
my $range= (1.0 .. 9.9)
my $val=15.3;
my $clamped=$range.clamp($val);
# $clamped would be 9.9
$val= -1.3;
$clamped=$range.clamp($val);
# $clamped would be 1.0
Another tact you might like to explore is using a Proxy, which allows you to define "hooks" when fetching or storing a value from a container
sub limited-num(Range $range) is rw {
my ($min, $max) = $range.minmax;
my Numeric $store = $min;
Proxy.new(
FETCH => method () { $store },
STORE => method ($new) {
$store = max($min, min($max, $new));
}
)
}
# Note the use of binding operator `:=`
my $ln := limited-num(1.0 .. 9.9);
say $ln; # OUTPUT: 1
$ln += 4.2;
say $ln; # OUTPUT: 5.2
$ln += 100;
say $ln; # OUTPUT: 9.9
$ln -= 50;
say $ln; # OUTPUT: 1
$ln = 0;
say $ln; # OUTPUT: 1
This particular limited-num will initialise with it's min value, but you can also set it at declaration
my $ln1 := limited-num(1.0 .. 9.9) = 5.5;
say $ln1; # OUTPUT 5.5;
my $ln2 := limited-num(1.0 .. 9.9) = 1000;
say $ln2; # OUTPUT 9.9
I don't think so. So, perhaps:
multi clamp ($range, $value) {
given $range {
return .max when (($value cmp .max) === More);
return .min when (($value cmp .min) === Less);
}
return $value
}
my $range = (1.0 .. 9.9);
say $range.&clamp: 15.3; # 9.9
say $range.&clamp: -1.3; # 1
my $range = 'b'..'y';
say $range.&clamp: 'a'; # b
say $range.&clamp: 'z'; # y
The MOP allows direct exploration of the objects available in your P6 system. A particularly handy metamethod is .^methods which works on most built in objects:
say Range.^methods; # (new excludes-min excludes-max infinite is-int ...
By default this includes just the methods defined in the Range class, not the methods it inherits. (To get them all you could use say Range.^methods: :all. That'll net you a much bigger list.)
When I just tried it I found it also included a lot of methods unhelpfully named Method+{is-nodal}.new. So maybe use this instead:
say Range.^methods.grep: * !~~ / 'is-nodal' /;
This netted:
(new excludes-min excludes-max infinite is-int elems iterator
flat reverse first bounds int-bounds fmt ASSIGN-POS roll pick
Capture push append unshift prepend shift pop sum rand in-range
hyper lazy-if lazy item race of is-lazy WHICH Str ACCEPTS perl
Numeric min max BUILDALL)
That's what I used to lead me to my solution above; I sort of know the methods but use .^methods to remind me.
Another way to explore what's available is doc, eg the official doc's Range page. That netted me:
ACCEPTS min excludes-min max excludes-max bounds
infinite is-int int-bounds minmax elems list flat
pick roll sum reverse Capture rand
Comparing these two lists, sorted and bagged, out of curiosity:
say
<ACCEPTS ASSIGN-POS BUILDALL Capture Numeric Str WHICH append
bounds elems excludes-max excludes-min first flat fmt hyper
in-range infinite int-bounds is-int is-lazy item iterator
lazy lazy-if max min new of perl pick pop prepend push
race rand reverse roll shift sum unshift>.Bag
∩
<ACCEPTS Capture bounds elems excludes-max excludes-min flat
infinite int-bounds is-int list max min minmax pick
rand reverse roll sum>.Bag
displays:
Bag(ACCEPTS, Capture, bounds, elems, excludes-max, excludes-min,
flat, infinite, int-bounds, is-int, max, min, pick,
rand, reverse, roll, sum)
So for some reason, list, minmax, and sum are documented as Range methods but are not listed by my .^methods call. Presumably they're called Method+{is-nodal}.new. Hmm.
say Range.^lookup('minmax'); # Method+{is-nodal}.new
say Range.^lookup('minmax').name; # minmax
Yep. Hmm. So I could have written:
say Range.^methods>>.name.sort;
(ACCEPTS ASSIGN-POS AT-POS BUILDALL Bag BagHash Capture EXISTS-POS
Mix MixHash Numeric Set SetHash Str WHICH append bounds elems
excludes-max excludes-min first flat fmt hyper in-range infinite
int-bounds is-int is-lazy item iterator lazy lazy-if list max min
minmax new of perl pick pop prepend push race rand reverse roll
shift sum unshift)
Anyhow, hope that's helpful.
Strange that no one has suggested using augment. Admittedly, it creates global changes, but that might not be an issue.
augment class Range {
method clamp ($value) { ... }
}
You will need to use the pragmause MONKEY-TYPING in the same scope before the augment in order to use it though. But this way, you can simply say $range.clamp(5), for instance. It saves you one character over raiph's answer, but at the (not insignificant) cost of breaking precompilation.

What is the point of coercions like Int(Cool)?

The Perl 6 Web site on functions says
Coercion types can help you to have a specific type inside a routine, but accept wider input. When the routine is called, the argument is automatically converted to the narrower type.
sub double(Int(Cool) $x) {
2 * $x
}
say double '21'; # 42
say double Any; # Type check failed in binding $x; expected 'Cool' but got 'Any'
Here the Int is the target type to which the argument will be coerced, and Cool is the type that the routine accepts as input.
But what is the point for the sub? Isn't $x just an Int? Why would you restrict the caller to implement Cool for the argument?
I'm doubly confused by the example because Int already is Cool. So I did an example where the types don't share a hierarchy:
class Foo { method foomethod { say 'foomethod' } }
class Bar {}
class Quux is Foo {
# class Quux { # compile error
method Bar { Bar.new }
}
sub foo(Bar(Foo) $c) {
say $c.WHAT; # (Bar)
# $c.foomethod # fails if uncommented: Method 'foomethod' not found for invocant of class 'Bar'
}
foo(Quux.new)
Here the invocant of foo is restricted to provide a Foo that can be converted to a Bar but foo cannot even call a method of Foo on $c because its type is Bar. So why would foo care that the to-be-coerced type is a Foo in the first place?
Could someone shed some light on this? Links to appropriate documentation and parts of the spec are appreciated as well. I couldn't find anything useful there.
Update Having reviewed this answer today I've concluded I had completely misunderstood what #musiKk was getting at. This was revealed most clearly in #darch's question and #musiKk's response:
#darch: Or is your question why one might prefer Int(Cool) over Int(Any)? If that's the case, that would be the question to ask.
#musiKk: That is exactly my question. :)
Reviewing the many other answers I see none have addressed it the way I now think it warrants addressing.
I might be wrong of course so what I've decided to do is leave the original question as is, in particular leaving the title as is, and leave this answer as it was, and instead write a new answer addressing #darch's reformulation.
Specify parameter type, with no coercion: Int $x
We could declare:
sub double (Int $x) { ... } # Accept only Int. (No coercion.)
Then this would work:
double(42);
But unfortunately typing 42 in response to this:
double(prompt('')); # `prompt` returns the string the user types
causes the double call to fail with Type check failed in binding $x; expected Int but got Str ("42") because 42, while looking like a number, is technically a string of type Str, and we've asked for no coercion.
Specify parameter type, with blanket coercion: Int() $x
We can introduce blanket coercion of Any value in the sub's signature:
sub double (Int(Any) $x) { ... } # Take Any value. Coerce to an Int.
Or:
sub double (Int() $x) { ... } # Same -- `Int()` coerces from Any.
Now, if you type 42 when prompted by the double(prompt('')); statement, the run-time type-check failure no longer applies and instead the run-time attempts to coerce the string to an Int. If the user types a well-formed number the code just works. If they type 123abc the coercion will fail at run-time with a nice error message:
Cannot convert string to number: trailing characters after number in '123⏏abc'
One problem with blanket coercion of Any value is that code like this:
class City { ... } # City has no Int coercion
my City $city;
double($city);
fails at run-time with the message: "Method 'Int' not found for invocant of class 'City'".
Specify parameter type, with coercion from Cool values: Int(Cool) $x
We can choose a point of balance between no coercion and blanket coercion of Any value.
The best class to coerce from is often the Cool class, because Cool values are guaranteed to either coerce nicely to other basic types or generate a nice error message:
# Accept argument of type Cool or a subclass and coerce to Int:
sub double (Int(Cool) $x) { ... }
With this definition, the following:
double(42);
double(prompt(''));
works as nicely as it can, and:
double($city);
fails with "Type check failed in binding $x; expected Cool but got City (City)" which is arguably a little better diagnostically for the programmer than "Method 'Int' not found for invocant of class 'City'".
why would foo care that the to-be-coerced type is a Foo in the first place?
Hopefully it's now obvious that the only reason it's worth limiting the coerce-from-type to Foo is because that's a type expected to successfully coerce to a Bar value (or, perhaps, fail with a friendly message).
Could someone shed some light on this? Links to appropriate documentation and parts of the spec are appreciated as well. I couldn't find anything useful there.
The document you originally quoted is pretty much all there is for enduser doc. Hopefully it makes sense now and you're all set. If not please comment and we'll go from there.
What this does is accept a value that is a subtype of Cool, and tries to transform it into an Int. At that point it is an Int no matter what it was before.
So
sub double ( Int(Cool) $n ) { $n * 2 }
can really be thought of as ( I think this is how it was actually implemented in Rakudo )
# Int is a subtype of Cool otherwise it would be Any or Mu
proto sub double ( Cool $n ) {*}
# this has the interior parts that you write
multi sub double ( Int $n ) { $n * 2 }
# this is what the compiler writes for you
multi sub double ( Cool $n ) {
# calls the other multi since it is now an Int
samewith Int($n);
}
So this accepts any of Int, Str, Rat, FatRat, Num, Array, Hash, etc. and tries to convert it into an Int before calling &infix:<*> with it, and 2.
say double ' 5 '; # 25
say double 2.5; # 4
say double [0,0,0]; # 6
say double { a => 0, b => 0 }; # 4
You might restrict it to a Cool instead of Any as all Cool values are essentially required to provide a coercion to Int.
( :( Int(Any) $ ) can be shortened to just :( Int() $ ) )
The reason you might do this is that you need it to be an Int inside the sub because you are calling other code that does different things with different types.
sub example ( Int(Cool) $n ) returns Int {
other-multi( $n ) * $n;
}
multi sub other-multi ( Int $ ) { 10 }
multi sub other-multi ( Any $ ) { 1 }
say example 5; # 50
say example 4.5; # 40
In this particular case you could have written it as one of these
sub example ( Cool $n ) returns Int {
other-multi( Int($n) ) * Int($n);
}
sub example ( Cool $n ) returns Int {
my $temp = Int($n);
other-multi( $temp ) * $temp;
}
sub example ( Cool $n is copy ) returns Int {
$n = Int($n);
other-multi( $n ) * $n;
}
None of them are as clear as the one that uses the signature to coerce it for you.
Normally for such a simple function you can use one of these and it will probably do what you want.
my &double = * * 2; # WhateverCode
my &double = * × 2; # ditto
my &double = { $_ * 2 }; # bare block
my &double = { $^n * 2 }; # block with positional placeholder
my &double = -> $n { $n * 2 }; # pointy block
my &double = sub ( $n ) { $n * 2 } # anon sub
my &double = anon sub double ( $n ) { $n * 2 } # anon sub with name
my &double = &infix:<*>.assuming(*,2); # curried
my &double = &infix:<*>.assuming(2);
sub double ( $n ) { $n * 2 } # same as :( Any $n )
Am I missing something? I'm not a Perl 6 expert, but it appears the syntax allows one to specify independently both what input types are permissible and how the input will be presented to the function.
Restricting the allowable input is useful because it means the code will result in an error, rather than a silent (useless) type conversion when the function is called with a nonsensical parameter.
I don't think an example where the two types are not in a hierarchical relationship makes sense.
Per comments on the original question, a better version of #musiKk's question "What is the point of coercions like Int(Cool)?" turned out to be:
Why might one prefer Int(Cool) over Int(Any)?
A corollary, which I'll also address in this answer, is:
Why might one prefer Int(Any) over Int(Cool)?
First, a list of various related options:
sub _Int_strong (Int $) {} # Argument must be Int
sub _Int_cool (Int(Cool) $) {} # Argument must be Cool; Int invoked
sub _Int_weak (Int(Any) $) {} # Argument must be Any; Int invoked
sub _Int_weak2 (Int() $) {} # same
sub _Any (Any $) {} # Argument must be Any
sub _Any2 ( $) {} # same
sub _Mu (Mu $) {} # Weakest typing - just memory safe (Mu)
_Int_strong val; # Fails to bind if val is not an Int
_Int_cool val; # Fails to bind if val is not Cool. Int invoked.
_Int_weak val; # Fails to bind if val is not Any. Int invoked.
_Any val; # Fails to bind if val is Mu
_Mu val; # Will always bind. If val is a native value, boxes it.
Why might one prefer Int(Cool) over Int(Any)?
Because Int(Cool) is slightly stronger typing. The argument must be of type Cool rather than the broader Any and:
Static analysis will reject binding code written to pass an argument that isn't Cool to a routine whose corresponding parameter has the type constraint Int(Cool). If static analysis shows there is no other routine candidate able to accept the call then the compiler will reject it at compile time. This is one of the meanings of "strong typing" explained in the last section of this answer.
If a value is Cool then it is guaranteed to have a well behaved .Int conversion method. So it will not yield a Method not found error at run-time and can be relied on to provide a good error message if it fails to produce a converted to integer value.
Why might one prefer Int(Any) over Int(Cool)?
Because Int(Any) is slightly weaker typing in that the argument can be of any regular type and P6 will just try and make it work:
.Int will be called on an argument that's passed to a routine whose corresponding parameter has the type constraint Int(...) no matter what the ... is. Provided the passed argument has an .Int method the call and subsequent conversion has a chance of succeeding.
If the .Int fails then the error message will be whatever the .Int method produces. If the argument is actually Cool then the .Int method will produce a good error message if it fails to convert to an Int. Otherwise the .Int method is presumably not a built in one and the result will be pot luck.
Why Foo(Bar) in the first place?
And what's all this about weak and strong typing?
An Int(...) constraint on a function parameter is going to result in either:
A failure to type check; or
An.Int conversion of the corresponding argument that forces it to its integer value -- or fails, leaving the corresponding parameter containing a Failure.
Using Wikipedia definitions as they were at the time of writing this answer (2019) this type checking and attempted conversion will be:
strong typing in the sense that a type constraint like Int(...) is "use of programming language types in order to both capture invariants of the code, and ensure its correctness, and definitely exclude certain classes of programming errors";
Currently weak typing in Rakudo in the sense that Rakudo does not check the ... in Int(...) at compile time even though in theory it could. That is, sub double (Int $x) {}; double Date; yields a compile time error (Calling double(Date) will never work) whereas sub double (Int(Cool) $x) {}; double Date; yields a run time error (Type check failed in binding).
type conversion;
weak typing in the sense that it's implicit type conversion in the sense that the compiler will handle the .Int coercion as part of carrying out the call;
explicit type conversion in the sense that the Int(...) constraint is explicitly directing the compiler to do the conversion as part of binding a call;
checked explicit type conversion -- P6 only does type safe conversions/coercions.
I believe the answer is as simple as you may not want to restrict the argument to Int even though you will be treating it as Int within the sub. say for some reason you want to be able to multiply an Array by a Hash, but fail if the args can't be treated as Int (i.e. is not Cool).
my #a = 1,2,3;
my %h = 'a' => 1, 'b' => 2;
say #a.Int; # 3 (List types coerced to the equivalent of .elems when treated as Int)
say %h.Int; # 2
sub m1(Int $x, Int $y) {return $x * $y}
say m1(3,2); # 6
say m1(#a,%h); # does not match
sub m2(Int(Cool) $x, Int(Cool) $y) {return $x * $y}
say m2('3',2); # 6
say m2(#a,%h); # 6
say m2('foo',2); # does not match
of course, you could also do this without the signature because the math operation will coerce the type automatically:
sub m3($x,$y) {return $x * $y}
say m3(#a,%h); # 6
however, this defers your type check to the inside of the sub, which kind of defeats the purpose of a signature and prevents you from making the sub a multi
All subtypes of Cool will be (as Cool requires them to) coerced to an Int. So if an operator or routine internal to your sub only works with Int arguments, you don't have to add an extra statement/expression converting to an Int nor does that operator/routine's code need to account for other subtypes of Cool. It enforces that the argument will be an Int inside of your sub wherever you use it.
Your example is backwards:
class Foo { method foomethod { say 'foomethod' } }
class Bar {}
class Quux is Bar {
method Foo { Foo.new }
}
sub foo(Foo(Bar) $c) {
#= converts $c of type Bar to type Foo
#= returns result of foomethod
say $c.WHAT; #-> (Foo)
$c.foomethod #-> foomethod
}
foo(Quux.new)