Paradigms
A paradigm is a way of expressing computation, not a category a language belongs to. Nearly every language in this atlas supports several; what distinguishes them is which paradigm the idiomatic style and the standard library assume.
Paradigm groups¶
Six groups, each with the trade-off it makes. Listed languages are those whose idiomatic style leans on the paradigm, not the complete set that permits it.
| Paradigm | Model of computation | Representative languages | What it buys | What it costs |
|---|---|---|---|---|
| Imperative | Computation is a sequence of statements that change program state. Control flow is explicit; the machine model is visible. | Fortran, C, Pascal, Go | Direct mapping to hardware; predictable cost | State is global by default, making local reasoning harder |
| Object-oriented | State and the operations on it are bundled into objects that interact by method call or message send. Behaviour is selected by the receiver's type. | Smalltalk, Java, C#, Ruby | Encapsulation and substitutability across large teams | Inheritance hierarchies couple types that need not be related |
| Functional | Computation is the application of functions to values. Functions are first-class, and data is preferably immutable. | Haskell, OCaml, Clojure, F# | Referential transparency makes code easy to test and parallelise | Persistent data structures add allocation and indirection |
| Declarative / logic | The program states what must hold rather than how to compute it; a solver or planner supplies the strategy. | Prolog, SQL, Datalog | Concise problem statements; the engine may optimise freely | Performance depends on an optimiser the author cannot always see |
| Generic / metaprogramming | Code is written against type parameters or generated at compile time, so one definition serves many concrete types. | C++, Rust, Zig, Scala | Abstraction with no runtime dispatch cost | Error messages and compile times degrade with template depth |
| Concurrent / dataflow | The unit of composition is an independently executing process or stream, connected by messages or channels rather than shared memory. | Erlang, Elixir, Go, Swift | Failure and load can be isolated per process | Message protocols become an unchecked interface unless typed |
Sources: language specifications and reference documentation. Trade-off columns summarise widely documented consequences and are not survey results.
Convergence¶
Paradigms are no longer a partition
Between 1990 and 2010 the mainstream languages absorbed features that had been considered functional research: lexical closures reached Java in 2014 and C++ in 2011, algebraic data types and exhaustive pattern matching reached C#, Java, Python, and Swift in the following decade.
The same happened in the other direction. Value semantics, once a systems-language concern, became a first-class design point in Swift, and ownership discipline moved from research languages into Rust and then, in narrower form, into C++ and Swift.
The practical consequence is that paradigm is now a weak predictor of what a language can express, and a strong predictor of what its standard library and community consider idiomatic. This atlas therefore records paradigm emphasis rather than a single paradigm label.
Concurrency models¶
Concurrency is treated as a separate axis because a language's concurrency model is often chosen independently of its paradigm emphasis.
| Model | Mechanism | Representative languages | Principal failure mode |
|---|---|---|---|
| Threads and locks | OS threads over shared mutable memory, guarded by mutexes and condition variables. | C, C++, Java, C# | Data races and deadlocks are the programmer's responsibility |
| Actors | Independent processes owning private state, communicating only by asynchronous message. | Erlang, Elixir, Swift, Akka (Scala) | Mailbox growth and message ordering become the failure modes |
| CSP channels | Lightweight processes passing values through typed channels; the channel, not the data, is shared. | Go, occam, Clojure core.async | Channel topology can deadlock as easily as locks |
| Async/await | Suspension points marked in the source; a scheduler interleaves tasks on a small thread pool. | JavaScript, Python, C#, Rust, Swift | Function colouring splits the library ecosystem in two |
| Data parallel | One operation applied across a whole array or collection, with the runtime choosing the split. | Fortran, APL, Julia, CUDA C++ | Only fits problems that are naturally uniform |
Languages frequently support more than one model; the entry lists the model its standard library makes primary. See the Swift article for a worked example combining async/await with actors.