Create your own
Lesson illustration

Static vs. Dynamic Typing: A Historical Perspective

Hello! Welcome to the first lesson in our module on Type Systems.

Over the past few modules, we've explored the foundations of functional programming, recursion, and higher-order patterns—focusing on what we can express in code. Now, we'll shift our focus to a fundamental aspect of language design that governs how programs are constructed and verified: type systems.

This lesson addresses the learning outcome: Distinguish static typing from dynamic typing, referencing their historical origins in the Fortran/ALGOL vs. Lisp traditions.

We will explore what a type system is, define the core difference between checking types before a program runs versus during its execution, and trace this fundamental design choice back to the influential languages that pioneered each approach.

1. What is a Type System?

At its core, a type system is a set of rules that assigns a property called a "type" to the various constructs of a computer program, such as variables, expressions, functions, or modules. You're likely familiar with basic types like integer, float, and string from your work in Python.

A type system's primary purpose is to reduce the possibility of bugs by preventing type errors—situations where an operation is applied to a value of an inappropriate type, like adding an integer to a text string.

To get a more formal grounding, let's turn to the lecture notes based on John C. Mitchell's book.

Concepts in Programming Languages Practicalities Main ...

These notes provide a concise, academic overview of types in programming languages. We'll read a short section to establish our key definitions.

Please read the section 'Types in programming languages' on slide 160. Focus on the three main uses of types and the definitions of a 'type system' and 'type safety' on slides 160-163.

As the notes point out, types help us organize concepts, ensure data is interpreted consistently, and enable compiler optimizations. The central question for a language designer, however, is when to enforce these type rules. This leads us to the two dominant approaches: static and dynamic typing.

2. Static vs. Dynamic Typing: The Core Distinction

The fundamental difference between static and dynamic typing lies in the timing of the type-checking process.

  • Static Typing: Type checking is performed before a program is executed, typically during a compilation phase. The compiler analyzes the source code to ensure that all operations are type-safe. If a type error is found (e.g., passing a string to a function that expects an integer), the compiler will report an error and refuse to build the program.
  • Dynamic Typing: Type checking is performed as the program is executing (at run-time). In this model, variables don't have fixed types, but the values they hold do. An operation's validity is checked just before it's performed. If the types are incompatible, a run-time error (often an exception) is raised, and the program typically halts.

Your experience with Python and Haskell provides a perfect contrast. Python is dynamically typed. You can write x = 5 and then x = "hello" without issue. A type error only occurs if you try an invalid operation, like x + 10, when x holds "hello". Haskell, on the other hand, is statically typed. The compiler would prevent you from ever assigning a string to a variable that was inferred to be a number.

This next reading provides a clear, tabular summary of the trade-offs.

Concepts in Programming Languages Practicalities Main ...

Let's look at how the same lecture notes directly compare the two approaches.

Please read slides 165 and 166. Focus on the definitions of run-time vs. compile-time checking and the table summarizing their advantages and disadvantages.

The key trade-off is between early error detection and flexibility. Static typing catches errors before the program even runs, which is invaluable for reliability, but the rules can sometimes feel restrictive. Dynamic typing offers more flexibility and faster prototyping, but you might not discover certain type errors until the code is running in production.

3. The Historical Origins

This division in typing philosophy is not a recent development. It dates back to the very first high-level programming languages in the 1950s, which were created with very different goals in mind.

The Fortran/ALGOL Tradition: Static Typing for Performance and Safety

The first major high-level language, Fortran (FORmula TRANslator), was developed at IBM in the mid-1950s for scientific and engineering calculations.

  • Goal: The primary concern was execution efficiency. The team had to convince skeptical programmers that a high-level language could generate machine code as fast as hand-written assembly.
  • Design Implication: To generate highly optimized code, the compiler needed to know the type of every variable in advance. This allowed it to allocate the exact amount of memory needed and use the most efficient machine instructions (e.g., integer arithmetic vs. floating-point arithmetic). This necessity gave rise to static typing.

The ALGOL family of languages followed, building on Fortran's procedural nature but with a more mathematical and structured approach. ALGOL introduced key concepts like block structure, nested scopes, and a more formal static type system that became the foundation for many future languages, including Pascal, C, and C++.

Bjarne Stroustrup, the creator of C++, provides some excellent historical context on these early languages.

Bjarne Stroustrup: Journey to C++ from Fortran, Algol, Simula, and C

