How the first Pascal compiler was written
raw.github.com
raw.github.com
I suspect that the good programmers did notice the benefits of a strict statically typed language, however the cost was probably deemed to be too high at the time. Because C was a "mid-level language" and stayed close to the metal it could efficiently use the limited resources of pre-LSI (let alone VLSI) computers; effectively C allowed you to break the rules if you wanted or needed to. These days obviously the performance difference is generally less than a few percent and hence can be largely ignored.
* Lack of a complete standard. The language was initially used in academy, so when it was adopted by industry, numerous mutually incompatible extensions were developed. The standard C library today may seem basic, but it was nice in comparison.
* Virtual machines. Now they're the cool thing. Then they were a shortcut to have a quick and dirty implementation for new hardware, instead of writting a compiler. Bad performance was associated with the language, not the implementation.
* Some implementations were of the "bondage and discipline" kind.
TurboPascal compiler in the eighties solved all three problems, adding an IDE and a $50 tag for good measure. But it was too late. Although very popular in Europe and for little shops in general, the big software firms in the USA had adopted C already.
From the PDF:
"The clean solution given in Oberon [6] is the concept of type extension, in object-oriented languages called inheritance. Now it became possible to declare a pointer as referencing a given type, and it would be able to point to any type which was an extension of the given type. This made it possible to construct inhomogeneous data structures, and to use them with the security of a reliable type checking system. An implementation must check at run-time, if and only if it is not possible to check at compile-time,.
Programs expressed in languages of the 1960s were full of loopholes. They made these programs utterly error-prone. But there was no alternative. The fact that a language like Oberon lets you program entire systems from scratch without use of loopholes (except in the storage manager and device drivers) marks the most significant progress in language design over 40 years."
I remember reading somewhere that Pascal calling convention was more efficient. Return from a function and cleaning the parameters from the stack could be done with a single RET N instruction, while C calls needed both a RET and a separate stack realignment, because C allows variable number of params.
I don't know if it's still the case with recent processors.
The point is moot though, because just because an operation can be represented using a single assembler mnemonic doesn't mean that it's any faster than an alternative that uses several. A case in point is the "REP MOVS" style string functions, that until very recently were actually slower than opencoding the equivalents, since they trapped to microcode.
I think we would be using safer systems where buffer overruns and pointer exploits would be almost non existent.
The hegemony of C has cost the industry millions of euros/dollars in software correctness.
Every Pascal I ever worked with (with the exception of the Pascal I used in school) had extensions to make it work in the Real World. Unextended Pascal was a toy.
Apple was a Pascal shop in the 80s, but they extended it to the point that I just translated (in my head) the C code that I wanted to write into the variant of Pascal they'd implemented. Pascal had a couple of nice things that C didn't (nested procedures, for one), but they weren't used all that much by us.
Strings were still a pain. Str255 stunk, and everyone knew it, but nobody had anything that was dramatically better.
I don't mean to offend. If I'm unaware of major pieces of software done in Modula-N or Oberon, then feel free to correct me.
I only know of some teaching-level and research OS work, and some GUI work, but little in the way of commercial stuff.
I also found it surprising that he disliked Dijkstra's display idea since I've used a variant of that to great effect.
Anyway, there's a lot of fun reading in there so thanks for the link! Here's a discussion on Lambda the Ultimate: http://lambda-the-ultimate.org/node/1773
I guess I've never actually bootstrapped a compiler, but I've always found the interpreters I've written to be easier to reason about than the compilers I've written.
So what you say is true--it is a good way to see how the language feels--but not relevant to the facet of language development I was musing about.
Don't discard unsexy solutions out of hand; choose the right tool for the job.
It might take awhile, but it doesn't seem like it would be as hard to macro up some higher-level routines for parsing and code-generating a simple PASCAL.
I'd expect that most Beta-machines (nowadays virtual machines) would have been written in assembler back then, including lots of expertise running compiled Algol amongst other things.
Also, consider the stated goal of the project. They wanted to write a throwaway compiler so that they could bootstrap a Pascal version. In this case, it's a bad choice to use a language (Fortran) that's so different from Pascal. On the other hand, assembly is very flexible, so you can use whatever idioms you think will apply in the Pascal code you later intend to write. This will make the translation much simpler.
In Pascal, a function can be passed as an argument to another function, but cannot be stored in a variable or data structure, cannot be returned from a function, and cannot be created without being given a name. However, when a function is passed to another function and later called, it will execute in the lexical context it was defined in, so it is, in some sense, "closed over" that context.
I also liked the I can pass parameters per value or reference without explicitly using pointers.
References are cool. That makes one point for Pascal.
Subranges are also a nice feature.
Those, and nested procedures are about all I can come up with.
Furthermore, C itself evolved rather substantially over the years, so people who mostly experienced the ANSI/ISO C era may not realize what an appallingly messy language it used to be. By the time the original K&R got published, the situation had improved considerably (with the exception of function prototypes), but I found the C code in 6th edition UNIX a truly hair raising read: http://www.tom-yam.or.jp/2238/src/
type
TFoo = (foOne, foTwo, foThree);
const
FooStr: array[TFoo] of string = ('One', 'Two', 'Three');
There's no mucking around with implicit or explicit conversions between integers and enumerations. Similarly, you can enumerate through them easily with standard routines: var i: TFoo;
{ ... }
for i := Low(TFoo) to High(TFoo) do { etc. }
Doing it in C relies on you adding fake enumeration members to stand for counts; and it gets worse in C++, because enumerations are more reluctant to decompose into integers.Writing scanners (as in, compiler lexers) in Pascal is very convenient with set notation:
{ skip whitespace }
while cp^ in [#1..#32] do Inc(cp);
With a good debugger, it can be nice to use sets instead of bit flags, because you generally get a more reliable symbolic breakdown.If you write a lot of code that breaks down in a procedural way, having nested routines is very nice. It can limit the scope of functions and procedures to just the code that needs them. In C, one can be tempted to cram it all into a single long function instead.
Practical Pascals like Turbo Pascal and Delphi have a real live module format that works fairly well for independent compilation. Changes to the interface exported by a unit do not necessarily need all dependent units recompiled. The Pascal linker associates versions with all exported symbols (basically, a hash of their signature), and only units whose import symbol versions don't match the export symbol versions need to be recompiled. This also prevents type mismatches that can (albeit rarely in practice) affect C, where if you change the signature of a function or the type of a variable, and fail to recompile clients, you won't get an error from the linker, because C linkers don't usually encode that info.
But that's for the initial draft. What of its later evolution?