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