Discussion:
Variable parameters and code readability
(too old to reply)
August Karlstrom
2017-08-15 15:22:46 UTC
Permalink
I often find that procedure calls involving variable parameters can be
somewhat hard to read. For instance, in a call like

Matrices.Multiply(A, B, C)

it is not clear if A or C is is the result of the operation (it could
even be B). In this case I happen to know about the convention of
ordering formal parameters as input, input/output and output parameters
respectively. Therefor I would assume the result is C. Still, it's a bit
unsatisfactory. Are there any other best practices?


Regards,
August
Richard
2017-08-15 20:25:06 UTC
Permalink
Post by August Karlstrom
I often find that procedure calls involving variable parameters can be
somewhat hard to read. For instance, in a call like
Matrices.Multiply(A, B, C)
it is not clear if A or C is is the result of the operation (it could
You could include the information in the procedure name: e.g.:

Matrices.MultiplyABtoC (...)

Richard
Diego Sardina
2017-08-17 09:55:53 UTC
Permalink
Post by August Karlstrom
I often find that procedure calls involving variable parameters can be
somewhat hard to read. For instance, in a call like
Matrices.Multiply(A, B, C)
it is not clear if A or C is is the result of the operation (it could
even be B). In this case I happen to know about the convention of
ordering formal parameters as input, input/output and output parameters
respectively. Therefor I would assume the result is C. Still, it's a bit
unsatisfactory. Are there any other best practices?
One major advantage of block comments is that you can write descriptive comment between two syntax elements. Sometimes I find useful to write (* out *) in output variables.

Matrices.Multiply(A, B, (* out *) C);

I also use (* in/out *).

In general there are cases where it's important to know if a procedure is going to modify a variable.

This tecnique is also useful to achieve named parameters such as in Ada and Modula-3.

Strings.Copy((* src => *) str1, (* dst => *) str2);

Well, here doesn't look pretty nice but with a good editor that supports syntax coloring for Oberon, it looks nicer :-)


That said, since I was in mathematics/physics field, some programmers put a comment before the call to that function:

(* C = A x B *)
Matrices.Multiply(A, B, C);

This may also a good alternative.


I don't like the use of &.
1) It's error prone since it's easy to forget.
2) It makes readabily worse: f(a,b,&c).


--
Diego Sardina
August Karlstrom
2017-08-20 20:38:16 UTC
Permalink
Post by Diego Sardina
I don't like the use of &.
In this case I tend to disagree (I assume we are talking about C here).
Post by Diego Sardina
1) It's error prone since it's easy to forget.
If you forget it the compiler will tell you.
Post by Diego Sardina
2) It makes readabily worse: f(a,b,&c).
I think it enhances readability since it indicates that c simulates call
by reference.


-- August
Diego Sardina
2017-08-21 10:48:01 UTC
Permalink
Post by August Karlstrom
Post by Diego Sardina
2) It makes readabily worse: f(a,b,&c).
I think it enhances readability since it indicates that c simulates call
by reference.
I find & to be really invasive to the reader.

What we really miss is a good IDE for Oberon with a parser for semantic analysis, one could use a different color (blue) or font style (italic) for variable parameters, so it's more visible which ones are going to be modified.
Or just having procedure's signature moving the mouse's pointer onto it.


--
Diego Sardina
c***@gmail.com
2017-08-19 13:44:52 UTC
Permalink
Post by August Karlstrom
I often find that procedure calls involving variable parameters can be
somewhat hard to read. For instance, in a call like
Matrices.Multiply(A, B, C)
it is not clear if A or C is is the result of the operation (it could
even be B).
I know it doesn't answer your question in general, but if you were implementing your particular example in Oberon-2:

* Assume A and B are of a user-defined type 'Matrix'.
* Declare C as a POINTER TO Matrix.
* Implement Multiply as a function procedure which returns a POINTER TO Matrix as its result.

Then you could write:

C := Matrices.Multiply(A, B);

Regards,
Chris Burrows
CFB Software
http://www.astrobe.com
Diego Sardina
2017-08-19 18:54:38 UTC
Permalink
Post by c***@gmail.com
* Assume A and B are of a user-defined type 'Matrix'.
* Declare C as a POINTER TO Matrix.
* Implement Multiply as a function procedure which returns a POINTER TO Matrix as its result.
C := Matrices.Multiply(A, B);
In Component Pascal there is a very nice feature, it's not needed that C is of type POINTER TO Matrix, but just Matrix.

From the report:

"Dereferencing is also implied if a pointer is assigned to a variable of a record or array type [...]"

It means that the code below is valid.

MODULE Test;

TYPE
Matrix = ARRAY 3 OF REAL;

VAR
c: Matrix;

PROCEDURE F(): POINTER TO Matrix;
VAR r: POINTER TO Matrix;
BEGIN
NEW(r);
RETURN r
END F;

BEGIN
c := F()
END Test.


--
Diego Sardina
c***@gmail.com
2017-08-20 03:37:43 UTC
Permalink
Post by Diego Sardina
In Component Pascal there is a very nice feature, it's not needed that C is of type POINTER TO Matrix, but just Matrix.
"Dereferencing is also implied if a pointer is assigned to a variable of a record or array type [...]"
True. However, if you declare all of the matrix variables as pointers there is no need to do any explicit dereferencing in Oberon-2 either.

TYPE
MatrixArray = ARRAY 3, 3 OF INTEGER;
Matrix = POINTER TO MatrixArray;

