[M3devel] small objects
Rodney M. Bates
rodney.m.bates at cox.net
Tue Apr 7 18:11:48 CEST 2009
Tagged types and object type hierarchies:
At first, I was imagining that you could create
an object subtype of a TAGGED type:
(* An untagged hierarchy: *)
TYPE O1 = OBJECT F1: INTEGER END;
TYPE O2 = O1 OBJECT F2: CHAR END;
TYPE O3 = O2 OBJECT F3: BOOLEAN END:
(* Tag each one directly: *)
TYPE TO1 = TAGGED O1;
TYPE TO2 = TAGGED O2;
TYPE TO3 = TAGGED O3;
(* A tagged hierarchy: *)
TYPE TO2S = TO1 OBJECT F2: CHAR END;
TYPE TO3S = TO2S OBJECT F3: BOOLEAN END;
Is TO2S = TO2 and TO3S = TO3?
Is O2 <: TO2S and O3 <: TO3S?
Therein lies tar. The type equality and subtype relations are getting
awfully elaborate for a language that advertises relative simplicity.
Then I thought about two parallel hierarchies, one tagged and one not,
as Tony's second proposal. But that leaves it awfully inconvenient
to have both a tagged and untagged counterpart that are otherwise the
same, and you need the untagged counterpart to do ISTYPE/NARROW/TYPECASE/:=
to get the "definitely-a-heap-reference" value out of a tagged type.
If I read it right, Tony's first proposal does not allow to subclass an
already tagged type (i.e., no "tagged hierarchy" as above). Is that
what you meant, Tony? If so, I think this is enough, and avoids the
complicated type equality and subtype relations.
A separate issue:
On the INTEGER side of this, I think we need a builtin type that is an
implementation-defined subrange of INTEGER, that can hold just the
integer values a tagged type can hold. Maybe "TAG"? (Actually, this
name is a poor choice, since it's not the tag itself, but it's the best
I can think right now, so I will use it in discussion).
If we really want to constrain the implementation to always use exactly
the low bit as the tag, then CARDINAL would do for this. A new builtin
type would leave open other tagging representations with other ranges of
representable integers, as well as the low-bit tag with signed integers.
Then we could also allow ISTYPE(x, TAG), etc. to get the "definitely-an-
integer" value out.
This gives fully type-safe support for small objects, which I really
prefer, if not too complicated.
Tony Hosking wrote:
> I like the notion of having a TAGGED INTEGER type that is a hybrid
> ordinal/reference.
>
> Subtyping rules for references would now be as follows:
>
> NULL <: REF T <: REFANY
> TAGGED INTEGER <: REF T <: REFANY
>
> ROOT <: REFANY
> NULL <: T OBJECT ... END <: T
> TAGGED INTEGER <: T OBJECT ... END <: T (but only for T <: REFANY, not
> T <: ADDRESS)
>
> Thus, TAGGED INTEGER is a funny sort of hybrid ordinal/reference
> type. Values of TAGGED INTEGER are non-pointer values similar to the
> value NIL. We can then do ISTYPE(x, TAGGED INTEGER), and NARROW(x,
> TAGGED INTEGER), and TYPECODE(TAGGED INTEGER), similarly to ISTYPE(x,
> NULL), NARROW(x, NULL), and TYPECODE(NULL).
>
> Because TAGGED INTEGER is an ordinal we can extract the integer value
> it holds using ORD(x).
> Similarly, we can construct a TAGGED INTEGER using VAL(x, TAGGED
> INTEGER).
>
> ***The only problem with this scheme is how to efficiently perform
> run-time tests for dereferencing NULL, and TAGGED INTEGER?***
>
> So, here is a slightly less elegant alternative that is probably
> straightforward to implement. Introduce a separate TAGGED hierarchy.
>
> NULL <: REF T <: REFANY
> NULL <: UNTRACED REF T <: ADDRESS
> TAGGED INTEGER <: TAGGED REF T <: TAGGED REFANY
>
> Note that NULL is not a subtype of TAGGED REF T or TAGGED REFANY.
>
> ROOT <: REFANY
> TAGGED ROOT <: TAGGED REFANY
> NULL <: T OBJECT END <: T where T <: REFANY or T <: ADDRESS
> TAGGED INTEGER <: T OBJECT ... END <: T where T <: TAGGED REFANY
>
> Note that NULL is not a subtype of T OBJECT ... END where T <: TAGGED
> REFANY.
>
> This way, tagged references only need a run-time test for the tag on
> dereference (we can throw an "attempt to dereference TAGGED INTEGER"
> at run time, just like we throw "attempt to dereference NIL" for
> untagged references). This check can be a straightforward test of the
> low bit tag. Of course, the weird thing here is now that we don't
> have a single NULL value for TAGGED references --- we have multiple
> null values VAL(x, TAGGED INTEGER). Instead of asking "x == NIL" we
> must ask "ISTYPE(x, TAGGED INTEGER)".
>
> Of course, because TAGGED INTEGER is an ordinal, we can also perform
> the usual ordinal operations like INC(x), DEC(x), wherever x is typed
> TAGGED INTEGER.
>
More information about the M3devel
mailing list