First, consider the stated examples for Generic type parameters, from the
passage which defines the terminology in S02:
sub max (Num ::X @array) {
push @array, X.new();
}
sub compare (Any ::T $x, T $y) {
return $x eqv $y;
}
There are only two paragraphs and these two examples, so many questions are
"begged". I propose to answer many of these with this treatment, and hold it
up for peer review and acceptance. I also want to propose a new feature change.
Question 1 - can the Any be left off? That is, ::X as an undeclared type name
used as the only type, syntactically correct?
Question 2 - is $x constrained to ::T or does that just note what it was
originally? (in cases where $x is not read only. In this example that is moot.)
The syntax to define the value type for variables provides for multiple types
simply listed one after the other, which are taken to mean "and".
Dog Fish $x;
This means that $x must have both Dog and Fish interfaces. A better example
might be
Document Storable Positional $doc;
where the code makes use of several different fairly abstract interfaces and
doesn't care what kind of concrete class it gets.
So, we might want to capture that concrete type to, and write
Document Storable Positional ::DocType $doc;
Now the fact that this is a generic type notwithstanding, it is just another
type in the list. It just happens to be the same type that is the actual type
$doc was initialized with. But, it implies that yes, $doc is constrained to
that type from then on. The generic type just customizes the list at run-time,
but it is still a list of juxaposed types, so they are ANDed together.
Mammel Elephant $dumbo;
might be redundant if Elephant .does Mammel. But that's beside the point.
Likewise that DocType (after binding) subsumes all the others in the list.
Now we have a list of juxaposed type names, that are ANDed together. The
generic type is the one that wasn't defined (as a type) before. It needs the
:: so it doesn't flag as an undefined symbol, but the others could have the
sigil if you wanted to be explicit:
::Document ::Storable ::Positional ::DocType $doc;
Whether or not the sigil was present is lost on the semantic meaning, once it
gets past the parser. The only reason we had it on ::DocType or ::T was
because it was not defined.
The only real difference between the generic type and the others is that one
was not previously known. Really, it is just a list of type names.
So,
::T $x;
Should be just fine. The parser is not depending on another type already seen
before that. It is a list of one type, and after it figures that out, it later
notices that it is not defined yet.
That answers the two questions, based only on other things in the Synopses.
Now for a proposal, and the observation of an issue.
The only thing that makes a generic type parameter generic is that it was
previous undefined as a type. So, what if you have code that's working just
fine, and then some other change puts a symbol T into the lexical scope,
perhaps as a global import? Boom! The code changes meaning drastically.
Contrast that to the normal meaning of declaring a variable, in which case it
hides anything with the same name in the outer scopes. Introducing a global
one does not change the code that uses lexical variables. Except for generics.
That is inconsistant and wrong for the same reason that it would be wrong for
all other kinds of symbols.
To address this, I propose using a positive way to declare generic parameters
rather than having them implicit based on not previously existing. I propose
the triple colon, :::, for this purpose:
:::T $x;
I beleive this does not conflict with anything, and it is in the same spirit as
@@ and ;;.
--John