(Warning, this note is kind of rambling with no real point.)
Finkel says, describing Modula-3's subtyping rules:
If every value of one type is a value of the second, then the first type is a called a 'subtype' of the second. For example, a record type TypeA is a subtype of another record type TypeB only if their fields have the same names and the same order, and all of the types of the fields of TypeA are subtypes of their counterparts in TypeB.
Initially, this struck me as violating ML's "value restriction" and therefore being unsafe, but I was wrong. Suppose we say
type Ushort = 0..65535;
Long = -2147483648..2147483647;
TypeB = record
Data: Long;
end;
TypeA = record
Data: Ushort;
end;
Now it seems that Ushort is a subtype of Long, because every value
of type Ushort is a value of type Long. So it is safe to assign a
value of type Ushort to a variable of type Long. TypeA and
TypeB have fields of the same names in the same order, and the
single field in TypeA has a type that is a subtype of the single
field in TypeB.
However, I guess if you're passing by value, you can safely copy a
record of type TypeA into a variable declared as a record of type
TypeB, and you're still safe with no run-time type checks.
It's only once you get into aliasing that you start running into
problems; if you have a record that is mutably accessible through both
TypeA and TypeB pointers, you can set its Data to -1 through
the TypeB pointer and then access it through the TypeA pointer.
It seems that if you could ensure that the TypeB pointer were
const --- that is, didn't allow modification of the pointed-to value
--- you could avoid this. That would allow the following subtyping
rule:
if TypeA is a subtype of TypeB
-------------------------------------------------------
then pointer to TypeA is a subtype of pointer to const TypeB
That allows you to copy a pointer to TypeA into a variable declared as
pointer to const TypeB. Which is pretty much equivalent to copying a
TypeA into a variable declared as TypeB, but more efficient.
When it comes to arrays of the same element type but varying sizes,
the definition of "A is a subtype of B" that works in the above
subtyping rule is "A's indices are a superset of B's indices". That
is, if you have an int[100] array, it's perfectly OK to copy a
pointer to it someplace that is expecting a pointer to a const
int[10] array; no run-time type checks will be needed. Actually,
though, that's true even if that pointer isn't const --- which I
guess is Finkel's point when he distinguishes "extensions" from
"subtypes".
Anyway, just some interesting things I hadn't realized before about safe static typing. There's a subtyping relation that still applies after you take mutable references, and another one that doesn't, and the one that doesn't can be pretty broad.