VAR
a, b, c: Matrix;

PROCEDURE Multiply(a, b: Matrix): Matrix;
VAR
i, j: INTEGER;
m: Matrix;
BEGIN
NEW(m);
FOR i := 0 TO LEN(a) - 1 DO
FOR j := 0 TO LEN(a[0]) - 1 DO
m[i, j] := a[i, j] * b[i, j]
END
END;
RETURN m
END Multiply;
...
...
NEW(a);
NEW(b);
...
...
c := Multiply(a, b);

--
Chris Burrows
c***@gmail.com
2017-08-20 04:04:10 UTC
Permalink
Post by c***@gmail.com
True. However, if you declare all of the matrix variables as pointers there is no need to do any explicit dereferencing in Oberon-2 either.
P.S. Using pointers for all the matrices also has the advantage in that you can write generic code that handles 2-D arrays of any size e.g.

TYPE
MatrixArray = ARRAY OF ARRAY OF INTEGER;
Matrix = POINTER TO MatrixArray;

VAR
a, b, c: Matrix;

PROCEDURE Multiply(a, b: Matrix): Matrix;
VAR
i, j: LONGINT;
m: Matrix;
BEGIN
NEW(m, LEN(a, 0), LEN(a, 1));
FOR i := 0 TO LEN(a, 0) - 1 DO
FOR j := 0 TO LEN(a, 1) - 1 DO
m[i, j] := a[i, j] * b[i, j]
END
END;
RETURN m
END Multiply;
...
...
(* 3 x 3 matrices *)
NEW(a, 3, 3);
NEW(b, 3, 3);
...
...
c := Multiply(a, b);
...
(* 4 x 6 matrices *)
NEW(a, 4, 6);
NEW(b, 4, 6);
...
...
c := Multiply(a, b);
August Karlstrom
2017-08-20 20:59:39 UTC
Permalink
Post by c***@gmail.com
P.S. Using pointers for all the matrices also has the advantage in
that you can write generic code that handles 2-D arrays of any size
It's too bad this cannot be done in standard Oberon. Of course you can
use open (one-dimensional) arrays and calculate element indices manually
but then you also need to pass the number of columns for each matrix.


-- August
August Karlstrom
2018-07-30 15:20:40 UTC
Permalink
Post by August Karlstrom
Post by c***@gmail.com
P.S. Using pointers for all the matrices also has the advantage in
that you can write generic code that handles 2-D arrays of any size
It's too bad this cannot be done in standard Oberon. Of course you can
use open (one-dimensional) arrays and calculate element indices manually
but then you also need to pass the number of columns for each matrix.
I was wrong here; Oberon actually does support open arrays of any dimension.

-- August
c***@gmail.com
2018-07-31 11:33:57 UTC
Permalink
Post by August Karlstrom
Post by August Karlstrom
It's too bad this cannot be done in standard Oberon. Of course you can
use open (one-dimensional) arrays and calculate element indices manually
but then you also need to pass the number of columns for each matrix.
I was wrong here; Oberon actually does support open arrays of any dimension.
My first reaction was to disagree but I now see that you are right. The syntax does allow e.g. for a three-dimensional open array parameter:

PROCEDURE P(VAR a: ARRAY OF ARRAY OF ARRAY OF INTEGER);

My next question then was how would you know the length of each dimension but I suppose (?) it is just:

Dimension 0: LEN(a)
Dimension 1: LEN(a[0]);
Dimension 2: LEN(a[0, 0] or LEN(a[0][0])

Do you know of any current Oberon compilers that support open arrays that are not restricted to 1 dimension?

Regards,
Chris Burrows
CFB Software
http://www.astrobe.com
August Karlstrom
2018-07-31 12:29:57 UTC
Permalink
Post by c***@gmail.com
My first reaction was to disagree but I now see that you are right.
I also overlooked this fact despite having studied the Oberon grammar
many times.
Post by c***@gmail.com
PROCEDURE P(VAR a: ARRAY OF ARRAY OF ARRAY OF INTEGER);
Already with two dimensions the verbosity is excessive, though. The
alternative would have been to use a place holder like _ or * for the
lengths:

a: ARRAY _ OF INTEGER
A: ARRAY _, _ OF INTEGER

However, I can understand Niklaus Wirth's decision not to complicate
things for such a marginal case.
Post by c***@gmail.com
Dimension 0: LEN(a)
Dimension 1: LEN(a[0]);
Dimension 2: LEN(a[0, 0] or LEN(a[0][0])
Indeed.
Post by c***@gmail.com
Do you know of any current Oberon compilers that support open arrays that are not restricted to 1 dimension?
Yes, OBNC:

http://miasap.se/obnc/


-- August

August Karlstrom
2017-08-20 20:49:42 UTC
Permalink
Post by c***@gmail.com
Post by August Karlstrom
I often find that procedure calls involving variable parameters can be
somewhat hard to read. For instance, in a call like
Matrices.Multiply(A, B, C)
it is not clear if A or C is is the result of the operation (it could
even be B).
* Assume A and B are of a user-defined type 'Matrix'.
* Declare C as a POINTER TO Matrix.
* Implement Multiply as a function procedure which returns a POINTER TO Matrix as its result.
This will indeed make the procedure more pleasant to use (although a bit
slower).
Post by c***@gmail.com
C := Matrices.Multiply(A, B);
Here I would also rename the function procedure to Product.


-- August
Loading...