These are my proposals for Pascal
but some ideas are applicable for other programming languages as well.
I had most of the proposals posted in a talk of Modernized Pascal and/or in the development forum of Free Pascal.
Comments or questions? E-mail: email@example.com
In many cases it makes sense to allow an additional trailing separator as well as allowing for empty lists, especially when dealing with conditional compilation. As an additional benefit it makes it easier to move around lines of code.
Here are some (more or less stupid) examples:
// no hassle with the last comma, empty uses (if neither x1 nor x2 defined) uses (*$ifdef a *) unit_a, (*$endif *) (*$ifdef b *) unit_b, (*$endif *) ;
// empty case statement (if neither x1 nor x2 defined) case expr of (*$ifdef x1 *) 1: write('x1'); (*$endif *) (*$ifdef x2 *) 2: write('x2'); (*$endif *) end;
// no hassle with the last semicolon: extensible parameters procedure f( const s: string; x,y: integer; );
// no hassle with the last comma: extensible sets / arrays function_call([ 1, 2, ]);
// no hassle with the last comma: extensible parameters writeln( var_1, var_2, );
// empty type list (if neither x1 nor x2 defined) // in some cases types must be in one block type (*$ifdef x1 *) t1=integer; (*$endif *) (*$ifdef x2 *) t2=integer; (*$endif *)
There should be no problems concerning compatibility and implementation.
What about better readable numbers such as 100_000 or $_1234_5678 (note the "_" signs)?
A number still should be started with "$" or a digit. All occurring "_" shall simply be accepted and ignored.
A base should be specifiable as well, e.g.
These ideas are stolen from e.g. Ada.
There should be no problems concerning compatibility and implementation.
The string-case currently realized in Free Pascal allows for ranges which is nice but in reality most often not what is effectively needed. Typically what I need instead is a matching of string beginnings. A more or less stupid idea for the syntax could be:
case str_expr of 'match'.. :; // match all strings beginning with 'match' end;
However probably the parser needs to be changed to accept the missing second argument of "..".
See also speeding up case of string.
What about arrays with an unspecified upper bound such as this?:
type ta_byte = array [0..] of byte;
No bounds checking should occur on such array accesses. The size of such type should be 0.
I would like to propose to enhance the case statement for floats. What is needed then is the ability to test for open ranges and half open ranges.
case float_expr of <2 : ; // match all below 2 >=2 .. <4: ; // match 2<=x<4 4 : ; // match 4 >4 .. <=6: ; // match 4<x<=6 8 .. 9 : ; // match 8<=x<=9 >9 .. 10 : ; // match 9<x<=10 end;
In ranges the left expression should be less than the right one and therefore the first relational operator should be ">" or ">=" and the second one "<" or "<="; if the operators are absent ">=" resp. "<=" should be assumed.
In Visual Basic .NET an "is" is added before the relational operator.
An alternative syntax idea could be ranges like 4<..<=6.
I often would like to explicitly discard a function result such as
The usual workaround is
my_func(x); // using "extended syntax"
dummy := my_func(x); // dummy is not used, this gives a hint
Both workarounds are not the best way to express the intention. There should be a way to allow for the new syntax and at the same time provide a hint/warning for the workaround with the extended syntax.
There should be no problems concerning compatibility (extra switch) and implementation.
May I propose multiple assignments, e.g.
a,b := 0;
a,b += c+d;
The right hand expression should be evaluated once and assigned
(independently and in no particular order) to all the entries
to the left of :=.
The operator += is a C operator copied by Free Pascal. In Delphi you would write inc(v,x).
I would like to propose an enhanced replacement for the special assignment operators +=, -=, *= and /=:
More Pascal-like would be constructs of the form "operator :=" such as
a + := 1; a div := 3; a or := b; a xor := 3; a shl := 1;
Together with my prior proposal of multiple assignments there could be stuff like below as well:
a,b + := c+d; a,b shl := 1;
I am not sure as how to further enhance this to monadic operators such as "-" and "not".
The C operators can be thought of a composition of a binary operator ("+", "-", "*", "/") and the C assignment operator "=", so my solution should be the natural pendant in Pascal.
My proposal is to replace the operators "op="
by one operator "op" and a
This can be further enhanced to work together with my other proposal for
a + := 1; a + := pi; // float a + := 'bla'; // string a + := [1,2]; // set a < := b; // booleans, this is ugly but should be OK (but not with C-style operators), see PS a div := 3; a or := b; a xor := 3; a shl := 1; f()^ + := 1; // f might have side-effects! a[i+j] * := 2; // @a[i+j] only evaluated once a,b + := c+d; // c+d only evaluated once a,b shl := 1; a^.b[f(i)], c^.d[g(j)] + := h(k); // f,g,h might have side-effects, stuff evaluated only once
I cannot think of a more readable and more Pascal-like solution. For me the C-like pendants are even less readable and appear to me as a too limited quick hack. The Delphi-style replacements such as inc/dec/include/exclude are even less usable since both assignment and the operation itself is no longer visible and to wordy. A new pseudo-procedure for every such operator is for sure not the best idea.
There are some reasons for my solution:
I object to the existing Free Pascal operators "+=", "-=", "/=", "*=" because:
Borland's alternative procedure notation with inc, dec, include, exclude is not better since they introduce a flood of global names and hide the operator nature.
The main reason for the proposal is not saving keystrokes but functionality and speed. It is meant to be an optimizing aid to the compiler being able to omit the common expression optimizer in this case without loss and to avoid side-effects. All parts of the statement should be evaluated once. Saving keystrokes and avoiding to write things twice are welcome side-effects.
This has also been the reason for including these: inc, dec, include, exclude, "+=", "-=", "*=", "/=".
I have often missed e.g. "* :=" and "shl :=".
Semantic of "a op := expr":
p := @a; // only once! p^ := p^ op expr;
Semantic of "obj.prop op := expr":
p := @obj; // address of object, only once! p^.prop := p^.prop op expr; // access property
Semantic of "a,b := expr":
tmp := expr; // only once! a := tmp; b := tmp;
Semantic of "a,b op := expr":
tmp := expr; // only once! p := @a; // only once! p^ := p^ op tmp; p := @b; // only once! p^ := p^ op tmp;
BTW: "a[random(2)] + := 1" is not equivalent to "a[random(2)] := a[random(2)] + 1" due to the side-effect of random()!
Let me propose some kinds of type attributes which are handled by the compiler. They mostly have no consequences to the generated code but can catch several common programming bugs. I am not really sure about the syntax.
CONST km = 1000 * m; mm = 0.001 * m; inch = 25.4 * mm; N = kg * m/(s*s); // Newton C = A*s; // Coulomb minute = 60 * s; speed_light = 2.99792458e+8 * m/s; // aka c earth_standard_velocity = 9.80665 * m/(s*s);To avoid confusion at least these constants should be defined in separate modules or other name spaces in order to be able to qualify the constants with the name space, e.g. dimensions.m or physics.c.
Here are some other ideas, not fully specified and only sketched here. Keywords are displayed in uppercase letters.
FOR VAR i: type := … FOR VAR i := … // implicit type
FOR REPEAT count DO …
FOR VAR i := start REPEAT count DO // start..start+count-1 … FOR VAR i := start DOWNTO REPEAT count DO // start..start-count+1 …
CONST s = [1,2]; … CASE e OF s: …or
CASE e OF [1,2]: …instead of
CASE e OF 1,2: …
LOCAL declaration BEGIN … ENDAn alternative keyword could be BLOCK or DECLARE. The local declarations shall only be visible inside LOCAL…END.
WITH v: expr DO …This form can (and IMHO should) replace the classical unnamed form. The compiler should optionally output a hint or warning when using unnamed WITH.
TYPE tr_rec = RECORD a,b: integer; END; VAR v: tr_rec; o: OFFSET tr_rec OF integer; BEGIN o := OFFSET(tr_rec,a); v.[o] := v.b; … END.An alternative syntax for "OFFSET(tr_rec,a)" could be "tr_rec @ a" thereby overloading the address operator.
TYPE tp_int = ^integer; ta_8u = ARRAY [0..maxint] OF byte; tpa_8u = ^ta_8u; tpr_rec = ^tr_rec; VAR v: tr_rec; o: integer; BEGIN o := integer(@tpr_rec(nil)^.a); // o := OFFSET(tr_rec,a); tp_int(@tpa_8u(@v)^[o])^ := v.b; // v.[o] := v.b; … END.As you see, there are some nasty things necessary for the emulation: We assume that nil is zero and we use a cast from pointer to integer, and there are some more castings. An alternative approach using "extended syntax" (pointer arithmetic) is a little better. My proposal however is clean and type-safe and does not need any castings.
CASE rel_expr OF <: ... =: ... >: ... END;or, similar to FORTRAN's "three-way if", we could write in Pascal the expression if(rel_expr,less_expr,equal_expr,greater_expr); evidently this syntax could be applied to boolean values as well such as if(bool_expr,true_expr,false_expr) (comparable to C's ternary ?: operator). Some other languages such as Perl, PHP (since version 7), Ruby and Groovy support such a three-way comparison operator featuring the "spaceship-operator" <=>. As an extension such expressions might be chainable in a short-circuit way such that the first relation yielding less or greater determines the result prematurely.