Le Tue, 06 Nov 2012 00:58:53 +0100, Hibou57 (Yannick Duchêne)
<
yannick...@yahoo.fr> a écrit:
Back to tell more, and I hop, clearer.
First a summary, then later more details: there's a difference between
reuse inheritance and interface inheritance; both must not be confused
(unfortunately, it often is), then (as already said) there's a set of
values and a set of types; here also, both must not be confused
(unfortunately, due to the use of inheritance for reuse, this confusion
often occurs too).
Personally, I see types only via their interface, so to me the proper
inheritance is the interface inheritance. The following is not personal
opinion: a type is defined by a set of value and a set of operations, with
properties. A derived type must provide at least the same set of
operations, and in the programming area, it must provide all, even the
ones which could be derived from provided ones. Ex. even if the equality
may be derived from some others primitives, if it was specified in a
parent interface it will have to be provided (software design and math
analysis differs a bit here, software design is a bit more strict, as it
does not automatically derives things which could be). Then, with
operations, comes two things: domains and codomains (also known as target
domain). For each operation of a derived type, the domain must be a
superset (at worst, an equal set) of the domain of the corresponding
operation in the parent. Inversely, the codomain of an operation must be a
subset (at worst an equal set). Hence, things cannot be described as set
ownership, except for the set of operations, and the set of values is not
enough for a caracterisation. You have: a set of values, a set of
operations, operation's domain sets, and operation's codomain sets.
Now, when you create a “derived” type from Integer, you are restricting
both the domain and the codomain, which makes it anything you would want,
but not an interface derivation. That's OK with the codomain which become
a subset, but the domain too becomes a subset (domain and codomain are
covariant in this case), which is wrong for an interface derivation.
For a numeric type, say `My_Integer_Type`, to be really derived from
`Integer`, as an example, the signature for the addition should be defined
as:
function "+" (Left, Right: Integer) return My_Integer_Type;
This is actually not the case, and you get instead:
function "+" (Left, Right: My_Integer_Type) return My_Integer_Type;
Conclusion: `My_Integer_Type` does not belong to anything like an
`Integer'Class` (which does not exist in Ada, and Ada is right with).
If there was an universal_integer class to which `Integer` would belong,
then we should have something like:
function "+" (Left, Right: universal_integer) return Integer;
which does not exist as‑is (except for intermediate results).
So, what's this, if not a proper type derivation (interface inheritance)?
That's a reuse inheritance. Strictly speaking, reuse inheritance should
never appears, that's a dangerous practice (from a design point of view),
and composition should be used instead, defining a fully new root type and
root class in the while.
Ada is right with its notion of subtype, like in:
subtype My_Integer_Type is Integer range -10 .. 10;
But Ada is a bit wrong (personal opinion) with this:
type My_Integer_Type is new Integer range -10 .. 10;
It would better be instead:
subtype My_Integer_Type is new Integer range -10 .. 10;
When you do:
type My_Integer_Type is new Integer range -10 .. 10;
you do this just to reuse the Integer type's physical representation and
built‑in primitives, not to define a type belonging to an Integer class.
I said the inclusion relation you gave is the reverse of that of the class
relation, here is why: if there is a proper type derivation, that's not
Integer which derives from universal_integer, but the opposite,
universal_integer which derives from Integer. You can check if you try to
apply the rules of a valid type/interface derivation. The domain of
operation of universal_integer is a superset of that of Integer, the
codomain is not a subset, but equals the same codomain when the input
belongs to the same domain, so that universal_integer could belong to a
class defined by Integer; not the other way. Similarly, Integer belongs to
the class of Positive or any subtype of Positive. With the numeric type
hierarchy, the real type hierarchy is head down feet up: universal_integer
inherits the interface of all Positive subtypes and all Integer subtypes.
All of this also explains why a subtype is not the same as a type
derivation (and is even the opposite), and Ada is nice to provide this
distinction (I know no other language with this, even SML don't have
this). At least, the existence of a notion of subtype, makes things like
constants, parameter modes and others, describable in the own terms of
Ada, properly (Dmitry gave a good example).