Parameterised Types
Hans-Dieter.Dreier@materna.de
Hans-Dieter.Dreier@materna.de
Mon, 17 May 1999 18:29:04 +0200
--ks8deCx4ecYBByykPj81c1ph2FEIVrHa
Content-type: text/plain; charset="ISO-8859-1"
Content-transfer-encoding: quoted-printable
>Hans-Dieter Dreier wrote:
>
>> Conceptually, for me type parameters are instance items. Strictly speaki=
ng,
>> List <Integer> and List <SomethingElse> are actually the same type, but =
each
>> of them may or may not be used in some context because their different
>> parameterization results in different constraints. Those may or may not
>> evaluate to true in some context. Example:
>> =
>> List <GeneralObject> a ;
>> List <SpecialObject> b;
>> b may be used as a data source where a can be used because in this case =
the
>> contents of a may be substituted by the contents of b. So it is legal to
>> write
>> a =3D b;
>> but it is illegal to write
>> b =3D a;
>
>This is difficult in general because of the following case.
>
>class A<X>
> Field: X
> ...
>
>procedure ...
> ...
> Variable: A<General> :=3D new A<Special>
> Variable.Field :=3D new General
>
>Here Variable.Field gets a General object although it is only allowed a
>special object. Generic parameters have to be invariant where they are
>used to instantiate read-write variables. Where the X was only used for
>read-only variables, the covariant relationship you outlined would
>hold. Where the X was only used for write-only variables, it would be
>contravariant instead.
If it is possible to detect situations like to one shown above (and I belie=
ve it is), the compiler may insert a runtime check. Though I don't like thi=
s as much as static type checks, it's better than nothing.
>> The implementation of our parser may of course *assume* that there is su=
ch a
>> constraint if a type parameter is of type type because this will most of=
ten
>> be the case. Then things may probably be simplified by making the value =
of
>> this type a part of the type name of the parametrized type (similar to n=
ame
>> decoration in C++). But if we do this, we should keep in mind that it is=
a
>> mere simplification and not a concept in its own right.
>
>Hmm, could you elaborate?
It would construct a name for internal use from the type and the actual cla=
ss parameters, something like @A@@X@ and match those. So "Variable" is of t=
ype @A@@General@ and since that is different from @A@@Special@, types would=
be different and assignment would be possible only if a conversion were a=
vailable.
>> The bounds may even be changed at runtime, resulting in a reallocation. =
Some
>> programming languages allow this. I always found it very convenient to u=
se.
>
>This is possible, but it would invalidate existing references. How is
>this usually handled?
References to the moved object are fixed up if the object cannot be realloc=
ated in-place. No problem if we don't allow <shudder> pointer-arithmetics <=
/shudder>.
>Copying GCs already have to handle this sort of
>situation. What languages do you know that do this?
Centura (IIRC PowerBuilder as well).
>This is similar to
>being able to change an object's impl, parent impl, etc. at run-time.
Not really. The basic structure of an instance doesn't change (there are no=
additional "names"); its length was not known at compile time anyway.
>I think some object-based inheritance languages allow the addition of
>fields at run-time, in parallel to a type downcast.
IMO that's quite another thing.
>
>I'm not sure this would always work easily in general however. The
>canonical example of value-driven data is an array but generic value
>parameters can be used for typing as well as allocation.
Yes. The fact that allocation is dynamic must be made explicitly available =
to the compiler, like this:
Class X (iItems)
Instance items
...
Y: x [iItems]
Y would need to be the last instance item if we demand instance items' offs=
ets to be known at compile time. =
>> Contravariance is what I would allow by default.
>
>That would be the other option I'd take. I haven't really decided
>whether contravariance or invariance is better.
If we express variance in terms of assertions, the programmer choose which =
one he wants, even on a per-instance-item basis. It could be tricky however=
to determine in which case to apply the default, since it needs looking at=
the semantics of each assertion. Otherwise the "natural" default would be =
"no type checking" which would not be good...
>> I'd suggest to use normal function syntax, ie write
>> AnIntegerArray =3D new Array (Integer);
>> instead of
>> AnIntegerArray =3D new Array <Integer>;
>
>Possibly. () is usually used for construction parameters, which are
>handled differently, so I'd like a way of demonstrating to the reader
>that they are a different type of parameter, say by putting the type
>parameters first and separating them by a "|" or similar symbol. Since
>the type parameter list is defined in a different place to the
>construction paremeter list, it would be handy to determine which list
>has been passed to incorrectly better, which would be easier if you
>separated them.
I never liked the C++ way of naming the constructor he same as the class. =
Instead, we should choose the Eiffel way and flag methods as "creation feat=
ures".
Assuming a constructor called "new", we would create a parameterized class =
like this:
X (MyClassParameter).new (MyCreationParameter)
>> The C++ template syntax makes parsing unneccessarily hard because <> are
>> used as operators too.
>
>Well I don't think this would affect a top-down parser.
Of course it does. It can cause a lot of backtracking. Just look at the fol=
lowing:
-------Snip------
template <class X> class Test
{
int operator> (X x) {return true}
int operator< (X x) {return false}
Test()
{
Test<Test> Test =3D new Test<Test>;
Test<Test> Test; // now is this a comparison or a declaration?
// my claim is that it might be either
}
~Test ();
};
Test<int> A ();
------Snap-------
This compiles with VC++ 6.0.
You get the point why I don't like C++ <template syntax>?
Oh, BTW, I'd not allow to have any two identifiers with the same name in th=
e same scope. I simply do not believe that it adds to clarity or enriches t=
he language if you have lots of polymorphic functions with the same name th=
at may or may not do the same thing. My approach would be: (Re-)Name them d=
ifferently and use conversions instead. The above example would not be poss=
ible if C++ would stick to this rule. They could tell exactly what is a var=
iable name and what is a type name.
>A bottom up
>parser might be affected, but I doubt this is significant. Error
>recovery could be a bit more important.
You bet!
> Normal Field Subtype Param Client Param
>Part Of Variable Type Spec N Y Y
>Can Be Overriden Y Y N#
>Can Be Nonconstant Y N N
>Specified At Instantiation N* N@ Y
>Restricted To Type Fields N Y Y
>
># =3D Unless provided as a default value in the absence of client
>specification.
>* =3D Only through constructor parameters.
>@ =3D Implictly.
My idea is to not specify such exact rules, but rather see it from the usag=
e side: The programmer may specify what he wants for, say, a client paramet=
er,
as long as the compiler can distill sense out of it. If he insists on passi=
ng a string as a class parameter, so be it. There are but a few situations =
where the compiler *must* know the value of an expression, and I would stat=
e *them* as rules:
1. To determine class layout (feature offsets, vtable layout)
2. To translate feature names to offsets or other access code
I cannot think of other restrictions at the moment, but surely there are mo=
re. Maybe you can name some. Even assertions' values need not necessarily b=
e known at compile time, as long as code for the appropriate runtime check =
can be generated.
I think that this approach may lead to a much shorter set of rules than wou=
ld result if we try to deal with each case as you did, while at the same ti=
me being more liberal.
In the general case, results will be similar to your table.
>-- =
> Matthew Tuck - Software Developer & All-Round Nice Guy
> mailto:matty@box.net.au (ICQ #8125618)
> Check out the Ultra programming language project!
> http://www.box.net.au/~matty/ultra/
>
--
Regards,
Hans-Dieter Dreier
(Hans-Dieter.Dreier@materna.de)=
--ks8deCx4ecYBByykPj81c1ph2FEIVrHa
Content-type: text/plain; charset="ISO-8859-1"
Content-transfer-encoding: quoted-printable
IDENTIFIKATIONSANGABEN:
a23343a.txt IA5 DX-MAIL X.400 User Agent=
--ks8deCx4ecYBByykPj81c1ph2FEIVrHa--