Case Sensitivity: A 70-Year Evolution from Fortran to Mojo

Case Sensitivity: A 70-Year Evolution from Fortran to Mojo

Table of Contents

I started my programming journey with Pascal. In that world, MyVariable and myvariable were exactly the same thing. The compiler didn’t care about capitalization.

Then I moved to C. myVariable and MyVariable were now completely different identifiers: capitalization mattered.

Years later I picked up Go, and discovered that capitalization had gained an entirely new meaning. In Go, the case of the first letter controls visibility:

  • ExportedFunction() → visible outside the package
  • internalFunction() → private to the package

At that point I started wondering:

Are programming languages becoming more case-sensitive over time?

To answer that question, I analyzed over 50 major languages created between 1957 and 2023 and mapped how each one treats capitalization.

The result is the chart below.

Before diving deeper, let’s clarify what case sensitivity means in programming.

A language is case-sensitive when identifiers that differ only by capitalization are treated as different symbols. For example, print, Print, and PRINT would refer to three separate identifiers.

A case-insensitive language treats those variations as the same symbol.

The Great Divide: A Visual Analysis

Once languages are plotted chronologically, several patterns emerge.

The earliest languages were often case-insensitive, but that approach gradually disappeared. Modern languages overwhelmingly treat capitalization as meaningful.

At the same time, some languages began using capitalization not just for distinction, but to encode additional meaning in the language itself.

To understand the chart, it helps to look at the different models languages use.

Understanding the Different Case Sensitivity Models

Looking at the languages in the dataset, five distinct approaches to case sensitivity emerge. Some languages ignore capitalization entirely, while others treat it as meaningful syntax.

1. Fully Case-Insensitive Languages

In these languages, identifiers are compared without regard to capitalization. Variable, variable, and VARIABLE all refer to the same symbol.

Examples: Fortran, COBOL, Pascal, Ada, Visual Basic, Delphi, PowerShell, Common Lisp

1with Ada.Text_IO; use Ada.Text_IO;
2procedure Example is
3   myVariable : String := "Hello World";
4begin
5   Put_Line (MYVARIABLE);
6end EXAMPLE;

This approach was common in early programming languages, where the goal was to reduce trivial errors. Historically, some computing systems and punch-card encodings didn’t even distinguish lowercase letters, so treating identifiers as case-insensitive simplified parsing.

Over time, however, this design became less common.

2. Strictly Case-Sensitive Languages

In case-sensitive languages, capitalization creates distinct identifiers.

1int value = 5;
2int Value = 10;   // different variable

Examples include: C, C++, Python, JavaScript, Rust, Swift, Zig, Kotlin, TypeScript, Julia

This is now the dominant design choice in modern languages.

Case sensitivity provides:

  • Clearer symbol boundaries
  • More expressive naming
  • Simpler compiler and tooling behavior

As software systems grew larger, these advantages became more important than avoiding capitalization typos.

3. Partial or Mixed Sensitivity

A few languages treat some identifiers as case-sensitive but others as case-insensitive.

The most famous example is PHP:

  • Variables → case-sensitive
  • Functions and classes → case-insensitive
1$user = "Alice";
2
3echo $USER;      // Error (variables are sensitive)
4
5myFunction();
6MYFUNCTION();    // Works (functions are insensitive)

This hybrid model often reflects historical layering in language design.

4. Style-Insensitive Languages

A rare category ignores both case and certain formatting characters.

The main example is Nim.

In Nim, identifiers are normalized so that case and underscores do not matter.

1var my_variable = 10
2
3echo myVariable
4echo MYVARIABLE

All three identifiers resolve to the same symbol.

This design was introduced to make it easier to integrate libraries that use different naming conventions such as: camelCase, snake_case, PascalCase.

5. Case-Sensitive Languages with Semantic Meaning

Some languages go further: they are case-sensitive, but capitalization also conveys meaning inside the language.

In these languages, case is not just stylistic — it encodes part of the syntax.

Several distinct patterns appear in the dataset.

Variables vs. Atoms / Constants

Some languages use capitalization to distinguish variables from constant symbols.

Examples: Prolog, Erlang

1X = 10.        % variable
2value = 10.    % atom

Here, variables must start with an uppercase letter.

Types vs. Values

In many functional languages, capitalization distinguishes types from values or functions.

Examples: Haskell, Elm, OCaml, Gleam

1data Tree = Leaf Int | Node Tree Tree
  • Tree, Leaf, Node → type and constructors
  • lowercase identifiers → functions or values

This convention makes it easier to visually separate data structures from runtime variables.

Constants vs. Variables

Some languages use capitalization to signal constants.

Examples: Ruby, Crystal

1MAX_SIZE = 100

A capitalized identifier denotes a constant rather than a mutable variable.

Visibility or Module Boundaries

In a few languages, capitalization controls public visibility.

Examples: Go

1func ExportedFunction() {}   // public
2func privateFunction() {}    // package-internal

Instead of using keywords like public or private, the language infers visibility from capitalization.

Naming Rules that Encode Structure

Some newer languages enforce capitalization patterns for different symbol categories.

Examples: V, C3

Typical rules may include:

  • PascalCase → types
  • UPPER_CASE → constants
  • snake_case → variables (V)

This helps make code visually self-describing.

A Clear Historical Pattern

Looking across the dataset, a clear historical shift appears:

  1. Early languages (1950s–1980s): Many were fully case-insensitive.

  2. 1990s onward: Case-sensitive languages became the dominant design.

  3. Modern languages: Often remain case-sensitive but sometimes use capitalization to encode additional meaning.

