[comp.lang.modula2] Make the language Perfect/SOS

USDGOG@VTVM1.BITNET (Greg Granger) (06/25/91)

Well after a short conversation today I'm back on the "warpath" trying
to 'better' (slight bias to my views here :-) the language.

First: ADD OPERATOR OVERLOADING.  Despite recent well written notes on
the subject (which I basically disagree with), operator overloading
would be a great benefit to the language.  This feature would not
require run-time checking or much compiler overhead, if implemented
correctly.  Consider adding unary and binary operator overloading,
via a preprocessor step (this is to support my claim of no run-time
checking and limited compiler overhead).
Example syntax: (normal preprocessor special id symbols omitted)
TYPE  UserType = REAL;
      OtherUserType = INTEGER;

PROCEDURE EquateMyTypes(x: OtherUserType): UserType;
BEGIN
   RETURN ConvertInteger2Real(x);
END EquateMyTypes;

UNARY OPERATOR UserType ':=' OtherUserType USES EquateMyTypes;

Calls such as
  userType:= otherUserType;
would then be converted to
  userType:= EquateMyTypes(otherUserType);

Binary operators can be handled in much the same way and could encompass
precedence.
Consider:
BINARY OPERATOR Type1 '+' Type2 Type3 USES AddTypes23 PRECEDENCE 10;
BINARY OPERATOR Type1 '+' Type3 Type2 USES AddTypes32 PRECEDENCE 10;
BINARY OPERATOR Type1 '*' Type2 Type3 USES MulTypes23 PRECEDENCE 20;
BINARY OPERATOR Type1 '*' Type3 Type2 USES MulTypes32 PRECEDENCE 20;
BINARY OPERATOR Type1 '**' Type2 Type3 USES RaiseTypes23 PRECEDENCE 30;
BINARY OPERATOR Type1 '**' Type3 Type2 USES RaiseTypes32 PRECEDENCE 30;

Or Perhaps just:

BINARY OPERATOR Type1 '+' Type2 Type3 USES AddTypes PRECEDENCE 10
COMMUTES; (* hope I got that right, long time out of math :-) *)
BINARY OPERATOR Type1 '*' Type2 Type3 USES MulTypes PRECEDENCE 20
COMMUTES;
BINARY OPERATOR Type1 '**' Type2 Type3 USES RaiseTypes PRECEDENCE 30
COMMUTES;

Let a, b, c be Type1, Type2 and Type3, respectively.
Then the expression
   a:= b + c * b ** c;
would be converted to
   a:= AddTypes(b, MulTypes(c, RaiseTypes(b, c)));

I see no reason that this couldn't be written as a preprocessor,
and/or built into the language/compiler.  Readability and abuse are
valid concerns, but not sufficient to discard consideration of such
a valuable feature.  (BTW, nowhere did I say I wanted to write such
a preprocessor ;-)
-----------------------

Second: MODIFY PARAMETER PASSING FOR GENERIC PARAMETERS.
This one has to be done at the compiler level.  Basically, add at
GENERIC parameter and LIST modifier to the formal parameters.
Consider a built-in language type GENERIC defined as follows:
GENERIC = RECORD
   type: AtomicTypes; (* Set of AtomicTypes *);
   variable: BOOLEAN;
   addr: ADDRESS;
   length: CARDINAL;
   next: POINTER to AnotherGenericRecord; (* Used to decompose array and
                                             record types *)
   END;

The above would likely need some 'tweaking' for such things as pointers
and perhaps some additional info for arrays.

The LIST modifier would allow a procedure to accept an arbitrary number
of parameters (in the form of a list).

