[M3devel] small array in modula-3?

Darko darko at darko.org
Fri Nov 16 05:29:06 CET 2007


Not sure what the problem is with modules. If you want to avoid the  
dot you can name the type something unique then import that type  
name. You can create an interface with all the nice type names and  
import all those names in modules by adding an EXPORTS clause too.

Personally I think M3, with a couple of extensions, wouldn't need  
unsafe anything. The unsafe features are there mostly because of  
legacy interfaces. I forget the name but the folk who wrote an OS in  
M3 found it very effective with only couple of minor points with  
regards to interfacing to C code they built on top of, I think one of  
them was being able to pass NIL to a VAR parameter, which is a common  
C idiom (and an annoying one for M3 users who then can't use VAR).


On 15/11/2007, at 8:08 PM, Jay wrote:

> Rodney, thank you, this is interesting.
> I definitely don't want to pay for heap allocation for a tiny array.
> I don't know about "subarray". It looks like it takes and returns  
> arrays. It's not a type. I ordered another copy of the Nelson book  
> so I don't have to find mine.
>
> Good idea "MakeT" I forgot about that pattern. I'll try it out.
>
> The record and array I propose are not all that different really.
> I just heard of something new. Have you heard of it? "Duck typing".  
> If it acts/quacks/walks like a duck, it is a duck.
> This is in very dynamic languages like Ruby.
> You apply object.method and if object defines something named  
> "method", then it "must" have "the" meaning you intend.
> Kind of like how C++ templates "accept as parameters whatever  
> happens to work".
>
> Operator overloading is great for strings and "math".
>
> Maybe I should try to have this language debate from scratch again..?
>
> I still am conflcted.
>
> Modules seem overly heavyweight.
> I don't want Module1.T, Module2.T, I want T1, T2.
>
> C++ stack structs with non virtual member functions:
> class Rect_t
> {
>   Rect_t() : top(0), left(0), right(0), bottom(0) { }
>
>    int Height() { return bottom - top; }
>    int Width() { return right - left; }
>    int top, left, bottom, right;
> };
>
> seems "good".
>
> And it seems not really all that complicated for the compiler to  
> flow the static type information around to resolve the functions..
>
> Rect_t r;
> r.Height() => Rect_Height(&r); ..and would be inlined anyway.
>
> I do find some compelling features in Modula-3. Mainly that it  
> compiles to native code and has OPTIONAL safety, optional garbage  
> collection, and that the syntax of modules/interfaces allows fast  
> compilation -- no longer reparsing the same headers over and over  
> and over and over.
>
> Operator overloading btw..have seen "template SafeInt"? It acts  
> like a primitive integer, but raises exceptions upon overflow.
> For this to work very much requires operator overloading.
>
> A point, of course, is to be able to have user defined types that  
> can act like the built in types..
>
>  - Jay
>
>
> > Date: Thu, 15 Nov 2007 18:39:18 -0600
> > From: rodney.bates at wichita.edu
> > To: m3devel at elegosoft.com
> > Subject: Re: [M3devel] small array in modula-3?
> >
> > Jay wrote:
> >
> > > What is the right way to have a variably sized but always small  
> array in Modula-3?
> > > My array will only ever have 1 or 2 elements.
> > > I'd like to always allocate room for 2 elements, and have there  
> be a size.
> > >
> > > It seems I have a choice of
> > >
> > > a) an "open" array
> >
> > Heap allocated, I presume? If the max size is only 2 elements,
> > this is pretty extravagant, as a heap allocated open array will
> > have 4 extra behind-the-scenes words of space overhead, plus
> > maybe heap fragmentation, and time overhead of allocation,
> > collection, and maybe reduced locality of reference.
> >
> > > b) wrap it up in a record
> > >
> > > I'd like so have, like:
> > >
> > > TYPE A = ARRAY [0..1] OF FOO;
> > >
> > > And be able to say:
> > >
> > > VAR
> > > a : A;
> > >
> > > ..
> > > a.size
> > > FOR i := 0 TO a.size DO
> > > do something with a[i]
> > >
> > > It seems I have no option but, like:
> > >
> > > TYPE A = RECORD
> > > a: ARRAY[0..1] OF FOO;
> > > size := 1; (* usually of size 1, sometimes 2 *)
> > > END
> > >
> > > and then
> > > FOR i := 0 TO a.size DO
> > > do something with a.a[i];
> > >
> > > That is "ok". Not great -- what to call the inner a?
> > > But then furthermore, I'd like to construct constants.
> > > It seems I am stuck with either the verbose:
> > >
> > > PROCEDURE F(a:A);
> > >
> > > F( A { ARRAY[0..1] OF FOO { .. } );
> > >
> > > OR I have to come up with a name for the embedded array.
> > > But of course, its type is really "the same" as the outer type.
> >
> >
> > Ignoring the fact that the types are different linguistically
> > (one a record type, the other an array), the types are different
> > at a deep semantic level too. The inner array has static
> > size, the outer one (call it a "variable array", perhaps)
> > has dynamically changeable size, even more easily changed
> > after it is created than a heap-allocated open array. This
> > is a significant semantic difference, so it also makes program
> > design sense to view them as different types.
> >
> > >
> > > To wit:
> > >
> > > TYPE FooArray = ARRAY[0..1] OF Foo;
> > >
> > > TYPE FooArray = RECORD
> > > a : FooArray;
> > > size := 1;
> > > END;
> > >
> > > F( FooArray { FooArray { .. } );
> > >
> > > But I have to come up with different names.
> > >
> > > And I don't think I can leave out the name for the constructor,  
> like:
> > >
> > > F( FooArray { { .. } );
> >
> >
> > Yes, this is a limitation in Modula-3. Ada allows value constructors
> > to omit inner type names in cases like this, and it is  
> convenient. But
> > it also crosses a really major line on complexity of the language  
> semantics,
> > since type analysis information now flows not only upward, but  
> also downward
> > in expression typing. I am mostly content to live with this as  
> one bit of
> > the price of avoiding a horribly over complex language.
> >
> > Ordinary procedures can do pretty much all of the things done by  
> exotic
> > and complex language stuff like C++ constructors. If you don't  
> want to write
> > the ponderous nested value constructor, write a constructor  
> procedure and
> > call it. As Antony suggested, you can make it accept an open  
> array formal,
> > which then codes just like a simple array value constructor. You  
> could even
> > make it elaborate and general, e.g.:
> >
> > PROCEDURE MakeF ( Val : ARRAY OF FOO ) : F
> >
> > = VAR LSize : CARDINAL
> > ; VAR LResult : A
> >
> > ; BEGIN
> > LSize := NUMBER ( Val )
> > <* ASSERT LSize <= NUMBER ( FooArray ) *>
> > ; LResult . size := LSize
> > ; SUBARRAY ( LResult . a , 0 , LSize )
> > := SUBARRAY ( Val , FIRST ( Val ) , LSize )
> > ; RETURN LResult
> > END MakeF
> >
> > Alternatively, since your maximum element count is so small, you  
> could
> > write a constructor procedure that took two formals of type FOO,  
> with
> > at least the second one optional. (This would require a  
> distinguished
> > value of type FOO that would be used to mean "omitted".)
> >
> > And, of course, if you want to use abstraction, you can put the  
> types,
> > constructor procedures, and various other procedures for  
> manipulating
> > the variable sized array in a module, behind an interface.
> >
> > An additional limitation of Modula-3 in this regard, is that you  
> can't
> > make the type F opaque, unless you heap allocate it, which we  
> were trying
> > to avoid. Ada would allow you to do this, but it's another  
> example of
> > a convenience with high cost. It forces the equivalent of the  
> revelation
> > to be located in the equivalent of the interface, but makes it  
> illegal
> > to write client code that depends on what the revelation is.
> >
> > This in turn creates a source code control nightmare in a large  
> project,
> > because now someone who wants to make what is really a purely  
> internal,
> > implementation change, nevertheless has to check out the  
> interface, and
> > if you aren't in denial mode, that means implementers of all the  
> client
> > code have to go to extra trouble to somehow find out that what  
> appears
> > to be an interface change actually doesn't affect them after all.
> >
> > >
> > > Though that might seem nice.
> > >
> > > Am I understanding everything?
> > >
> > > Have folks hit this before and there's a set of names that  
> don't seem too lame
> > > that folks use?
> > >
> > > Also -- language documentation?
> > > Nelson's green book is excellent.
> > > The stuff in the doc directory is very dry and scientific.
> > > The tutorial seems more like a reference. Or maybe I didn't  
> read it enough.
> > > Is there better, in case I need a refresher?
> > > I think I got it, via Nelson's book from memory (wonder if I  
> can find mine..) and the reference,
> > > but I don't think anyone could learn from the current online  
> docs in the source tree.
> > >
> > > Maybe it's me, some combination of laziness and short attention  
> span
> > > as I age..
> > >
> > > Btw, C doesn't offer a great solution here, though it offers  
> leaving out some type names.
> > > In C++ I could have both .size and operator[].
> > > Modula-3 seems to be missing operator overloading.
> > > It's got some builtin stuff, even array assignment and equality  
> and I think
> > > even record assignment and equality, but it is still a bit  
> limiting.
> >
> >
> > I have raved on this before, but, having had lots of painful  
> experience with
> > user-defined overloading in Ada and C++, I can say with  
> authority, that it is
> > a programming language disaster. It interacts with just about  
> everything
> > else in the language, in very complicated ways, and all it buys  
> you is saving
> > time/energy thinking up distinct names for procedures/functions.  
> Even this,
> > aside from the effects on the language, is a net loss, by the  
> time you consider
> > readability along with writability.
> >
> > User-defined overloaded operators provide a slight readability  
> benefit, _if_
> > used with great restraint and discipline, something you can rely  
> on not happening.
> > Meanwhile, the language complexity can easily double or worse.  
> And, with the
> > exception of compiler writers and language lawyers who spend  
> hundreds of hours
> > on just this, programmers don't understand the rules, not even  
> close.
> >
> > >
> > > Also, if I understand things correctly, this has long bothered  
> me about Modula-3,
> > > though I'm more tolerant now -- Modula-3 doesn't seem to allow  
> for lighter wieght
> > > objects. Like, small stack allocated structs with member  
> functions. You seem to have
> > > to chose between heap allocated garbage collected virtual  
> member functions full
> > > featured objects, or featureless dumb structs. It's nice how C+ 
> + allow hybrids --
> > > objects don't have to be heap allocated and member functions  
> don't have be virtual.
> > > Or am I missing something?
> >
> >
> > C++'s supposedly lighter weight forms of classes/structs with  
> their special
> > member functions buy nothing that plain records, plain  
> procedures, and
> > interfaces/modules don't already provide, again, at significant  
> and gratuitous
> > language complexity. Except when methods/member functions  
> actually dispatch
> > dynamically, there is nothing that the above won't do, and when  
> you create a
> > class instance as a local variable (i.e., on the stack), it is  
> necessarily
> > not polymorphic, that is, it can't change its "allocated" or  
> dynamic type
> > among various subtypes at runtime. This in turn means it can't  
> dispatch.
> >
> > So, just use plain procedures, an interface and a module, and you  
> will get
> > everything a lighter-weight C++ class would give you.
> >
> >
> > >
> > > Anyway, I've gotten to accept C a bit more vs. C++ so I can  
> deal with
> > > t: Type;
> > > Type_DoSomething(t);
> > >
> > > in place of:
> > > t.DoSomething();
> >
> >
> > Exactly. The special "receiver object" in a true method call has  
> significant
> > semantic differences from an ordinary parameter and introduces  
> new complexities
> > and non-orthogonalities. It has some very valuable uses too.
> >
> > But to then create degenerate forms of it that still carry a lot  
> of these
> > complexities, but are equivalent in programing power to plain old
> > parameters is just bad program design and bad language design.  
> Objects and
> > methods are indeed cool for situations that utilize their  
> semantic complexity.
> > But it's deeply uncool to try to look superficially cool by using an
> > inappropriately sophisticated construct when the problem doesn't  
> justify it.
> > Some OO proponents have gone way over the deep end here.
> >
> > >
> > > - Jay
> > >
> > >
> > >  
> ---------------------------------------------------------------------- 
> --
> > > Boo! Scare away worms, viruses and so much more! Try Windows  
> Live OneCare! Try now!
> > <http://onecare.live.com/standard/en-us/purchase/trial.aspx? 
> s_cid=wl_hotmailnews>
> >
> >
> > --
> > -------------------------------------------------------------
> > Rodney M. Bates, retired assistant professor
> > Dept. of Computer Science, Wichita State University
> > Wichita, KS 67260-0083
> > 316-978-3922
> > rodney.bates at wichita.edu
> >
> > --
> > -------------------------------------------------------------
> > Rodney M. Bates, retired assistant professor
> > Dept. of Computer Science, Wichita State University
> > Wichita, KS 67260-0083
> > 316-978-3922
> > rodney.bates at wichita.edu
>
>
> Boo! Scare away worms, viruses and so much more! Try Windows Live  
> OneCare! Try now!




More information about the M3devel mailing list