In other words, the industry gradually moved from ignoring case to using it as a structural signal in the language itself.

Another pattern appears when separating native compiled languages from interpreted languages in the chart.

Native languages converge strongly toward strict case sensitivity.
After the 1990s, nearly every new native language in the dataset treats capitalization as significant.

Interpreted languages remain more diverse.
While many are case-sensitive (Python, JavaScript, Lua), others keep alternative models such as partial sensitivity (PHP), semantic casing (Ruby), or full case insensitivity (PowerShell).

This difference likely reflects their typical use cases: systems languages prioritize precision and unambiguous identifiers, while scripting environments historically favored convenience and flexibility.

Why Early Languages Ignored Case

The origins of case insensitivity are surprisingly practical.

Early computers often used limited character encodings. Many systems relied on 6-bit character sets or hardware that didn’t support lowercase letters at all.

Punch cards made this even more restrictive.

As a result, early languages like Fortran and COBOL simply treated uppercase and lowercase as identical.

Even after hardware improved, the convention stuck.

Languages such as Pascal and Ada intentionally preserved case insensitivity to avoid trivial syntax errors. The philosophy was simple:

The compiler should focus on meaning, not typography.

Pascal’s creator Niklaus Wirth designed the language primarily for teaching, so minimizing frustrating syntax mistakes was a deliberate goal.

So… Are Languages Becoming Case-Sensitive?

Looking at seventy years of language design, the answer is mostly yes. Modern languages overwhelmingly prefer strict case sensitivity. Why?

Because large software systems benefit from: unambiguous identifiers, consistent tooling, and clear namespace boundaries.

But the story isn’t entirely linear. Some languages experiment with semantic casing, while others (like Nim) attempt to reduce stylistic friction.

What started as a hardware limitation has evolved into a design choice — one that reflects the trade-offs each language makes between precision, readability, and developer ergonomics.

Detailed Language Table

LanguageYearExecution TypeSensitivity Classification
Fortran1957NativeFully Insensitive
COBOL1959NativeFully Insensitive
Pascal1970NativeFully Insensitive
C1972NativeSensitive
Prolog1972InterpretedSensitive + Semantic (Caps = Variables)
AWK1977InterpretedSensitive
Ada1983NativeFully Insensitive
C++1985NativeSensitive
Objective-C1984NativeSensitive
MATLAB1984InterpretedSensitive
Common Lisp1984InterpretedFully Insensitive
Erlang1986InterpretedSensitive + Semantic (Caps = Variables)
Perl1987InterpretedSensitive
Tcl1988InterpretedSensitive
Bash1989InterpretedSensitive
Haskell1990NativeSensitive + Semantic (Caps = Types & Constructors)
Python1991InterpretedSensitive
Visual Basic1991NativeFully Insensitive
R1993InterpretedSensitive
Lua1993InterpretedSensitive
Java1995InterpretedSensitive
PHP1995InterpretedPartial (Sensitive Variables Only)
Ruby1995InterpretedSensitive + Semantic (Caps = Constants)
Racket1995InterpretedSensitive
Delphi1995NativeFully Insensitive
JavaScript1995InterpretedSensitive
OCaml1996NativeSensitive + Sensitive (Caps = Constructors & Mods)
C#2000InterpretedSensitive
D2001NativeSensitive
Scala2004InterpretedSensitive
Groovy2003InterpretedSensitive
F#2005NativeSensitive
PowerShell2006InterpretedFully Insensitive
Clojure2007InterpretedSensitive
Nim2008NativeStyle-Insensitive (Ignores Case/Underscores)
Go2009NativeSensitive + Semantic (Caps = Public)
Rust2010NativeSensitive
Dart2011NativeSensitive
Kotlin2011NativeSensitive
Elixir2011InterpretedSensitive + Semantic (Module Naming Rules)
Julia2012NativeSensitive
Elm2012InterpretedSensitive + Semantic (Case defines Type vs. Value)
TypeScript2012InterpretedSensitive
Swift2014NativeSensitive
Crystal2014NativeSensitive + Semantic (Caps = Constants)
Solidity2015NativeSensitive
Zig2016NativeSensitive
Raku2015InterpretedSensitive
Gleam2016InterpretedSensitive + Semantic (Caps = Types & Constructors)
V2019NativeSensitive + Semantic (Forced Pascal/snake_case…)
C32019NativeSensitive + Semantic (Case: Type, Const, Var, …)
Mojo2023NativeSensitive

Did I miss your favorite language?

With thousands of languages in existence, I’m sure a few notable ones slipped through the cracks.

If you notice a missing language or want to challenge my classification of a “quirky” one, let me know in the comments or reach out through the contact form.

Related Posts

Dependency Hell: 5 Strategies to Manage Open Source Risks (The Dependency Dilemma)

Dependency Hell: 5 Strategies to Manage Open Source Risks (The Dependency Dilemma)

The question is: To import or not to import? A library saves weeks of coding, but introduces Dependency Hell risks. This article shares a painful, …

Read More
C++ Learning Resources and Coding Conventions

C++ Learning Resources and Coding Conventions

If you’re looking to learn the C++ programming language and improve your coding skills, using the right resources and following solid coding …

Read More
Stack vs Heap in C++: Supercharge STL Performance with Preallocation

Stack vs Heap in C++: Supercharge STL Performance with Preallocation

Think you’re writing fast C++? Think again. If you’re using STL containers like std::vector or std::unordered_map without thinking about …

Read More