In this clip, Bjarne Stroustrup discusses his early experiences and the innovations of Fortran and Algol. Notice his emphasis on structure, efficiency, and how a 'stricter type system' could be helpful.

Watch from 04:10 to 07:45. Pay attention to why Fortran was a breakthrough (portability and raising the language to the human level) and how Algol represented a 'technical breakthrough' that refined these ideas.

To solidify this, let's quickly review the details of Fortran and ALGOL's design.

Concepts in Programming Languages Practicalities Main ...

These sections from the lecture notes detail the design of Fortran and Algol.

Skim the following sections: On slide 29, note Fortran's orientation towards 'execution efficiency'. On slide 40, see the explicit mention of 'Static type checking'. On slide 91, note that 'static typing' is listed as a main characteristic of the Algol family. On slide 93, observe the mention of a 'primitive static type system' in Algol 60.

The Lisp Tradition: Dynamic Typing for Flexibility

Appearing just after Fortran, Lisp (LISt Processor) was created at MIT with a completely different purpose.

  • Goal: Lisp was designed for research in artificial intelligence and symbolic computation. The priority was not numerical crunching but flexibility in representing complex, evolving data structures and even treating code as data.
  • Design Implication: Forcing the programmer to declare all types in advance would have been antithetical to Lisp's exploratory nature. Lisp's core data structure, the list, was designed to hold elements of any type. This led to a design where type checks were deferred to run-time, establishing the dynamic typing paradigm.

This short video gives a great overview of Lisp's pioneering role.

Lisp in 100 Seconds

This video from Fireship quickly summarizes Lisp's impact and innovations.

Watch the first 35 seconds. Note that Lisp is introduced as the first interpreted language and that 'Dynamic typing' is listed as one of its key innovations.

The lecture notes confirm this, identifying Lisp as an "untyped" language (the historical term for dynamically typed).

Concepts in Programming Languages Practicalities Main ...

This section on LISP highlights its design philosophy.

Please read slide 54. The key takeaway is the last bullet point: 'LISP is an untyped programming language.'

To fully appreciate the trade-offs, let's return to Bjarne Stroustrup, who, coming from the static tradition, offers a critical perspective on the Lisp approach.

Bjarne Stroustrup: Journey to C++ from Fortran, Algol, Simula, and C

Here, Stroustrup contrasts the philosophy of Lisp with the needs of systems programming, which C++ addresses. This clearly articulates the different priorities that lead to choosing one typing discipline over another.

Watch from 11:11 to 13:19. Listen for his reasoning about why highly dynamic languages can be problematic for systems that require high reliability and performance in constrained environments.

Stroustrup's comments perfectly frame the debate: the dynamic flexibility prized in Lisp is seen as a potential liability in domains where performance and ironclad reliability are paramount.

4. Summary and Reflection

The divergence between static and dynamic typing is one of the oldest and most significant forks in the road of programming language design.

FeatureStatic TypingDynamic Typing
When CheckedBefore execution (compile-time)During execution (run-time)
Primary GoalPerformance, safety, early error detectionFlexibility, expressiveness, rapid prototyping
Error ReportingErrors caught by the compiler before runningErrors (exceptions) occur during execution
Historical RootFortran / ALGOLLisp
Modern ExamplesC++, Java, Haskell, Rust, C#Python, JavaScript, Ruby, Lisp, R

Given your experience with languages from both camps (Python, Lisp vs. Haskell, Rust), think about how this fundamental difference has impacted your work.

  • When has Python's dynamic nature been an advantage?
  • When have you been grateful for Haskell's or Rust's static checks?
  • How does the typing discipline affect the process of debugging or refactoring code in these languages?

Conclusion

In this lesson, we've established the crucial distinction between static and dynamic typing. We saw that:

  • Static typing verifies types at compile-time, a tradition started by Fortran and ALGOL to ensure performance and safety.
  • Dynamic typing checks types at run-time, a tradition pioneered by Lisp to maximize flexibility and expressiveness.
  • This choice represents a fundamental trade-off between finding errors early and allowing for more dynamic program structures.

In our next lesson, we will build directly on this foundation. We'll delve deeper into the mechanics of compile-time vs. run-time type checking, examining how these checks are implemented and the specific kinds of errors they are designed to catch.

Can't find a good explanation? Sign up and we'll make it for you

Sign up