Re: The meaning of returns
HaloO Autrijus, you wrote: D) Make the return type observe both #2 and #3 at compile time, using type variables: sub id ( (::T) $x ) returns ::T { return($x) } And this is a natural extension to guide the inferencer so it won't be totally giving up on polymorphic functions such as id. C) and D) can be taken together, resulting to a powerful soft typed language. This is my preference. The only known issue with parametric typing is the proliferation of params as soon as you want to stay away from unpecificity. However, if we take the view that type annotation are merely storage allocation hints and runtime coercers, then A) is probably the way to go. Please no. Or at least not exclusively. I see your storage allocation hints as a data environment needed to implement the type. -- $TSa.greeting := HaloO; # mind the echo!
The meaning of returns
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
Re: The meaning of returns
On Thu, Jul 28, 2005 at 05:03:05AM +0800, Autrijus Tang wrote: Hence, it seems to me that there are only four ways out: Some annotations copied from discussion in #perl6: 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) This is the view that says (returns Foo) amounts to declare a: my sub return (Foo $x) { *return($x) } and perform no static checking for return types to match their calling context. It's got the dynamic language feel, which means practically no nontrivial type errors will happen at compile time, because function calls, regardless of its declared return type, can be used in any type context. 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) This is quite absurd, and was included only for completeness. 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) } This is similar to the approach taken by OO-style local type inferencers; see the Colored local type inference paper for details. The idea is that, if one omits the returns declaration from a function of undecidable type, the inferencer can silently fill in (Any|All) and make the program compile, effectively defer typechecks to runtime. On the other hand, if the user does provide a type annotation, then both #2 and #3 will be observed, and type errors can occur. It's closer to the soft typing or incremental typing idea. D) Make the return type observe both #2 and #3 at compile time, using type variables: sub id ( (::T) $x ) returns ::T { return($x) } And this is a natural extension to guide the inferencer so it won't be totally giving up on polymorphic functions such as id. C) and D) can be taken together, resulting to a powerful soft typed language. However, if we take the view that type annotation are merely storage allocation hints and runtime coercers, then A) is probably the way to go. Thanks, /Autrijus/ pgpcVsbqfgSlq.pgp Description: PGP signature
Re: The meaning of returns
On Thu, Jul 28, 2005 at 05:57:28AM +0800, Autrijus Tang wrote: On Thu, Jul 28, 2005 at 05:03:05AM +0800, Autrijus Tang wrote: Hence, it seems to me that there are only four ways out: Some annotations copied from discussion in #perl6: Last time I reply to myself on this thread, hopefully. :-) 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) This is quite absurd, and was included only for completeness. On second thought, this is not as absurd as it seems. This view is that sub f () returns Foo means f() can be used in any place that a Foo literal can occur, which means that it can be used in contexts that demands a supertype of Foo (like Any), but never a subtype of Foo. The language defined this way would still be dynamic; disabling #2 means that the type of $x will not be checked against Foo upon return($x), so anything at all can be used in that position. Meaningful type errors can occur, for example when saying close(length(abc)), where length would have returns Int and close have a IO parameter. However, this does require the curious device of returns All as the default return type, unless we eliminate that using type variables and/or type inferencing, at which time we can get #2 back for free and implement full static typechecking anyway. Thanks, /Autrius/ pgpaH5X3qHeTw.pgp Description: PGP signature