So the procedure:
PROCEDURE Test(LIST OF GENERIC);
would accept anything given it (and still retain knowledge of it's type).
Likely this feature would have to restricted in certain ways to
guarantee that all type arbitration would be performed at compile time.

Lots of room for abuse with this feature and considerable overhead
would likely be associated with it's use, but it would give
considerable power to toolmakers, prototypes and OOP folks.
-----------------------

Pet Peeve: Consistency across systems.
I wish that INTEGERs and CARDINALs were defined by default to be 32bits,
basically I'd like to see all atomic types defined at the bit level.
Further (u didn't think I would stop there did u :-), I'd like to see
the ability to 'build a variable' at the bit level.
Example
   littleCard: BITFIELD RANGE 0..15; (* 4 bits *)
   bigCard: BITFIELD BITS 96; (* 96 bits *)
   largeInt: BITFIELD BITS 64 SIGNED; (* 64 bit signed number *)
Of course arrays of these should only take up the minimum bits needed
for storage.  This type and the ability to overload operators (as
outlined above) should provide a rather rich low-level type structure.
Overhead on this one would likely be pretty bad, but could be bypassed
by staying with default types.
-----------------------

Well that should be enough to firmly establish me on racial lunatic
outer fringe.

Oh, and before it's mentioned, yes I can spell Ada <grin>.

What'a think?

Greg

X903@DMAFHT1.BITNET (Marc Wachowitz) (06/26/91)

On Tue, 25 Jun 91 09:25:58 EDT Greg Granger said:
>correctly.  Consider adding unary and binary operator overloading,
>via a preprocessor step (this is to support my claim of no run-time
>checking and limited compiler overhead).
>Example syntax: (normal preprocessor special id symbols omitted)
Sorry, but I think this argument is rather nonsensical. When talking
about overhead (i.e. complexity or runtime cost), "compiler" is any-
thing you have to run to obtain the object file (or whatever you have
instead) from your source. It doesn't matter if this is separated into
several programs, or if one of them is called preprocessor.
>Binary operators can be handled in much the same way and could encompass
>precedence.
>Consider:
>BINARY OPERATOR Type1 '+' Type2 Type3 USES AddTypes23 PRECEDENCE 10;
>BINARY OPERATOR Type1 '+' Type3 Type2 USES AddTypes32 PRECEDENCE 10;
>BINARY OPERATOR Type1 '*' Type2 Type3 USES MulTypes23 PRECEDENCE 20;
>BINARY OPERATOR Type1 '*' Type3 Type2 USES MulTypes32 PRECEDENCE 20;
>BINARY OPERATOR Type1 '**' Type2 Type3 USES RaiseTypes23 PRECEDENCE 30;
>BINARY OPERATOR Type1 '**' Type3 Type2 USES RaiseTypes32 PRECEDENCE 30;
>
>Or Perhaps just:
>
>BINARY OPERATOR Type1 '+' Type2 Type3 USES AddTypes PRECEDENCE 10
>COMMUTES; (* hope I got that right, long time out of math :-) *)
>BINARY OPERATOR Type1 '*' Type2 Type3 USES MulTypes PRECEDENCE 20
>COMMUTES;
>BINARY OPERATOR Type1 '**' Type2 Type3 USES RaiseTypes PRECEDENCE 30
>COMMUTES;
(I hope you don't want to say that e.g. 2 ** 1 = 1 ** 2? :-)
The problem with this approach to precedence is that it requires a
global (i.e. across modules) knowledge of all operators that may be
used together at some later time somewhere - probably in ways the
original author never dreamed of. That's my most serious concern
against this "feature": I consider it a strong attack against reuse
and maintenance of large systems. All those "nice features" look
quite innocent when you are doing everything alone, but may cause
horror to someone who has to maintain code written by many others
(and that's what we all want to be feasable, isn't it?).
>and/or built into the language/compiler.  Readability and abuse are
>valid concerns, but not sufficient to discard consideration of such
>a valuable feature.  (BTW, nowhere did I say I wanted to write such
Well, that's where our opinions differ - I consider readability much
more important than ease of writing (this does not imply that I liked
Ada, though I appreciate that readablity was one of its main design
goals).
>Second: MODIFY PARAMETER PASSING FOR GENERIC PARAMETERS.
Perhaps have a look at Modula-3, it may have solved many of the problems
you are addressing. Treat all those "generic" types as pointers. Then
in Modula-3 you can declare the "generic" variables as "REFANY", and
assign to it every (traced, for those who know it) pointer. It is
possible to test the actual type, since every pointer type has a unique
typecode associated with it. Of course, objects are the better solution
in most of those cases. Modula-3 does also offer several additional
features which can be used to write generic code, look into the report
if you want to know more about it.
Variable-length argument list can the just be implemented as open arrays
of REFANY/objects etc., particularly as you have very flexible means to
denote constructors for complicated types).
>Pet Peeve: Consistency across systems.
>I wish that INTEGERs and CARDINALs were defined by default to be 32bits,
>basically I'd like to see all atomic types defined at the bit level.
>Further (u didn't think I would stop there did u :-), I'd like to see
>the ability to 'build a variable' at the bit level.
>Example
>   littleCard: BITFIELD RANGE 0..15; (* 4 bits *)
>   bigCard: BITFIELD BITS 96; (* 96 bits *)
>   largeInt: BITFIELD BITS 64 SIGNED; (* 64 bit signed number *)
Again Modula-3 has (is?) the solution to this problem. There is just
one type INTEGER, which can be expected to be the largest reasonable
signed whole number type for a given implementation - though this need
not be 32 bits on every architecture. Then you have subrange types
(CARDINAL is just the non-negative subset of INTEGER, thus eliminating
all those nasty problems when signed meets unsigned).
If you need a low-level mapping, there are packed types, of the form
  BITS NumberOfBits FOR BaseType
where BaseType can be any type, not only numeric types. Thus you can
just state what your precise requirements are for a given problem.
Of course, not every possible size will be possible on all architectures,
but the inablitiy of an implementation to give you what you requested is
a static (i.e. usually compile-time) error.

The report on Modula-3 and the implementation (as far as I know it's yet
the only one) for various U*X-systems is available by anonymous ftp on
gatekeeper.dec.com, directory /pub/DEC/Modula-3.

Marc Wachowitz
X903@DMAFHT1.BITNET

UNBCIC@BRFAPESP.BITNET (06/28/91)

=> Sorry, but I think this argument is rather nonsensical. When talking
=> about overhead (i.e. complexity or runtime cost), "compiler" is any-
=> thing you have to run to obtain the object file (or whatever you have
=> instead) from your source. It doesn't matter if this is separated into
=> several programs, or if one of them is called preprocessor.

A preprocessor NEVER adds runtime overhead. It is a COMPILEtime overhead.


=> The problem with this approach to precedence is that it requires a
=> global (i.e. across modules) knowledge of all operators that may be
=> used together at some later time somewhere - probably in ways the
=> original author never dreamed of. That's my most serious concern

I don't think that's true because the proposed scheme *IS* used in Prolog with
success.

=> Marc Wachowitz
=> X903@DMAFHT1.BITNET

                              (8-DCS)
Daniel C. Sobral                           Errare Humanum Est...
UNBCIC@BRFAPESP.BITNET                     ...Perseverare Autem Diabolicum
UNBCIC@FPSP.FAPESP.ANSP.BR
--------------------------------------------------------------------------
No one, but me, is responsible for the above message.

X903@DMAFHT1.BITNET (Marc Wachowitz) (06/28/91)

On Thu, 27 Jun 91 15:07:00 -0300 <UNBCIC@BRFAPESP> said:
>
>=> Sorry, but I think this argument is rather nonsensical. When talking
>=> about overhead (i.e. complexity or runtime cost), "compiler" is any-
>=> thing you have to run to obtain the object file (or whatever you have
>=> instead) from your source. It doesn't matter if this is separated into
>=> several programs, or if one of them is called preprocessor.
>
>A preprocessor NEVER adds runtime overhead. It is a COMPILEtime overhead.
Of course :-) I'm talking of the runtime of the translation process.
Sorry that I haven't made that clear enough the first time.
...
>I don't think that's true because the proposed scheme *IS* used in Prolog with
>success.
I know that it is done in Prolog. Note that I never tried to say it were
impossible to implement or anything like that. What constitutes a
success here is not proven by giving an existing implementation; one
cannot derive what is desired from what exists (though one should take
current practice into account). This would be as saying in ethics
that it's quite okay to kill other people, since it is not so uncommon -
I guess you would hardly follow this kind of argumentation.
If one views something as desirable depends largely on personal taste
and experience (not more vs. less but the particular kind of experience
one has). Btw, prolog has - in my personal and limited view, of course -
major problems exactly in this area: large projects with hundreds
of modules written by many people over a long period of time and
designed to be reusable for different problems.

Marc Wachowitz
X903@DMAFHT1.BITNET