Oh, the Pascal in question is a practical Pascal, not the original teaching language, which is deficient in many ways.
This is my personal opinion of these two third-generation languages. I like third-generation languages. Because of my interest in performance and state, their general lack of higher-level abstraction means that you can pretty much always tell what a program is doing, and how long it is going to take, and what resources it is going to consume; the general flow of control is not hidden. (To me, high-abstraction languages are a lot like handing tasks off to graduate assistants. I may be thinking that he'll get right on it, whereas he might think that the waves are bitchin' gnarly right now, and he'll do my task maybe tomorrow. Sometime. Sure, to avoid this I can be very specific in my task assignments, but often it's easier to just do it myself than to fully specify all the necessary parameters. The same, I find, can be true of computer languages.)
I'm going to ignore both languages' Object modeling. (And thus C++ and its ilk.) Just the straight procedural languages, which is where I have always been, and in fact still am, working.
I'm going to ignore most purely syntactic differences, such as
:=" and "
!=" vs "
<>", and Pascal's terminating period. Po-tay-toe,
po-tah-toe. I'm also going to ignore attributes of specific
compiler/IDE products, such as convenience, speed, or quality of
generated code. These can all change, and have nothing to do with
the qualities of the language.
NB: Both C and Pascal have learned from each other over the years, and some of what once might have been clear advantages are no more, in any significant way. These languages are now more alike than different.
#include allows for one to construct a semantic whole out
of fragments placed in multiple files, controlled by
#define, besides being used for type
definitions and primordial enumerations, can be 'abused' for textual
aliasing. It can even be used for system configuration duties, by
#include-ing configuration files written in a
configuration meta-language of sorts, using
determine what flavor of configuration is being pulled in at that
And, of course, the pre-processor and the easily-abused ternary and comma operators make it the unquestioned champion of obfuscated language contests between the two languages. (Maybe that's not a plus!)
f()" make it very clear that flow of control is going elsewhere, whether or not arguments are necessary. (In C
x = y;is always a 'cheap' operation; in Pascal
x := ycould be cheap, or could be a very expensive function call with deep side-effects. There's no way to tell by looking, you have to do deep analysis.)
result := X" uses a magic name, which is less obvious than C's explicit "
if X then begin Y; end else begin Z; end;is just too damned wordy. (Oh look: there's an extra semicolon, and don't you forget it. Yay!)
if (x = y()) z();" is just what you wanted. This compact notation is especially attractive for programmers (like me) who learned assembly language first, where partial results ("
x", above) are easily saved for later re-use, and where condition-code-based conditional execution is the norm. In C, "
x = y = z;" is a clever side-effect of regular assignment, and can be used anywhere.
Consider a situation where we are going to be concatenating two strings, but the result cannot be overlong, so we're going to preferentially punish the longer of the two components. In C it's pretty short, and moderately efficient. (If we really cared about runtime efficiency we'd factor the string lengths out of the loop entirely, or even eliminate the loop altogether, but that would be more complex, which carries its own problems. We favor here the simplest form, and modest efficiency. Good Enough, with a reasonable path for getting Better if it should later turn out to be necessary, is often the best design choice.)
vs Pascal's similar-sized (but far less efficient):while (((len1 = strlen(s1)) + (len2 = strlen(s2))) > 26) if (len1 > len2) s1[len1-1] = 0; else s2[len2-1] = 0;
while length(s1) + length(s2) > 26 do if length(s1) > length(s2) then setLength(s1, length(s1) - 1) else setLength(s2, length(s2) - 1);
But! Five different string length calls per loop iteration, versus two. (But still only two semicolons.) Any Pascal expression that tries to approach C's runtime efficiency here will be somewhat larger due to the syntactic requirements of the language, which will tend to obscure the intrinsic simplicity of what we are doing here. Perhaps:
while True do begin len1 := length(s1); len2 := length(s2); if len1 + len2 <= 26 then break; if len1 > len2 then setLength(s1, len1 - 1) else setLength(s2, len2 - 1); end;
You also want to know what data connections there are, and because Pascal's nested functions have implicit access to all their containing functions' variables means that the data connection graph is fairly complex, and somewhat hidden. This, IMHO, is unnecessary freedom, and can ultimately result in not knowing what is truly going on. More bugs, and much more difficulty in refactoring, hampering restructuring that might make sense logically, but which in practice would result in too much work, and so does not get done.
In this case, I argue that simpler is better. Complexity is one of the true sources of problems; unnecessary complexity is just stupid.
with X do' construct, which I have long thought of as a significant Pascal advantage. It saves a lot of typing. (On the other hand, it's also a disadvantage, because it introduces yet another layer of implicit context that can cloud the issue of exactly what variables are being referred to. C seemed a lot cruder, but if
Xis messy enough that repeated typing of it is repugnant, you can always introduce a short variable "
p->" to mitigate that, while not clouding the issue of what exactly you're referring to. This point I'm still waffling about.)
Likewise, while C's 'new' prototypes are a boon, how they were introduced was not. I loathe the fact that you must use the new-style function definitions when using prototypes. When rummaging through established code I rarely need to see the types of function arguments, and would much prefer the simpler argument lists of K&R C. If there's a typing problem I can always look at the type declarations, otherwise I can skip over them with ease. The compiler should be smart enough to do lint-style type checking, regardless of the form of the function definition.