[M3devel] small objects
Mika Nystrom
mika at async.caltech.edu
Fri Apr 10 20:35:10 CEST 2009
Ahhhh!!!!! Now I get it, Rodney!
You're talking about using tagged types "within" Modula-3.
I've just been asking for tagged types to do something "that's not
Modula-3". I'm completely aware that passing REFANY around is not
the "Modula-3 way", and had no intention of making it so. (That's
why I keep saying that only lazy people do it, so it doesn't matter.)
My desire for storing integers and pointers in the same machine word
is not something I thought would ever be useful in Modula-3 itself.
But now what you say makes sense to me... you want to build your
ADTs with this. Then you do need the full M3 type system "above"
your tagged reference.
I agree with you on MOST of the following:
>I really feel that the fad of all dynamic typing in languages is a huge
>mistake and that it will someday be recognized as such. In the
>meantime, we have a language that provides a very extensive set
>of statically-typed alternatives, while also allowing dynamic typing
>when you really need it. This is one of Modula-3's best principles.
>Let's don't erode the static alternatives unnecessarily.
I program, currently, mostly in Scheme and in Modula-3, (mostly M3)
and I am constantly amazed by:
1. The way that Modula-3 programs often are correct the first time
they are compiled, even if they haven't been carefully designed.
Using the M3 type system can really make your code obviously correct.
2. The way that Scheme programs can sometimes abstract away all sorts
of type irrelevancies. In M3 you'd have to have many versions of the
same code, or at least many generic instances. This is especially
true for "higher-higher order" types, where one type is made from
another and that was made from another, etc. (Interpret "type"
liberally, please. It's probably just some sort of lambda expression.)
Now I agree that untyped languages are probably a fad, and they have
set computing back by years or decades(?). Too easy to make mistakes.
But there is something of value there, and my feeling is that the best
way to take advantage of it is to *combine* the two worlds, rather than
trying to come up with some sort of "super language" that incorporates
the best of both worlds and somehow manages to step on no one's toes.
I'm presenting this as an alternative to type inferencing schemes in
environments such as ML.
So my idea, my "vision" if you like, is that we embed untyped
languages in Modula-3. The parts of the system that need performance
or are more convenient to program in the "bondage and discipline"
world of Algol get written in Modula-3, and the parts of the system
that it is more convenient (all things considered) to program in a
typeless environment can be programmed in Scheme (currently, other
interpreted languages can be added later if this turns out to be a
good idea). But it is clear that the typeless approach of REFANY
(REALLYREFANY, including the tagged types) is necessary to implement
this layer of the system---if you want to be able to pass data
structures seamlessly between the two layers.
Maybe that explains better where I'm coming from. I have no intention
of using these types for Modula-3 programming :-)
However, I do see your point. A facility provided is a facility
used, so if there's a very compact way of storing data in pointers,
other applications than the ones I have in mind will use them, too,
and perhaps lead to an abuse of Modula-3. But I'm not sure if this
isn't over the line of legislating programming *style*---if we go
down that route, we can do it the Java way and ban UNSAFE as well,
so people won't be tempted to program C in Modula-3. In any case,
the particular feature that one can hide 31 bits of information in
a REFANY (and only a REFANY) can't hurt you if you don't choose to
avail yourself of it---in fact it's transparent to people who don't
care about it. Ok, an added 2 instructions out of 1,000 on an
ISTYPE or TYPECASE of r : REFANY, but come on, that's not really
an argument!
So I am supportive of making a new type hierarchy TAGGED T for any
T. And yes I understand that now---once you have TAGGED T---it is
trivial to add a TAGGED REFANY, which you can distinguish from
REFANY. I think your arguments for the TAGGED hierarchy are very
good.
However, I still don't think you have made a good argument *against*
being able to hide 31 bits of information in a REFANY. You don't
have to use the facility if it doesn't meet your requirements!
Please remember that what I've been proposing is not a language
change at all but a request that the runtime system and compiler
respect a couple of not-difficult-to-ensure properties, so that we
may do some new tricks in UNSAFE code. Nothing more than that.
Mika
P.S. You didn't actually give any examples of code where you
would, after *your* proposed change, still use REFANY instead of
switching the code to TAGGED REFANY. You said that:
>And TAGGED REFANY generalizes it from REFANY.
i.e., container code would switch to TAGGED REFANY
>The other use is for the abstract type itself. Forgetting tagged
>types for a moment, I have never seen an interface/module that
>uses REFANY for this. Instead, the modules declare their abstract
>type as some proper subtype of REFANY, usually an opaque type.
i.e., other code doesn't actually use REFANY!
(For what it's worth, your experience here matches mine. Container
types (may) use REFANY and ADTs (almost) never do.)
So back to my question: where would you use REFANY???? If never,
then why bother having the two roots?
What you're saying is that after *my* proposed runtime change, *you*
will be tempted to abuse REFANY for ADTs. I don't think this is a
very good argument against my proposal (but it is a good argument
for yours, which is not incompatible with mine but instead expands it).
The only thing that makes your proposal slightly incompatible with
mine is that you're insisting on having a new distinction between
REFANY and TAGGED REFANY, which I am saying will never be used in
practice. And both your experience with Modula-3 and mine seem to
back up this assertion.
"Rodney M. Bates" writes:
>Mika Nystrom wrote:
>> Sorry to splice together two emails from you, but I feel I've already
>> used up my m3devel quota this week:
>>
>> "Rodney M. Bates" writes:
>>
>>>> Are you sure? I want a type---some type---that can hold "any
>>>> reference, even a tagged one", and I would rewrite most library
>>>> code that today takes REFANY to take that instead. Why not? Why
>>>> would I want to limit it to REFANY when it performs no operations
>>>> that couldn't legally be performed on TAGGED REFANY.
>>>>
>>>>
>>> I believe my "safe" proposal would make this very simple, if I am
>>> thinking of the kind of library code you are.
>>>
>>> I envision a container data structure that takes in things and returns
>>> them without performing operations on them other than moving them around
>>> and storing them, e.g. the ever belabored stack. The values going in
>>> and out are declared REFANY, and the client passes in values of
>>> some proper subtype of REFANY, which get assigned to the REFANY
>>> parameters. When it gets them back, it narrows them to this type.
>>>
>>> In this pattern, just changing the declared type of the container values
>>>
>> >from REFANY to TAGGED REFANY would do it. The library code's storage
>> ...
>>
>There are two very different kinds of uses of reference and
>tagged types. The one I speak of above is for the type of the
>elements inside a container data structure. In this case, it
>is the client that knows what type the value really is, and will
>declare it as such. The library ADT module should know as little
>as possible about it, so it will declare it as REFANY or
>TAGGED REFANY. Here, you want the most general type
>possible, just to make the abstraction more versatile.
>And TAGGED REFANY generalizes it from REFANY.
>
>But...
>>
>
>
>>>> you'd like to store these in a REFANY and dynamically test for the
>>>> appropriate tagged type:
>>>>
>>> No, I do not want to store these in a variable of type REFANY or any
>>> other existing type.
>>> In fact, I want to forbid it.
>>>
>>
>> I think you are thinking of exactly the same kind of library code.
>> TextRefTbl.T, RefList.T, etc.
>>
>> After the introduction of TAGGED REFANY, what use is there for
>> REFANY? What piece of code can you think of where the cost of the
>> 1-LSB check of TAGGED REFANY outweighs the convenience of being
>> able to process objects of type TAGGED T (for all ref types T) as
>> well as objects of type T?
>>
>The other use is for the abstract type itself. Forgetting tagged
>types for a moment, I have never seen an interface/module that
>uses REFANY for this. Instead, the modules declare their abstract
>type as some proper subtype of REFANY, usually an opaque type.
>
>It would be possible to use just REFANY here of course, but that would
>require an otherwise unnecessary RT check on the allocated type,
>every time an operation of the module is called. This is a much
>more expensive check than a tag check, as has been pointed out
>in this discussion.
>
>But worse, it would sacrifice the static checking that we now have
>that prevents, at compile time, clients from passing, say a Text.T
>to some procedure in Atom that expects an Atom.T. If these modules
>ever wanted to use a tagged type, but the only tagged type were
>REFANY, we would lose this static checking, because Text.T would
>have to become equal to Atom.T.
>
>In cases where your algorithms don't specifically need dynamic
>typing, static typing is always much better, because one run of
>the compiler on the source code will do it. Bugs checked
>only at runtime require a massive test suit to be coded and then
>regularly rerun to get even close to the same confidence they've
>been found.
>
>So when using tagged types as the ADT itself,, we need to be able
>to have a tagged version of whatever proper subtype of REFANY
>the abstraction needs, including an opaque subtype of REFANY
>or an opaque subtype of some Public subtype of REFANY. This is
>why I am adamant that we need to be able to build a tagged type
>from any traced or object type.
>
>And once you do this, keeping REFANY itself unchanged and allowing
>TAGGED REFANY as one case of a tagged type falls out of the system
>for free _and_ saves a lot of cases that would otherwise need a new RT
>check that can never fail, but the language can't know that, because
>the type system has lost the distinction.
>
>I really feel that the fad of all dynamic typing in languages is a huge
>mistake and that it will someday be recognized as such. In the
>meantime, we have a language that provides a very extensive set
>of statically-typed alternatives, while also allowing dynamic typing
>when you really need it. This is one of Modula-3's best principles.
>Let's don't erode the static alternatives unnecessarily.
>
>> Mika
>>
>>
More information about the M3devel
mailing list