Overview of Programming Languages
Introduction
Programming languages serve as the bridge between humans and computers. From the earliest machine code to modern high-level languages, the evolution of programming languages reflects the ongoing pursuit of abstraction, expressiveness, and productivity. This article provides a comprehensive survey across four dimensions: programming paradigms, type systems, execution models, and memory management.
1. Programming Paradigms
A programming paradigm is the design philosophy and organizational approach of a programming language.
1.1 Imperative Programming
Imperative programming accomplishes computation by executing a sequence of statements that modify program state.
Core concepts:
- Variables: mutable storage locations
- Assignment: changing the value of a variable
- Control flow: sequential execution, branching (if/switch), loops (for/while)
// C language example
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
Representative languages: C, Pascal, Fortran.
1.2 Object-Oriented Programming (OOP)
OOP centers on objects, encapsulating data and operations together.
Four principles:
| Principle | Meaning | Purpose |
|---|---|---|
| Encapsulation | Hiding internal implementation | Reducing coupling |
| Inheritance | Subclass reuses superclass code | Code reuse |
| Polymorphism | Same interface, different implementations | Flexible extension |
| Abstraction | Extracting commonalities, ignoring details | Simplified modeling |
class Animal:
def speak(self):
raise NotImplementedError
class Dog(Animal):
def speak(self):
return "Woof!"
class Cat(Animal):
def speak(self):
return "Meow!"
# Polymorphism
for animal in [Dog(), Cat()]:
print(animal.speak())
Representative languages: Java, C++, Python, C#.
1.3 Functional Programming
Functional programming treats computation as the evaluation of mathematical functions, emphasizing immutability and freedom from side effects.
Core concepts:
- Pure functions: the same input always produces the same output, with no side effects
- Immutable data: data cannot be modified once created
- Higher-order functions: functions can be passed as arguments or returned
- Recursion: using recursion instead of loops
- Lazy evaluation: delaying computation until results are needed
-- Haskell example: factorial
factorial :: Integer -> Integer
factorial 0 = 1
factorial n = n * factorial (n - 1)
-- Higher-order function: map
doubleAll = map (*2)
-- doubleAll [1,2,3] = [2,4,6]
Representative languages: Haskell, Erlang, Clojure, Scala.
1.4 Logic Programming
Logic programming accomplishes computation by declaring facts and rules, with an inference engine automatically deriving solutions.
% Prolog example
parent(tom, bob).
parent(tom, liz).
parent(bob, ann).
grandparent(X, Z) :- parent(X, Y), parent(Y, Z).
% ?- grandparent(tom, ann). => true
Representative languages: Prolog, Datalog.
1.5 Paradigm Family Tree
graph TD
A[Programming Paradigms] --> B[Declarative]
A --> C[Imperative]
B --> D[Functional]
B --> E[Logic]
B --> F[Dataflow]
C --> G[Procedural]
C --> H[Object-Oriented OOP]
D --> D1[Haskell / Erlang / Clojure]
E --> E1[Prolog / Datalog]
G --> G1[C / Pascal / Fortran]
H --> H1[Java / C++ / Python]
style A fill:#f9f,stroke:#333
style B fill:#bbf,stroke:#333
style C fill:#bfb,stroke:#333
2. Type Systems
A type system is a set of rules in a programming language that constrains the kinds of values and their permissible operations.
2.1 Static Typing vs Dynamic Typing
| Dimension | Static Typing | Dynamic Typing |
|---|---|---|
| When types are checked | Compile time | Runtime |
| Type declarations | Usually required (can be inferred) | Not required |
| Error detection | At compile time | At runtime |
| Representative languages | Java, C++, Rust, Go | Python, JS, Ruby |
| Advantages | Early error detection, performance optimization | Flexibility, faster development |
2.2 Strong Typing vs Weak Typing
| Dimension | Strong Typing | Weak Typing |
|---|---|---|
| Implicit conversion | Disallowed or strictly limited | Widely permitted |
| Representative languages | Python, Java, Haskell | C, JavaScript, PHP |
// JavaScript (weak typing) implicit conversion
"5" + 3 // "53" (string concatenation)
"5" - 3 // 2 (numeric operation)
# Python (strong typing) raises an error
"5" + 3 # TypeError
2.3 Type Inference
Modern languages reduce explicit type annotations through type inference:
- Hindley-Milner type inference: Haskell, ML family
- Local type inference: Java (
var), C++ (auto), Rust, Go
// Rust type inference
let x = 5; // inferred as i32
let y = 3.14; // inferred as f64
let v = vec![1,2,3]; // inferred as Vec<i32>
3. Compilation and Interpretation
3.1 Compiled Languages
Source code → Compiler → Machine code → Direct execution
- Advantages: fast execution, deep optimization possible
- Disadvantages: compilation takes time, platform-dependent
- Representatives: C, C++, Rust, Go
3.2 Interpreted Languages
Source code → Interpreter → Line-by-line / statement-by-statement execution
- Advantages: fast development, cross-platform
- Disadvantages: slower execution
- Representatives: Python, Ruby, JavaScript (early versions)
3.3 Bytecode + Virtual Machine
Source code → Compiler → Bytecode → Virtual Machine (VM) → Execution
- Java → JVM bytecode → JVM
- Python → .pyc bytecode → CPython VM
- C# → IL bytecode → CLR
3.4 JIT Compilation
Just-In-Time compilation combines the advantages of compilation and interpretation:
- Initially interpret (or execute bytecode)
- Identify hot spots
- Compile hot code to machine code
- Execute machine code directly thereafter
Representatives: JVM HotSpot, V8 (JavaScript), PyPy.
4. Memory Management
4.1 Manual Memory Management
The programmer is responsible for allocating and freeing memory:
int *arr = (int *)malloc(100 * sizeof(int));
// use arr
free(arr); // must be freed manually
Problems: memory leaks, dangling pointers, double frees.
4.2 Garbage Collection
Automatically reclaims memory that is no longer in use.
Reference Counting:
- Each object maintains a reference count
- Reclaimed immediately when the count reaches zero
- Problem: circular references
- Used by: Python (combined with mark-and-sweep), Swift (ARC)
Mark and Sweep:
- Starting from root objects, mark all reachable objects
- Sweep unmarked objects - Used by: Java, Go, JavaScript
Generational GC:
- Hypothesis: most objects are short-lived (weak generational hypothesis)
- Young generation: frequently collected (Minor GC)
- Old generation: occasionally collected (Major GC)
- Used by: Java (G1, ZGC), Python
4.3 Ownership System
Rust's unique approach:
fn main() {
let s1 = String::from("hello");
let s2 = s1; // ownership of s1 moves to s2
// println!("{}", s1); // compile error! s1 is no longer valid
println!("{}", s2); // OK
} // s2 goes out of scope, memory is automatically freed
Core rules:
- Each value has exactly one owner
- When the owner goes out of scope, the value is dropped
- Borrowing (reference) rules ensure safety
5. Paradigm Comparison
| Dimension | Imperative | OOP | Functional | Logic |
|---|---|---|---|---|
| Core abstraction | Statement sequence | Objects | Functions | Logical relations |
| State | Mutable | Mutable (encapsulated) | Immutable | Declarative |
| Control flow | Explicit | Message passing | Recursion/composition | Inference engine |
| Concurrency friendliness | Low | Medium | High | High |
| Typical applications | Systems programming | Enterprise apps | Data processing | AI reasoning |
| Learning curve | Low | Medium | High | High |
6. Modern Language Trends
6.1 Multi-Paradigm Fusion
Modern languages commonly support multiple paradigms:
- Python: imperative + OOP + functional
- Scala: OOP + functional
- Rust: imperative + functional + ownership
- Kotlin: OOP + functional + coroutines
6.2 Other Trends
- Type safety: TypeScript, Rust, etc. emphasize type safety
- Concurrency primitives: Go (goroutines), Rust (async/await), Erlang (actors)
- Zero-cost abstractions: C++, Rust pursue abstractions without runtime overhead
- Domain-specific languages (DSLs): SQL, regular expressions, Terraform HCL
References
- "Programming Language Pragmatics" - Michael L. Scott
- "Types and Programming Languages" - Benjamin C. Pierce
- "Structure and Interpretation of Computer Programs" - Abelson & Sussman