Disclaimer: ChatGPT generated document.
Programming languages can look very different on the surface, but underneath they are built from many of the same recurring concepts. C++ has templates and RAII, Java has interfaces and garbage collection, Rust has traits and ownership, Haskell has algebraic data types and type classes, Python has dynamic typing and comprehensions, and JavaScript has prototypes and promises. These features differ in syntax and implementation, but they often solve the same fundamental problems: how to represent data, organize code, control execution, manage memory, express abstraction, handle errors, and coordinate concurrent work.
This taxonomy is intended as a broad, language-independent map of that territory. It distinguishes the core language from the standard library, runtime, compiler or interpreter, toolchain, ecosystem, programming paradigms, and the deeper concepts from programming-language theory that explain how these systems work. Within each area, it groups related constructs and mechanisms—such as functions, pattern matching, generics, closures, modules, ownership, garbage collection, async/await, macros, type inference, and many others—so that unfamiliar terminology can be placed into a larger conceptual structure.
The goal is not merely to list features found in particular languages, but to provide a reusable mental model for understanding almost any programming language. When encountering a new concept, you should be able to ask: What category does this belong to? What problem does it solve? At what layer does it operate? And what alternative approaches do other languages use?
No taxonomy can literally contain every idea ever explored in programming-language research, and some concepts naturally overlap several categories. Nevertheless, the following provides a comprehensive map of the major concepts, constructs, mechanisms, abstractions, tools, and theoretical foundations encountered across modern and historical programming languages.
At the highest level:
Programming Language / Programming Platform
│
├── A. Core Language
│ ├── Lexical structure
│ ├── Syntax & grammar
│ ├── Names & binding
│ ├── Values & data
│ ├── Type system
│ ├── Expressions
│ ├── Statements
│ ├── Control flow
│ ├── Pattern matching
│ ├── Functions
│ ├── Abstraction
│ ├── Object-oriented features
│ ├── Generic programming
│ ├── Functional programming
│ ├── Modules
│ ├── Metaprogramming
│ ├── Error handling
│ ├── Resource/lifetime management
│ ├── Concurrency
│ └── Low-level/system features
│
├── B. Standard Library / Standard API
│
├── C. Runtime & Execution Model
│
├── D. Language Implementation
│
├── E. Build & Toolchain
│
├── F. Package & Dependency Ecosystem
│
├── G. Development Tools
│
├── H. Programming Paradigms
│
└── I. Language Design / PL Theory
The distinction between these levels matters. Pattern matching, for example, is primarily a core-language control/data-decomposition feature. A hash map is usually a standard-library data structure. Garbage collection is primarily a runtime/memory-management mechanism. Maven is build/dependency tooling. Functional programming is a paradigm rather than one particular construct.
Now let's expand the tree.
Before a language even has expressions, it has rules for turning source text into tokens.
Lexical Structure
├── Character set / source encoding
├── Whitespace
├── Significant whitespace
├── Line terminators
├── Comments
│ ├── Line comments
│ ├── Block comments
│ ├── Nested comments
│ └── Documentation comments
├── Identifiers
├── Keywords
│ ├── Reserved keywords
│ └── Contextual keywords
├── Literals
│ ├── Integer
│ ├── Floating-point
│ ├── Boolean
│ ├── Character
│ ├── String
│ ├── Raw string
│ ├── Byte string
│ ├── Regex
│ ├── Null/nil
│ └── Collection literals
├── Operators
├── Punctuation / delimiters
├── Escape sequences
└── Tokens / lexemes
For example:
x = 42might lex approximately into:
IDENTIFIER("x")
EQUALS
INTEGER_LITERAL(42)
Related concepts include lexing, tokenization, and scanner.
The grammar determines which combinations of tokens form valid programs.
Syntax
├── Grammar
├── Productions / grammar rules
├── Expressions
├── Statements
├── Declarations
├── Definitions
├── Blocks
├── Scope-forming constructs
├── Precedence
├── Associativity
├── Ambiguity
└── Syntactic sugar
Languages are commonly described formally using things such as:
BNF
EBNF
Context-free grammars
Parsing expression grammars
An important distinction is:
syntax = how something is written
semantics = what it means.
A huge amount of language design concerns the relationship between names and things.
Names & Binding
├── Identifier
├── Declaration
├── Definition
├── Binding
├── Name resolution
├── Scope
│ ├── Lexical/static scope
│ └── Dynamic scope
├── Visibility
├── Shadowing
├── Hiding
├── Namespace
├── Qualified names
├── Aliases
├── Imports
├── Exports
├── Forward declarations
└── Linkage
For example:
int x = 10;creates a binding roughly:
name x → object/value
Scope deserves its own subdivision.
Scope
├── Global scope
├── Module scope
├── Namespace scope
├── File scope
├── Class scope
├── Function scope
├── Block scope
├── Expression scope
├── Lexical scope
└── Dynamic scope
Related concepts:
visibility
accessibility
lifetime
storage duration
linkage
These are related but not identical.
The next major area is the language's data model.
Values
├── Literal values
├── Variables
├── Constants
├── Mutable values
├── Immutable values
├── References
├── Objects
├── Identity
├── Equality
├── Copying
├── Moving
└── Aliasing
Important distinctions include:
value vs object
value vs reference
identity vs equality
mutable vs immutable
copy vs reference semantics
Typical primitive types include:
Primitive Types
├── Boolean
├── Integer
│ ├── Signed
│ ├── Unsigned
│ ├── Fixed width
│ ├── Arbitrary precision
│ └── Machine word
├── Floating point
├── Decimal
├── Fixed point
├── Character
├── Byte
├── String
├── Symbol
├── Unit
├── Void
├── Null / nil / none
└── Never / bottom
Not every language considers these "primitive." For example, whether strings are primitive, objects, or library types varies considerably.
Composite Types
├── Arrays
├── Tuples
├── Records
├── Structs
├── Classes
├── Objects
├── Enumerations
├── Tagged unions
├── Variant types
├── Discriminated unions
├── Algebraic data types
├── Product types
├── Sum types
├── Lists
├── Maps
├── Sets
└── User-defined types
This leads into an important theoretical distinction.
A value contains A and B:
Person = String × Int
roughly:
struct Person {
name: String,
age: u32
}A value is A or B:
Result = Success | Failure
Rust:
enum Result<T, E> {
Ok(T),
Err(E),
}Haskell:
data Either a b = Left a | Right bThese are algebraic data types (ADTs).
This is one of the largest branches.
Type Systems
├── Static typing
├── Dynamic typing
├── Strong typing
├── Weak typing
├── Explicit typing
├── Implicit typing
├── Type inference
├── Structural typing
├── Nominal typing
├── Duck typing
├── Gradual typing
├── Dependent typing
├── Refinement typing
├── Linear typing
├── Affine typing
├── Ownership typing
├── Effect typing
├── Flow-sensitive typing
├── Path-sensitive typing
├── Occurrence typing
└── Optional typing
These dimensions are mostly independent.
For example:
C++ statically typed + mostly nominal
Java statically typed + nominal
Python dynamically typed + duck typing
TypeScript gradually typed + structural
Rust statically typed + nominal + affine/ownership features
Haskell statically typed + extensive inference
Type Relationships
├── Type equivalence
├── Type compatibility
├── Subtyping
├── Supertyping
├── Inheritance
├── Interface implementation
├── Structural compatibility
├── Coercion
├── Conversion
├── Casting
├── Upcasting
├── Downcasting
└── Type erasure
Subtyping itself introduces:
A <: B
meaning approximately:
A can be used where B is expected.
A particularly important taxonomy:
Polymorphism
├── Ad-hoc polymorphism
│ ├── Function overloading
│ └── Operator overloading
│
├── Parametric polymorphism
│ ├── Generics
│ └── Templates
│
├── Subtype polymorphism
│ ├── Interfaces
│ ├── Virtual methods
│ └── Dynamic dispatch
│
└── Coercion polymorphism
This is why "polymorphism" does not merely mean OOP.
C++ templates and Java generics are examples of parametric polymorphism, while overriding virtual/interface methods gives subtype polymorphism.
Generic Programming
├── Type parameters
├── Generic functions
├── Generic types
├── Generic classes
├── Templates
├── Constraints
├── Bounds
├── Concepts
├── Traits
├── Type classes
├── Associated types
├── Higher-kinded types
├── Variance
│ ├── Covariance
│ ├── Contravariance
│ └── Invariance
├── Monomorphization
├── Type erasure
└── Reification
Different languages attack the same problem differently:
C++ templates + concepts
Java generics + bounds
Rust generics + traits
Haskell type variables + type classes
C# generics + constraints
Swift generics + protocols
An expression generally computes or produces a value.
Expressions
├── Literals
├── Variable references
├── Arithmetic
├── Comparison
├── Logical operations
├── Bitwise operations
├── Assignment expressions
├── Conditional expressions
├── Function calls
├── Method calls
├── Member access
├── Indexing
├── Slicing
├── Casts
├── Object construction
├── Lambda expressions
├── Comprehensions
├── Await expressions
├── Match expressions
└── Block expressions
Some languages are heavily expression-oriented.
Rust:
let x = if condition {
10
} else {
20
};Here if produces a value.
Operators
├── Arithmetic
│ ├── +
│ ├── -
│ ├── *
│ ├── /
│ └── %
├── Comparison
├── Equality
├── Logical
├── Bitwise
├── Shift
├── Assignment
├── Compound assignment
├── Increment/decrement
├── Address/dereference
├── Member access
├── Indexing
├── Function application
├── Null-coalescing
├── Optional chaining
├── Pipeline
├── Range
└── User-defined operators
Concepts include:
arity
unary
binary
ternary
precedence
associativity
short-circuiting
operator overloading
Statements generally perform actions rather than produce values.
Statements
├── Expression statement
├── Declaration statement
├── Assignment
├── Block
├── Conditional
├── Loop
├── Jump
├── Return
├── Throw
├── Yield
├── Assert
├── Import
└── Empty statement
Some languages blur or eliminate the expression/statement distinction.
This is another major family.
Control Flow
├── Sequence
├── Selection
│ ├── if
│ ├── if/else
│ ├── switch
│ ├── case
│ ├── match
│ └── conditional expression
│
├── Iteration
│ ├── while
│ ├── do-while
│ ├── for
│ ├── foreach
│ └── loop
│
├── Jumps
│ ├── break
│ ├── continue
│ ├── return
│ └── goto
│
├── Exceptions
├── Coroutines
├── Generators
├── Continuations
└── Recursion
The example you specifically mentioned belongs to a surprisingly large family.
A pattern describes the shape a value must have and often simultaneously introduces bindings.
Basic example:
match value {
Some(x) => println!("{x}"),
None => println!("nothing"),
}Taxonomy:
Pattern Matching
├── Literal patterns
├── Variable/binding patterns
├── Wildcard patterns
├── Constant patterns
├── Tuple patterns
├── Record/struct patterns
├── Constructor patterns
├── Variant patterns
├── List patterns
├── Array patterns
├── Range patterns
├── Type patterns
├── Or-patterns
├── And-patterns
├── Nested patterns
├── Rest/spread patterns
├── Alias/as-patterns
├── Guarded patterns
└── Regex/string patterns
Pattern matching is closely connected to destructuring.
JavaScript:
const {name, age} = person;Python:
match value:
case ("point", x, y):
...Haskell:
head (x:xs) = xRust, Scala, OCaml, F#, Erlang, Elixir, Haskell and increasingly Python/Java/C# all expose different forms of this idea.
Related but separable from matching:
Destructuring
├── Tuple destructuring
├── Array destructuring
├── Object/record destructuring
├── Nested destructuring
└── Rest destructuring
Example:
const [first, second] = values;This essentially means:
decompose this structured value and bind its components to names.
A gigantic category in its own right.
Functions
├── Declaration
├── Definition
├── Parameters
├── Arguments
├── Return values
├── Multiple return values
├── Default parameters
├── Named parameters
├── Optional parameters
├── Variadic parameters
├── Function overloading
├── Anonymous functions
├── Lambdas
├── Closures
├── Nested functions
├── Local functions
├── Higher-order functions
├── First-class functions
├── Pure functions
├── Recursive functions
├── Tail recursion
├── Generic functions
├── Methods
├── Extension methods
├── Coroutines
├── Generators
└── Async functions
Important terminology:
first-class function means functions can themselves be treated as values.
higher-order function means a function takes functions as arguments and/or returns them.
A closure is approximately:
function + captured environment
For example:
function makeAdder(x) {
return y => x + y;
}The returned function retains access to x.
Related concepts:
free variables
captured variables
capture by value
capture by reference
lexical environment
closure object
escaping closure
non-escaping closure
Functional Features
├── First-class functions
├── Higher-order functions
├── Lambdas
├── Closures
├── Immutability
├── Pure functions
├── Function composition
├── Currying
├── Partial application
├── Recursion
├── Tail recursion
├── Pattern matching
├── Algebraic data types
├── Persistent data structures
├── Lazy evaluation
├── Referential transparency
├── Functors
├── Applicatives
├── Monads
└── Algebraic effects
Some of these are mathematical abstractions rather than literal keywords.
Object-Oriented Features
├── Objects
├── Classes
├── Instances
├── Fields
├── Properties
├── Methods
├── Constructors
├── Destructors
├── Encapsulation
├── Information hiding
├── Access modifiers
├── Inheritance
├── Multiple inheritance
├── Abstract classes
├── Interfaces
├── Protocols
├── Traits
├── Mixins
├── Composition
├── Delegation
├── Method overriding
├── Method overloading
├── Virtual methods
├── Dynamic dispatch
├── Static dispatch
├── Message passing
├── Reflection
└── Metaclasses
And an important conceptual hierarchy:
Polymorphism
│
├── Subtype polymorphism
│ └── dynamic dispatch
│
├── Parametric polymorphism
│ └── generics
│
└── Ad-hoc polymorphism
└── overloading
Access Control
├── public
├── private
├── protected
├── internal/package
├── friend
├── module-private
├── file-private
└── capability-based access
Related concepts:
encapsulation
information hiding
API boundary
implementation detail
interface
contract
At larger scales:
Program Organization
├── Source file
├── Translation unit
├── Module
├── Package
├── Namespace
├── Crate
├── Assembly
├── Library
├── Component
├── Import
├── Export
├── Include
├── Re-export
├── Visibility
├── Module interface
└── Dependency
Names vary substantially:
C++ namespaces, translation units, modules
Java packages, modules
Python modules, packages
Rust modules, crates
C# namespaces, assemblies
JavaScript modules, packages
Go packages, modules
Error Handling
├── Return codes
├── Sentinel values
├── Null/nil
├── Exceptions
│ ├── throw
│ ├── try
│ ├── catch
│ ├── finally
│ ├── checked exceptions
│ └── unchecked exceptions
├── Result types
├── Option/Maybe types
├── Error unions
├── Panic
├── Assertions
├── Preconditions
├── Postconditions
└── Contracts
Three major philosophies are roughly:
C/Go style explicit error values
Java/C# style exceptions
Rust style algebraic Result/Option values
Languages can combine these.
A whole subfield:
Absence
├── Null
├── Nil
├── None
├── Optional
├── Maybe
├── Nullable types
├── Non-null types
├── Null-coalescing
├── Optional chaining
└── Null safety
For example:
T
versus
Optional<T>
makes absence explicit in the type system.
Now we get closer to systems programming.
Memory
├── Stack
├── Heap
├── Static storage
├── Thread-local storage
├── Automatic storage
├── Dynamic allocation
├── Object lifetime
├── Storage duration
├── Alignment
├── Padding
├── Object representation
├── Memory layout
└── Memory ordering
Memory Management
├── Manual allocation
│ ├── malloc/free
│ └── new/delete
│
├── Garbage collection
│ ├── Reference counting
│ ├── Tracing GC
│ ├── Mark-and-sweep
│ ├── Mark-and-compact
│ ├── Copying GC
│ ├── Generational GC
│ └── Concurrent GC
│
├── Automatic reference counting
├── Ownership
├── Borrowing
├── Regions
├── Arenas
└── RAII
Languages occupy different points:
C manual
C++ RAII + manual + smart pointers
Java tracing GC
Python reference counting + cyclic GC
Rust ownership/borrowing + RAII
Swift ARC
Go GC
Indirection
├── References
├── Raw pointers
├── Smart pointers
│ ├── Unique ownership
│ ├── Shared ownership
│ └── Weak references
├── Borrowed references
├── Interior pointers
├── Function pointers
├── Null pointers
├── Fat pointers
└── Handles
Operations/concepts:
address-of
dereferencing
pointer arithmetic
aliasing
provenance
nullability
dangling pointers
use-after-free
Rust makes these especially visible, but the concepts are broader.
Ownership
├── Owner
├── Move semantics
├── Copy semantics
├── Borrowing
│ ├── Shared borrow
│ └── Mutable borrow
├── Lifetimes
├── Lifetime parameters
├── Borrow checking
├── Aliasing rules
├── Interior mutability
└── RAII
Memory isn't the only resource.
Resources
├── Memory
├── Files
├── Sockets
├── Locks
├── Database connections
├── OS handles
├── GPU resources
└── Transactions
Mechanisms include:
RAII
try/finally
defer
using
with/context managers
destructors
finalizers
scope guards
For example Python's:
with open(...) as f:
...is a context-management/resource-management construct.
Iteration
├── Index-based iteration
├── Iterator
├── Iterable
├── Enumerator
├── Generator
├── Range
├── Stream
├── Lazy sequence
├── Comprehension
└── Iterator protocol
This gives us distinctions such as:
collection
iterable
iterator
generator
stream
which aren't synonymous.
Examples:
[x * x for x in numbers if x > 0]Taxonomy:
Comprehensions
├── List comprehension
├── Set comprehension
├── Dictionary comprehension
├── Generator expression
└── Query comprehension
They combine:
iteration
mapping
filtering
construction
into one construct.
Evaluation Strategy
├── Eager / strict
├── Lazy
├── Call-by-value
├── Call-by-reference
├── Call-by-name
├── Call-by-need
├── Normal-order
└── Applicative-order
Haskell is famously lazy by default.
Most mainstream imperative languages are predominantly eager.
Parameter Passing
├── Pass by value
├── Pass by reference
├── Pass by sharing
├── Pass by name
├── Pass by need
├── Copy-in/copy-out
└── Move
This is frequently confused in discussions of languages like Python and Java.
Mutability
├── Mutable binding
├── Immutable binding
├── Mutable object
├── Immutable object
├── const
├── readonly
├── final
├── persistent structures
└── interior mutability
Crucially:
immutable variable
and
immutable object
are not necessarily the same thing.
Another enormous branch:
Concurrency
├── Threads
├── Processes
├── Tasks
├── Fibers
├── Coroutines
├── Green threads
├── Actors
├── Goroutines
├── Async/await
├── Futures
├── Promises
├── Channels
├── Message passing
├── Shared memory
├── Structured concurrency
└── Reactive programming
Synchronization
├── Mutex
├── Recursive mutex
├── Read/write lock
├── Spinlock
├── Semaphore
├── Monitor
├── Condition variable
├── Barrier
├── Latch
├── Atomic operation
├── Compare-and-swap
├── Memory fence
├── Transaction
└── Lock-free algorithms
Associated concepts:
race condition
data race
deadlock
livelock
starvation
fairness
atomicity
visibility
ordering
happens-before
Concurrency and parallelism aren't identical.
Parallelism
├── Data parallelism
├── Task parallelism
├── SIMD
├── Vectorization
├── Multithreading
├── Multiprocessing
├── GPU computing
└── Distributed computing
A useful distinction:
Concurrency concerns multiple computations making progress.
Parallelism concerns computations executing simultaneously.
Async Programming
├── Callbacks
├── Event loops
├── Futures
├── Promises
├── async/await
├── Coroutines
├── Async generators
├── Reactive streams
├── Completion handlers
└── Continuations
JavaScript, C#, Python, Rust, Kotlin and many others expose variants of this model.
Coroutines
├── Stackful coroutines
├── Stackless coroutines
├── Symmetric coroutines
├── Asymmetric coroutines
├── Generators
├── Async coroutines
└── Resumable functions
Core operations include:
yield
resume
suspend
await
A more advanced control-flow concept:
Continuation
├── Continuation-passing style
├── First-class continuation
├── Delimited continuation
├── call/cc
└── Continuation transformation
A continuation roughly represents:
"the rest of the computation."
This is an important concept behind exceptions, coroutines, async transformations and some functional-language features.
Programs that manipulate or generate programs:
Metaprogramming
├── Preprocessor
├── Macros
│ ├── Textual macros
│ ├── Syntactic macros
│ ├── Hygienic macros
│ ├── Procedural macros
│ └── Attribute macros
├── Templates
├── Compile-time evaluation
├── constexpr
├── Reflection
├── Introspection
├── Code generation
├── AST transformation
├── Annotation processing
├── Compiler plugins
└── Staged programming
Different examples:
C preprocessor macros
C++ templates + constexpr
Rust declarative/procedural macros
Lisp macros
Java reflection + annotation processors
C# reflection + source generators
Runtime Introspection
├── Inspect type
├── Inspect members
├── Inspect annotations
├── Discover methods
├── Dynamic invocation
├── Construct objects dynamically
├── Modify accessibility
└── Dynamic proxying
Related distinction:
introspection = inspect program structure
reflection = often inspect and manipulate/interact with program structure
though terminology varies.
Compile-Time Programming
├── Constant expressions
├── Constant evaluation
├── Templates
├── Compile-time functions
├── Type-level programming
├── Type computation
├── Macros
├── Code generation
└── Static assertions
This becomes especially sophisticated in:
C++
Rust
Haskell
Scala
D
Zig
Here types themselves become computational objects.
Type-Level Programming
├── Type functions
├── Type families
├── Conditional types
├── Mapped types
├── Type traits
├── Template metaprogramming
├── Higher-kinded types
├── Phantom types
├── GADTs
├── Dependent types
└── Kind polymorphism
TypeScript, surprisingly, has an extremely expressive type-level language.
A more advanced modern area:
Effects
├── Exceptions
├── State
├── I/O
├── Nondeterminism
├── Async
├── Logging
└── User-defined effects
An effect system tracks effects in the type system.
Related concepts:
effect handlers
algebraic effects
monadic effects
capability systems
Languages/research ecosystems such as Koka, Eff and newer language designs explore this heavily.
Systems languages expose things higher-level languages often hide.
Systems Features
├── Raw memory
├── Pointers
├── Pointer arithmetic
├── Memory layout
├── Alignment
├── Endianness
├── Bit manipulation
├── Volatile access
├── Atomics
├── Inline assembly
├── Intrinsics
├── SIMD
├── Foreign function interface
├── ABI control
├── Calling conventions
├── Linkage
├── Unsafe blocks
└── Zero-cost abstractions
Languages need to communicate with other languages.
FFI
├── Native calls
├── C ABI
├── Calling conventions
├── Name mangling
├── Data marshalling
├── Struct layout
├── Dynamic libraries
├── Static libraries
├── Bindings
└── Interop layers
Examples:
Python ↔ C
Java ↔ JNI
C# ↔ P/Invoke
Rust ↔ C ABI
Now we leave the language proper.
A typical standard library contains:
Standard Library
├── Core utilities
├── Data structures
├── Algorithms
├── Strings/text
├── Numeric facilities
├── I/O
├── Files
├── Networking
├── Dates/times
├── Concurrency
├── Randomness
├── Serialization
├── Regular expressions
├── Reflection
├── Process management
└── OS interfaces
Let's expand some.
Collections
├── Array
├── Dynamic array
├── Vector
├── List
├── Linked list
├── Stack
├── Queue
├── Deque
├── Set
├── Multiset
├── Map
├── Multimap
├── Dictionary
├── Hash table
├── Tree
├── Heap
├── Priority queue
├── Graph
├── Trie
├── Bitset
├── Bloom filter
└── Persistent collections
Not all of these are standard-library facilities in every language.
Algorithms
├── Searching
├── Sorting
├── Partitioning
├── Selection
├── Transformation
├── Filtering
├── Mapping
├── Folding/reduction
├── Accumulation
├── Comparison
├── Set operations
├── Heap operations
├── Numeric algorithms
└── Parallel algorithms
Functional vocabulary is especially useful:
map
filter
reduce/fold
scan
zip
flatMap/bind
Text
├── Strings
├── Unicode
├── Encodings
├── Code points
├── Grapheme clusters
├── Formatting
├── Parsing
├── Regular expressions
├── Tokenization
├── Localization
└── Internationalization
I/O
├── Standard input
├── Standard output
├── Standard error
├── Streams
├── Buffered I/O
├── Binary I/O
├── Text I/O
├── Files
├── Memory-mapped files
├── Pipes
├── Sockets
└── Async I/O
Serialization
├── Binary serialization
├── Text serialization
├── JSON
├── XML
├── CSV
├── Object serialization
├── Schema-based serialization
└── Encoding/decoding
Networking
├── TCP
├── UDP
├── Sockets
├── DNS
├── HTTP
├── TLS
├── URLs/URIs
├── WebSockets
└── Async networking
Whether these belong in the standard library varies considerably.
Numerics
├── Integers
├── Floating point
├── Decimal
├── Big integers
├── Complex numbers
├── Rational numbers
├── Math functions
├── Statistics
├── Random numbers
├── Linear algebra
└── SIMD
Now we're below the source-language surface.
Execution
├── Interpretation
├── Ahead-of-time compilation
├── Just-in-time compilation
├── Bytecode interpretation
├── Native compilation
├── Transpilation
├── Hybrid execution
└── Partial evaluation
Examples:
C++ usually AOT → native
Java source → bytecode → JVM/JIT
C# source → IL → CLR/JIT/AOT
Python source → bytecode → interpreter
JavaScript parsing + interpretation/JIT
Rust AOT → native
TypeScript transpiles → JavaScript
Runtime
├── Program startup
├── Memory allocation
├── Garbage collection
├── Stack management
├── Exception handling
├── Thread management
├── Dynamic dispatch
├── Type information
├── Reflection
├── Dynamic loading
├── JIT compilation
└── Shutdown
VM
├── Stack machine
├── Register machine
├── Bytecode
├── Bytecode verifier
├── Interpreter
├── JIT compiler
├── Garbage collector
├── Class/module loader
└── Runtime services
Examples include:
JVM
CLR
BEAM
WebAssembly runtimes
Lua VM
Python VM
Below APIs lies another crucial term:
ABI = Application Binary Interface
ABI
├── Calling convention
├── Register usage
├── Stack layout
├── Object layout
├── Symbol naming
├── Name mangling
├── Exception ABI
├── Binary formats
└── Linking conventions
API describes interaction at the source/programming level.
ABI describes compatibility at the binary level.
A compiler itself has a taxonomy.
Source Code
↓
Lexer
↓
Tokens
↓
Parser
↓
AST
↓
Semantic Analysis
↓
Typed IR
↓
Optimization
↓
Code Generation
↓
Object Code
↓
Linker
↓
Executable
Frontend
├── Lexing
├── Parsing
├── AST construction
├── Name resolution
├── Scope analysis
├── Type checking
├── Type inference
├── Overload resolution
├── Generic instantiation
├── Borrow/lifetime checking
└── Semantic analysis
IR
├── AST
├── High-level IR
├── SSA
├── Control-flow graph
├── Data-flow graph
├── Bytecode
├── Machine-independent IR
└── Machine IR
SSA means Static Single Assignment form and is extremely important in modern compilers.
Optimization
├── Constant folding
├── Constant propagation
├── Dead-code elimination
├── Common-subexpression elimination
├── Copy propagation
├── Inlining
├── Loop unrolling
├── Loop invariant code motion
├── Strength reduction
├── Tail-call optimization
├── Escape analysis
├── Devirtualization
├── Vectorization
├── Instruction scheduling
├── Register allocation
└── Link-time optimization
Backend
├── Instruction selection
├── Register allocation
├── Instruction scheduling
├── Machine-code emission
├── Object-file generation
├── Debug information
└── Relocations
Linking
├── Object files
├── Symbols
├── Symbol tables
├── Static linking
├── Dynamic linking
├── Shared libraries
├── Relocations
├── Symbol resolution
├── Name mangling
└── Link-time optimization
Then:
Loader
├── Map executable
├── Map libraries
├── Resolve dynamic symbols
├── Initialize runtime
└── Transfer control
A complete development environment extends much further.
Toolchain
├── Compiler
├── Interpreter
├── Assembler
├── Linker
├── Loader
├── Build system
├── Package manager
├── Dependency resolver
├── Debugger
├── Profiler
├── Formatter
├── Linter
├── Static analyzer
├── Documentation generator
├── Test runner
├── Benchmark runner
├── REPL
└── Language server
Build
├── Compilation units
├── Dependencies
├── Build graph
├── Incremental builds
├── Build caching
├── Configuration
├── Targets
├── Artifacts
├── Code generation
└── Cross-compilation
Examples:
C/C++ Make, CMake, Ninja, Meson, Bazel
Java Maven, Gradle
Rust Cargo
JavaScript npm ecosystem tooling
C# MSBuild/dotnet
Go go tooling
Package Management
├── Package
├── Package registry
├── Manifest
├── Dependencies
├── Transitive dependencies
├── Version constraints
├── Dependency resolution
├── Lockfile
├── Semantic versioning
├── Feature flags
├── Workspace
├── Publishing
└── Vendoring
Examples:
npm
Cargo
pip
Maven
NuGet
RubyGems
Composer
Go modules
Static Analysis
├── Type checking
├── Linting
├── Data-flow analysis
├── Control-flow analysis
├── Nullability analysis
├── Taint analysis
├── Escape analysis
├── Ownership analysis
├── Dead-code detection
├── Security analysis
└── Formal verification
"Static" means reasoning without executing the program in the ordinary way.
Dynamic Analysis
├── Debugging
├── Profiling
├── Tracing
├── Coverage
├── Sanitizers
├── Memory checking
├── Race detection
├── Runtime assertions
└── Instrumentation
Debugger
├── Breakpoints
├── Conditional breakpoints
├── Watchpoints
├── Stepping
├── Stack traces
├── Call stacks
├── Variable inspection
├── Memory inspection
├── Registers
├── Core dumps
└── Remote debugging
Performance
├── CPU profiling
├── Memory profiling
├── Allocation profiling
├── Sampling
├── Instrumentation
├── Flame graphs
├── Tracing
├── Benchmarking
├── Microbenchmarking
└── Hardware performance counters
Testing isn't usually part of the language itself, but is a major programming-platform concept.
Testing
├── Unit testing
├── Integration testing
├── System testing
├── Acceptance testing
├── Regression testing
├── Property-based testing
├── Fuzz testing
├── Mutation testing
├── Snapshot testing
├── Golden testing
├── Differential testing
├── Stress testing
├── Load testing
└── Benchmarking
Supporting concepts:
assertion
fixture
mock
stub
fake
spy
test double
coverage
REPL
= Read
→ Evaluate
→ Print
→ Loop
Common in:
Python
Lisp
Haskell
Julia
Ruby
JavaScript
Scala
Clojure
Closely related to notebooks and interactive shells.
Modern editor support often follows the Language Server Protocol model.
Language Intelligence
├── Autocomplete
├── Go to definition
├── Find references
├── Rename
├── Diagnostics
├── Hover information
├── Signature help
├── Refactoring
└── Semantic highlighting
Features aren't the same thing as paradigms.
A paradigm is a broad model/style for expressing computation.
Programming Paradigms
├── Imperative
├── Procedural
├── Structured
├── Object-oriented
├── Functional
├── Declarative
├── Logic
├── Dataflow
├── Reactive
├── Event-driven
├── Concurrent
├── Actor-oriented
├── Array-oriented
├── Stack-oriented
├── Aspect-oriented
├── Generic
├── Metaprogramming
└── Constraint programming
Most modern languages are multi-paradigm.
C++ is simultaneously:
imperative
procedural
object-oriented
generic
functional-ish
metaprogramming-oriented
systems-oriented
The program describes how state changes:
x = 1
x = x + 1
Core ideas:
state
mutation
assignment
sequence
loops
control flow
C, C++, Java, Python, etc. all support this.
Imperative programming organized primarily around procedures/functions:
data
↓
procedure()
↓
procedure()
↓
result
C is the classic example.
Organizes computation around interacting objects:
Object
├── state
└── behavior
Core concepts:
encapsulation
inheritance
polymorphism
dynamic dispatch
message passing
composition
But different OOP traditions emphasize different pieces. Smalltalk-style OOP and C++-style OOP aren't conceptually identical.
Organizes computation around function application and transformation of values.
input
↓
function
↓
value
↓
function
↓
output
Emphasizes:
immutability
pure functions
composition
higher-order functions
algebraic data types
pattern matching
Instead of saying:
Do X, then Y, then Z.
you primarily describe:
I want a result satisfying these properties.
Examples include:
SQL
logic programming
constraint programming
regular expressions
build systems
configuration languages
Classic example: Prolog.
Concepts:
facts
rules
queries
unification
backtracking
resolution
relations
Instead of explicitly implementing an algorithm, you define logical relationships.
Programs respond to changing values/events.
Reactive Programming
├── Event streams
├── Observables
├── Signals
├── Dataflow
├── Subscriptions
├── Backpressure
└── Propagation
Concurrency based around isolated actors:
Actor
├── private state
├── mailbox
└── behavior
↑
messages
Actors communicate via message passing rather than directly sharing memory.
Erlang/Elixir and Akka are prominent examples.
Now we reach concepts that explain why language features work.
Programming Language
├── Syntax
│ └── what programs look like
│
└── Semantics
└── what programs mean
Semantics itself includes:
Operational semantics
Denotational semantics
Axiomatic semantics
Static semantics
Dynamic semantics
Static semantics covers properties established before execution:
type correctness
name resolution
scope rules
generic constraints
Dynamic semantics covers what happens during execution:
evaluation
function calls
mutation
exceptions
object creation
The theoretical foundation becomes enormous:
Type Theory
├── Simply typed lambda calculus
├── Polymorphic lambda calculus
├── System F
├── Hindley-Milner
├── Subtyping
├── Algebraic data types
├── Recursive types
├── Existential types
├── Universal types
├── Dependent types
├── Linear types
├── Affine types
├── Refinement types
├── Intersection types
├── Union types
├── Effect systems
└── Kind systems
This is the theoretical backbone behind many modern language features.
A foundational model of computation.
A minimal lambda calculus essentially has:
variables
function abstraction
function application
For example:
λx.x
means roughly:
x => x
Many functional-programming concepts descend directly from lambda calculus.
Evaluation
├── Reduction
├── Normal form
├── Beta reduction
├── Alpha conversion
├── Substitution
├── Evaluation order
├── Normalization
└── Divergence
These become important when studying languages formally.
At an even deeper level:
Theory of Computation
├── Turing machines
├── Lambda calculus
├── Recursive functions
├── Turing completeness
├── Decidability
├── Undecidability
├── Halting problem
└── Computational complexity
Different formal models can describe equivalent classes of computation.
Formal Methods
├── Formal specification
├── Hoare logic
├── Preconditions
├── Postconditions
├── Invariants
├── Model checking
├── Theorem proving
├── Proof assistants
├── Refinement types
├── Dependent types
└── Program verification
Languages/tools such as Coq, Lean, Agda, Isabelle and Dafny live around this territory.
If your real objective is "I want to recognize the major ideas whenever I encounter a new language", I'd especially learn these families.
DATA
│
├── primitives
├── structs/records
├── tuples
├── arrays
├── collections
├── enums
├── sum types
└── product types
TYPES
│
├── static/dynamic
├── nominal/structural
├── inference
├── subtyping
├── generics
├── traits/interfaces
├── union/intersection types
├── nullability
└── dependent/refinement/linear types
CONTROL
│
├── conditionals
├── loops
├── recursion
├── pattern matching
├── exceptions
├── generators
├── coroutines
├── async/await
└── continuations
FUNCTIONS
│
├── functions
├── lambdas
├── closures
├── higher-order functions
├── currying
├── partial application
└── composition
ABSTRACTION
│
├── modules
├── namespaces
├── classes
├── interfaces
├── traits
├── protocols
├── generics
└── encapsulation
STATE & RESOURCES
│
├── mutation
├── references
├── pointers
├── ownership
├── borrowing
├── RAII
├── GC
└── immutability
CONCURRENCY
│
├── threads
├── locks
├── atomics
├── actors
├── channels
├── futures/promises
├── async/await
└── structured concurrency
METAPROGRAMMING
│
├── macros
├── reflection
├── templates
├── compile-time execution
├── code generation
└── type-level programming
Those are probably the highest-value branches of the tree.
A comparison makes the taxonomy much easier to internalize:
| Concept | C++ | Java | Python | Rust | Haskell | JavaScript |
|---|---|---|---|---|---|---|
| Static typing | ✓ | ✓ | —* | ✓ | ✓ | — |
| Dynamic typing | — | — | ✓ | — | — | ✓ |
| Type inference | ✓ | limited | runtime | ✓ | extensive | runtime |
| OOP | ✓ | ✓ | ✓ | traits/structs | limited | prototype-based |
| Functions | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| First-class functions | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Closures | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Generics | templates | ✓ | typing support | ✓ | parametric | — |
| Pattern matching | limited/evolving | evolving | ✓ | ✓ | ✓ | limited |
| ADTs | variant/enums |
sealed types | classes/unions | enum |
✓ | manual |
| Exceptions | ✓ | ✓ | ✓ | panic only† | exceptions/values | ✓ |
| Result types | library | library-ish | library/manual | ✓ | Either |
manual |
| GC | — | ✓ | ✓ | — | ✓ | ✓ |
| Ownership system | — | — | — | ✓ | — | — |
| RAII | ✓ | limited | context managers | ✓ | different model | — |
| Raw pointers | ✓ | — | — | unsafe |
— | — |
| Traits/interfaces | concepts | interfaces | protocols/duck | traits | type classes | structural/duck |
| Macros | preprocessor | limited | decorators/metaclasses | ✓ | extensions | — |
| Reflection | RTTI/limited | ✓ | ✓ | limited | limited | ✓ |
| Async | coroutines | ✓ | ✓ | ✓ | libraries/runtime | ✓ |
| Threads | ✓ | ✓ | ✓‡ | ✓ | ✓ | workers |
| Modules | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| Lazy evaluation | library | streams | generators | iterators | default | generators |
| Operator overloading | ✓ | mostly no | ✓ | traits | type classes | mostly no |
* Python has optional/static type annotations and external/static checkers, but runtime Python remains dynamically typed.
† Rust deliberately uses Result for ordinary recoverable errors rather than exception-style propagation.
‡ Python's concurrency situation depends heavily on implementation and workload.
The table is necessarily approximate because languages often implement the same concept in radically different ways.
When you encounter an unfamiliar term, one of the best questions is:
At what layer does this thing exist?
Consider:
SOURCE LANGUAGE
│
├── pattern matching
├── functions
├── classes
├── generics
└── async/await
│
▼
TYPE / STATIC SEMANTICS
│
├── type inference
├── overload resolution
├── borrow checking
└── effect checking
│
▼
STANDARD LIBRARY
│
├── strings
├── vectors
├── maps
├── algorithms
└── filesystem
│
▼
RUNTIME
│
├── garbage collector
├── allocator
├── exception runtime
├── scheduler
└── VM
│
▼
COMPILER / INTERPRETER
│
├── parser
├── optimizer
├── JIT
└── code generator
│
▼
ABI / MACHINE
│
├── calling conventions
├── registers
├── stack
├── machine instructions
└── memory
│
▼
OPERATING SYSTEM
│
├── processes
├── threads
├── virtual memory
├── files
└── sockets
│
▼
HARDWARE
This prevents a common learning problem: mixing concepts that live at completely different abstraction levels.
For example, for loop, iterator, thread, garbage collector, compiler, and CPU instruction are all relevant to executing a loop, but they are not the same kind of concept.
There's another distinction worth learning.
Suppose you write:
for x in items:
print(x)There are several layers involved:
Syntax / construct: for
Language protocol/mechanism: iteration protocol
Library/data abstraction: list
Runtime: Python interpreter
OS: eventually handles terminal output
So we could classify terminology as:
Construct
↓
Language mechanism
↓
Abstraction / API
↓
Runtime mechanism
↓
Implementation mechanism
Similarly, Rust:
for x in collection {
...
}involves the for language construct, iterator abstraction/protocol, Iterator trait, collection library type, compiler desugaring, and ultimately machine-level branching.
This distinction becomes extremely powerful once you're reading language specifications.
These words themselves have slightly different meanings.
A syntactically recognizable building block:
if
for
while
function declaration
class declaration
match
lambda
Broader term. Could include syntax or semantic capabilities:
generics
garbage collection
pattern matching
operator overloading
multiple inheritance
Describes how a capability is provided:
dynamic dispatch
reference counting
borrow checking
type erasure
monomorphization
Very general standards-language term:
language facility
library facility
concurrency facility
A conceptual interface that hides implementation details:
function
class
iterator
stream
future
collection
A conventional way of combining features:
RAII
dependency injection
builder pattern
Python context managers
A broad programming worldview:
OOP
functional
procedural
logic
reactive
These distinctions will make technical literature considerably easier to read.
If by "everything" you mean the entire territory surrounding programming, the tree keeps expanding beyond language design:
Programming
│
├── Programming Languages
│
├── Algorithms
│ ├── searching
│ ├── sorting
│ ├── graph algorithms
│ ├── dynamic programming
│ └── computational geometry
│
├── Data Structures
│
├── Software Design
│ ├── abstraction
│ ├── modularity
│ ├── coupling
│ ├── cohesion
│ ├── SOLID
│ └── design patterns
│
├── Software Architecture
│ ├── layered
│ ├── hexagonal
│ ├── event-driven
│ ├── microservices
│ └── distributed systems
│
├── Operating Systems
│ ├── processes
│ ├── threads
│ ├── scheduling
│ ├── memory
│ └── filesystems
│
├── Computer Architecture
│ ├── CPU
│ ├── ISA
│ ├── caches
│ ├── memory
│ └── pipelines
│
├── Databases
│
├── Networks
│
├── Distributed Systems
│
├── Security
│
├── Compilers
│
├── Formal Methods
│
└── Theory of Computation
At that point we've moved from a taxonomy of programming languages toward a taxonomy of computer science/software engineering.
If I wanted one map on my wall while learning programming languages, I'd probably reduce everything above to this:
PROGRAMMING LANGUAGE
│
├── 1. SYNTAX
│ ├── tokens
│ ├── grammar
│ ├── expressions
│ ├── statements
│ └── declarations
│
├── 2. DATA MODEL
│ ├── values
│ ├── objects
│ ├── primitives
│ ├── aggregates
│ ├── references
│ ├── mutability
│ └── identity/equality
│
├── 3. TYPE SYSTEM
│ ├── static/dynamic
│ ├── nominal/structural
│ ├── inference
│ ├── subtyping
│ ├── polymorphism
│ ├── generics
│ ├── ADTs
│ ├── traits/interfaces
│ ├── variance
│ └── advanced types
│
├── 4. CONTROL FLOW
│ ├── conditionals
│ ├── loops
│ ├── pattern matching
│ ├── recursion
│ ├── exceptions
│ ├── generators
│ ├── coroutines
│ └── continuations
│
├── 5. FUNCTIONS
│ ├── parameters
│ ├── lambdas
│ ├── closures
│ ├── higher-order functions
│ ├── overloading
│ ├── currying
│ └── composition
│
├── 6. ABSTRACTION
│ ├── procedures
│ ├── classes
│ ├── interfaces
│ ├── traits
│ ├── generics
│ ├── modules
│ └── packages
│
├── 7. STATE & MEMORY
│ ├── assignment
│ ├── references
│ ├── pointers
│ ├── stack/heap
│ ├── allocation
│ ├── GC
│ ├── ownership
│ ├── borrowing
│ └── lifetimes
│
├── 8. ERRORS & EFFECTS
│ ├── exceptions
│ ├── Result
│ ├── Option
│ ├── panic
│ ├── contracts
│ └── effect systems
│
├── 9. CONCURRENCY
│ ├── processes
│ ├── threads
│ ├── tasks
│ ├── locks
│ ├── atomics
│ ├── channels
│ ├── actors
│ ├── futures
│ └── async/await
│
├── 10. METAPROGRAMMING
│ ├── macros
│ ├── templates
│ ├── reflection
│ ├── compile-time evaluation
│ ├── type-level programming
│ └── code generation
│
├── 11. MODULE SYSTEM
│ ├── modules
│ ├── packages
│ ├── namespaces
│ ├── imports
│ ├── exports
│ └── visibility
│
├── 12. STANDARD LIBRARY
│ ├── collections
│ ├── algorithms
│ ├── strings
│ ├── numerics
│ ├── I/O
│ ├── filesystem
│ ├── networking
│ ├── concurrency
│ └── utilities
│
├── 13. RUNTIME
│ ├── allocator
│ ├── GC
│ ├── VM
│ ├── JIT
│ ├── exceptions
│ ├── scheduler
│ └── dynamic loading
│
├── 14. IMPLEMENTATION
│ ├── lexer
│ ├── parser
│ ├── AST
│ ├── semantic analysis
│ ├── type checker
│ ├── IR
│ ├── optimizer
│ ├── code generator
│ ├── assembler
│ ├── linker
│ └── loader
│
├── 15. TOOLING
│ ├── build system
│ ├── package manager
│ ├── debugger
│ ├── profiler
│ ├── formatter
│ ├── linter
│ ├── static analyzer
│ ├── test framework
│ ├── REPL
│ ├── IDE
│ └── language server
│
├── 16. PARADIGMS
│ ├── imperative
│ ├── procedural
│ ├── object-oriented
│ ├── functional
│ ├── declarative
│ ├── logic
│ ├── reactive
│ ├── actor
│ └── concurrent
│
└── 17. THEORY
├── formal grammars
├── semantics
├── lambda calculus
├── type theory
├── computability
├── formal verification
└── complexity
That's the mental model I'd recommend.
The particularly interesting realization is that C++, Java, Rust, Python, Haskell, JavaScript, etc. aren't collections of unrelated features. They are different sets of answers to essentially the same design questions:
What are values? How are values typed? How do we name them? How do we compose computations? How do we represent alternatives? How do we control execution? How do we abstract over types and behavior? How is state represented? Who owns memory/resources? How are errors represented? How do concurrent computations interact? What happens at compile time versus runtime? How are programs divided into components?
Once you start comparing languages along those axes, features such as C++ templates, Java generics, Rust traits, Haskell type classes, Python protocols, pattern matching, closures, coroutines, monads, ownership, etc. stop looking like an arbitrary vocabulary list. They become alternative solutions to recurring programming-language design problems.
