Programming languages began as a veneer over assembly language. They were close to the machine and tedious to program - the state of compiler technology was not advanced enough to support the many features that programmers would find useful, even if they could envision them.
To plug this gap, the idea of an interpreted language was born. This provided a way to write small programs - scripts - in an easier style. Performance was not critical, as the scripts were generally for simple tasks, and may only needed to have run once or occasionally. An entire folklore of interpreter writing techniques developed - it was much easier to implement useful, time-saving features in an interpreter than in a compiler.
At this point, the types of language supported by interpreters and compilers were very different. Interpreted programs were not considered mature enough for production use, hence they were known as 'scripting' languages, as opposed to compiled languages used for 'programming'. They could generally be distinguished as follows:
Programming languages | Scripting languages |
---|---|
Code processed by compiler to generate executable | Code written by programmer executed directly |
Machine code executed by hardware Runs independently on the machine |
Code executed by interpreter Requires a copy of the interpreter to run |
Executes entry-point (main) function | Runs top-to-bottom |
Strongly typed | Untyped |
Prioritizes performance | Proiritizes ease-of-use |
Low-level | High-level |
C, C++, Swift, Go, Rust | Bash, VB6, JavaScript, PHP, Perl, Ruby |
These differences are driven both by the differences between compiler and interpreter technology at the time, and the intended purpose of the languages.
Compiled languages rose up from very close to the metal. The objects were bytestrings, and the types provided interpretation for these bytestrings. For interpreters, it was often easier to use the same data structure for all objects rather than implement a type system. In addition, the correctness benefits of strong types (slight as they may have been at the time) were more important for programs that would be distributed to others for use in production, while for quick scripts it was deemed more important to free the programmer from the burden of thinking about types.
Many early interpreters where 'one-pass', reading in and executing the file at the same time. This means th ile was executed top-to-bottom, giving the program a less structured feel. This also helped contributed to the moniker 'scripting'. On the other hand, object files produced by comilers typically began with headers and static data followed by a series of functions, so it made sense to begin execution with a 'special' function.
However, language implementation technology has come a long way since then, and many of the limitations no longer exist, and indeed, many more recent languages 'mix and match' these design decisions. It's now much more difficult to 'pin down' a language into one category or another. The four technical distinctions in the upper half of the table, while interrelated, are conceptually independent, leading to a possible design space of 24 = 16 points, even before we get to hybrid options.
TODO: Survey of popular languages, and languages that represent interesting points in this space
Interesting as-yet-unexplored points in the design space