6
$\begingroup$

For me, one of the best things to happen to FORTRAN was the introduction of the LOGICAL*1 type. I quickly discovered that putting IMPLICIT LOGICAL*1 (A-Z) at the top of every program was a life saver.

Knowing about this one line saved me hours of work debugging new code, where many typos resulted in defining a new variable rather than producing an error message.

The iconic example is:

      INTEGER I
      INTEGER SUM

      SUM = 0
      DO 10 I=1,10
   10 SUM = SUM + I
      PRINT 11, SUM
   11 FORMAT (I10)

      SUM = 0
      DO 20 I=1.100
   20 SUM = SUM + I
      PRINT 21, SUM
   21 FORMAT(I10)

      STOP
      END

One would expect the output to be:

   55
 5050

But in fact it is:

   55
   11

The typo might be obvious to you now, but imagine that the line is written along the top edge of a punch card by a misaligned dot-matrix printer with a bad ribbon. Then imagine that that one card is buried in a deck of thousands of cards, and rather than being part of an immediately following PRINT, the error doesn't become apparent until hundreds of lines later.

One could easily spend hours tracking down the typo.

But if we began the program with IMPLICIT LOGICAL*1 (A-Z), the compiler would quickly report the problem:

12 | DO 20 I=1.100 | 1 Error: Cannot convert REAL(4) to LOGICAL(1) at (1)

And the difference between a faint blurry comma and a faint blurry period becomes apparent.

But the IMPLICITLOGICAL*1(A,Z) was only a work-around (albeit a very useful one). The underlying problem was that the implicit definition of variables created a large potential for hard to detect errors.

I later moved on to "B" and "C", where every identifier had to be explicitly declared, and life was wonderful.

But then even newer languages appeared, and shockingly they had reverted back to implicit definitions.

Having seen the problems with FORTRAN, and their elimination with "B" and "C", why would the designers of the newer languages think that implicit declarations were a good idea?

$\endgroup$

2 Answers 2

7
$\begingroup$

This is an issue of philosophy, and what the language is meant to be used for.

On the one hand there are languages which want to empower the programmer to write code which checks itself for correctness ─ the idea being that it saves time to catch bugs at compile-time instead of runtime, so the language should require the programmer to write in a way that allows mistakes to be detected at compile-time. This is the viewpoint espoused in your question; it leads to static type systems, var vs. const and readonly/immutable properties, visibility modifiers, and all sorts of other static analyses which will be checked but then erased by the compiler, and have no effect at runtime.

On the other hand there are languages which want to make it quicker and easier to write a program ─ the idea being that the programmer should only have to write code to say what the program does, and not have to write extra code to prove to the compiler that the program does what it is supposed to do. This viewpoint leads to dynamically typed languages, variables that don't need to be declared, all properties being mutable and public, and runtime errors for mistakes that would be compile-time errors in the other category.

Both viewpoints have their place. Generally speaking I think languages in the first camp are better suited for larger or more critical software projects, while languages in the second camp are good for smaller or more experimental projects ─ particularly simple programs that are unlikely to have many mistakes, scripts that will probably not be used more than a few times, or proof-of-concept prototypes where you just want to see if some idea will work.

It's worth adding that there's still a wide range of strictness in the first camp, with some languages even allowing the programmer to write preconditions, postconditions, invariants and formal proofs that the compiler can check. And languages in the second camp can still have tooling to detect some errors before runtime ─ for example, if you misspell a variable name, a linter might notice that the correctly-named variable is unused or has a dead store. So really there's a spectrum.

Additionally, as languages in the second camp mature, and start to be used for larger or more important projects, they typically add features which move them a bit in the direction of the first camp, e.g. const and private properties in Javascript, or optional type annotations in Python. So this might explain why "newer" languages are more likely to be closer to the second camp, although there are plenty of "new" languages like Rust which are firmly in the first and plenty of "old" languages like Lisp which are firmly in the second.

$\endgroup$
3
$\begingroup$

JS: A weird consistency

Consider these two rules in JavaScript:

  1. You may write a property that an object doesn’t already have.
let x = {};
x.a = 2;
  1. If an identifier is not declared by function or var (or let, const, or class in modern JS), it references a property on the global object (e.g. window in a browser).
setTimeout(() => {}, 1000); // calls window.setTimeout

Therefore, assigning to an undeclared identifier will add a property to the global object.

$\endgroup$
5
  • 2
    $\begingroup$ So are you saying that the developers of JS thought implicitly declared global variables were a good idea, because that behavior was consistent with the rest of the language? I'm experiencing a bit of cognitive dissonance here, since I'm so used to thinking of this feature as a bad thing to be avoided at all costs. I guess I assumed--perhaps incorrectly--that it must not have been an intentionally designed feature but rather something of an accident, with a later partial fix in the form of strict mode. $\endgroup$
    – DLosc
    May 19 at 5:32
  • 2
    $\begingroup$ @DLosc I don't know about this specific point, but the initial design of Javascript was famously done under great time pressure, and many of the initial decisions were solely based on what the author could implement in the few days that he had, or were just the first thing he happened to write. $\endgroup$ May 19 at 7:28
  • $\begingroup$ @DLosc I think "make everything implicitly global" is not so universally agreed as a bad idea for language design in all contexts; consider shell scripting languages, for example. Perl, PHP and other dynamic scripting languages designed in the late 80s/early 90s were arguably closer in spirit to shell scripts than they were to C or Java. These languages were not designed for writing large, complex software; it made sense to encourage globals because it made the language easy to use for its intended purposes. $\endgroup$
    – kaya3
    May 19 at 7:33
  • 2
    $\begingroup$ I will point out that this behavior is disabled in strict mode, so clearly the maintainers of the language saw the drawbacks to this behavior. $\endgroup$
    – Bbrk24
    May 19 at 11:22
  • $\begingroup$ IMHO, JavaScript could have benefited from having discrete assignment operators for "Modify field that must already exists", "Create field that must not exist", and "Create or modify field, based upon whether it exists", since a lot of code could use one of the first two forms, and use of such forms when applicable would make code much more robust. $\endgroup$
    – supercat
    Nov 15 at 22:55

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .