Consider this:
sub id (Any $x) returns Any { return($x) }
sub length (Str $y) returns Int { ... }
length(id("abc"));
Under standard static subtyping rules, this call will perform three
different typechecks:
1) "abc".does(Any) # ("abc" as Str) ===> (Any $x) in &id
2) $x.does(Any) # ($x as Any) ===> (returns Any) in &return
3) Any.does(Str) # (returns Any) ===> (Str $y) in &length
The final (returns Int) is unimportant here.
Obviously, typecheck #3 fails, as Any cannot do Str. Indeed, there is
no type literal in the return position that can satisfy this static
typecheck for &id, other than the bottom type which would be a subtype
for every type. Let's call it All:
sub id (Any $x) returns All { return($x) }
However, had we used that, #2 will fail, as it would now be checking for
$x.does(All), which is guaranteed to fail regardless of whether the
check occurs at runtime (Str.does(All)) or compile time (Any.does(All)).
Hence, it seems to me that there are only four ways out:
A) Omit the #3 check from compile time; at runtime, use the actual
type of $x. The "returns" type annotation will not propagate
outward to the caller.
At compile time, check for #2: Any.does(Any)
At runtime, check for #2: "abc".does(Any)
check for #3: "abc".does(Str)
B) Omit the #2 check from both compile time and runtime; this allows
us to write the "returns All" version.
At compile time, check for #3: All.does(Str)
At runtime, check for #3: "abc".does(Str)
C) Make the return type observe both #2 and #3 at compile time,
using junctive types to pass both checks:
sub id ( Any $x ) returns Any|All { return($x) }
D) Make the return type observe both #2 and #3 at compile time,
using type variables:
sub id ( (::T) $x ) returns ::T { return($x) }
At this moment, I don't have a strong preference to either; I'm more
curious on whether this topic has been covered before by p6l and @Larry.
Thanks,
/Autrijus/
pgpRRbY7g7rEe.pgp
Description: PGP signature
