[M3devel] small objects]

Rodney M. Bates rodney.m.bates at cox.net
Mon Apr 13 23:51:31 CEST 2009




Mika Nystrom wrote:
> 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
>   

No, not a tagged hierarchy.  That was an earlier idea I toyed with
that I considered a tar pit.  The reference types remain in the
existing hierarchy, but the tagged types have no subtype
relation to each other. 

> 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.  
I brought up container data structures as one important
group, but they are not the universe of uses of REFANY.

As an example, I have a good sized module that builds a
tree--not a general tree determined by input, but exactly
one, hard-coded specific tree.  It's needed to boostrap the
general tree-building code.  The tree is rather large, and
hard coding all the node constructor calls was very tedious
and boring.  

I wrote some node constructor procedures with the intent of
minimizing the keystrokes in the calls on them.  One way I did
this was to make the child parameters have type  REFANY,
with meaningful values being either TEXT or previously-built
tree nodes of a few possible different types.  The constructors
then TYPECASE on them.  REFANY is the narrowest possible
type that can take this set of options. 

This has nothing to do with any abstract data type and is not
intended for any general-purpose use by various other code.
In fact, it has a very specific and limited purpose. 

> 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