Post by Martin Brown
Modula's requirement for strict type safety precluded interpretted
format string typical of FORTRANs FORMAT statement or C's printf().
The trouble with format strings is generally that their designers all too often do not differentiate between formatting and typing. Separate the two and you can have type safe format strings.
That is to say, format strings can be save as long as the format string does not determine which type the output data shall be. IOW, the format string only determines formatting, not what type the data is in.
Post by Martin Brown
It was ahead of it's time, but at the time it didn't seem like that in the
real world. PASCALs Write & WriteLn were more flexible in practice.
The plethora of different IO procedures with different names depending on the type they are for is in fact very bad design in terms of readability of source code. It's absolutely horrible.
At the time it was perceived that you can either have nice IO syntax like Pascal or you can have library defined IO, but not both. In Pascal there are polymorphic Read and Write procedures but they only accept built-in types and the IO system is built into the language, not library defined.
Unfortunately Wirth did not realise that he had already invented and used a technique in Modula-2 that would have let him have his cake and eat it too, that is unified READ and WRITE with library defined IO.
The technique in question can be found in NEW and DISPOSE. These are built-in identifiers, but their implementation comes from a library called Storage. NEW maps to Storage.ALLOCATE, DISPOSE maps to Storage.DEALLOCATE. The technique is therefore called a Wirthian Macro.
This could have been used for IO by defining built-in identifiers READ and WRITE and map them to respective library implementations, one IO library per type.
Given the variables ...
VAR n : CARDINAL; i : INTEGER; r : REAL;
... the compiler would then map ...
WRITE(n) => CARDINALIO.write(n)
WRITE(i) => INTEGERIO.write(i)
WRITE(r) => REALIO.write(r)
This would also extend to library defined types, not just built-in types.
As for formatted IO, a third identifier WRITEF could be provided.
WRITEF( channel, "format string", list of variables to be output );
The variables to be output would all need to be of the same type.
Unfortunately, to provide library implementations, variadic parameter lists are required.
This is another feature that was once thought to be impossible to implement in a type safe manner. Nothing could be further from the truth however ...
PROCEDURE writef ( f : FILE; fmt : ARRAY OF CHAR; list : ARGLIST OF <Type> );
In fact, such polymorphic READ, WRITE and WRITEF procedures and type safe variadic parameters are both an integral part of Modula-2 R10.
User convenience and implementation flexibility and efficiency.
So, yes, you can have your cake and eat it too.