Last updated: 2026-09-23
Polyglot Programming & Paradigm Shifting
Languages are ephemeral; paradigms are foundational. Learn only syntax and every new language is a fresh start; learn how memory, execution flow and state change are managed across paradigms and you can pick up a new language in a weekend — because you will recognise it as a new accent, not a new tongue.
The Meta-Skill of Language Translation
When you meet an unfamiliar language, interrogate it with the same six questions and map the answers onto what you already know. This approach — organising paradigms by the underlying dimensions they vary along (state, control flow, binding, and so on) rather than treating each language's syntax as sui generis — is the same one Van Roy and Haridi take at book length, building up every major paradigm from a shared kernel language by adding one concept at a time1: paradigms turn out to be points in a shared space, not an unrelated list to memorise separately.
| Question | What you're mapping | Example spread |
|---|---|---|
| Where do values live, and who frees them? | Memory model | GC heap (Java/Ruby) ↔ ownership (Rust) ↔ manual (C) |
| When is a name visible, and when does its value die? | Scope & lifetime | Lexical closures ↔ block scope ↔ dynamic scope |
| When are types checked, and how strong is the promise? | Type discipline | Static/nominal (Java) ↔ static/structural (Go, TS) ↔ dynamic/duck (Ruby, Python) |
| How does one piece of behaviour substitute for another? | Polymorphism | Subtyping ↔ interfaces/protocols ↔ higher-order functions ↔ generics |
| What changes, and who is allowed to change it? | State model | Mutable objects ↔ immutable values ↔ monadic effects |
| Who decides what runs next? | Control flow | Call stack ↔ event loop ↔ solver/backtracking |
The Paradigm Landscape
- Imperative & procedural. Step-by-step state manipulation through explicit instructions grouped into routines. The focus is how: sequence, selection, iteration. Every mainstream language contains this core, which is why it is taught first — and why its habits (shared mutable state everywhere) are what the other paradigms exist to discipline.
- Object-oriented. Encapsulating state and behaviour into cohesive objects that communicate — as the rest of this track explores — by message passing, with polymorphism and interface contracts enforcing structural invariants. The deep idea is less "classes" than protected state behind a behavioural boundary (see Event-Driven Programming for messaging taken to its logical conclusion) — classes are simply the dominant mechanism for building that boundary, not the only one; see Prototype-Based Programming for the delegation-based alternative used by Self and JavaScript.
- Functional. Computation as evaluation of pure functions; mutable state and side effects pushed to the edges. Higher-order functions are first-class citizens: functions consume and return functions. You already use this paradigm every time you write a
map/filterchain, a Ruby block, or a Java stream — the paradigm arrived inside the OO languages while nobody was watching. - Event-driven. Control flow inverted: the system idles until a message arrives — a click, a packet, a domain event — and routes it to a handler. Covered in depth on Event-Driven Programming, including what the inversion costs in traceability.
- Logic programming. Declare facts and rules about what's true, and let a resolution engine derive new facts by backward-chaining through them — Prolog's approach, formalised by Kowalski's insight that a restricted fragment of first-order logic (Horn clauses) could double as an executable programming language2. You're not writing a search; you're writing the space of facts the engine searches.
- Goal-oriented programming. A close cousin, easy to conflate with logic programming but answering a genuinely different question: not "is this true?" but "what sequence of actions gets me from here to a declared goal state?" Each action declares its own preconditions and effects, and a planner searches for an ordered, often cost-minimising, path from the current state to the goal — the STRIPS formalism from 1970s AI planning research3, repackaged for real-time NPC behaviour as GOAP and demonstrated as a runnable planner (alongside Prolog-style backward chaining) in PatLang's goal-oriented programming demo. A build tool like Make is the same idea wearing yet another disguise — declare a goal (a target file), declare what it depends on, and let the tool work backward from the goal to find which rules actually need to run; see below. See GOAP vs Rule-Based Reasoning for the same small domain solved both ways side by side, and for real problems each approach actually suits.
One Problem, Six Accents
Total the prices of in-stock items" — watch the state and control flow move between paradigms:
# Imperative: explicit accumulation, mutable loop state
total = 0
for item in items:
if item.in_stock:
total += item.price
# Object-oriented: the collection owns the behaviour
total = basket.total_in_stock() # state hidden behind the message
# Functional: expression over immutable data, no assignment
total = sum(i.price for i in items if i.in_stock)
# Event-driven: totals maintained by reacting to facts
bus.subscribe("item.stocked", lambda e: ledger.add(e.price))
bus.subscribe("item.sold_out", lambda e: ledger.remove(e.price))
% Logic: declare the relation; the engine finds the total
in_stock_price(P) :- item(I), in_stock(I), price(I, P).
total(T) :- findall(P, in_stock_price(P), Ps), sum_list(Ps, T).
# Goal-oriented (Make): declare the goal (an up-to-date total.txt) and
# what it depends on; make works backward from the goal to find which
# rules must fire, and in what order — you never write "run step 1, then
# step 2", only what each step needs and produces.
total.txt: in_stock_items.txt
awk '{s+=$$2} END {print s}' in_stock_items.txt > total.txt
in_stock_items.txt: items.txt
awk '$$3=="instock" {print $$1, $$2}' items.txt > in_stock_items.txt
Run make total.txt and Make doesn't execute a script from top to bottom — it starts from the goal, notices total.txt depends on in_stock_items.txt, notices that depends on items.txt, and only then decides which rules actually need to run and in what order. That's the same backward-from-the-goal shape as a GOAP planner searching from a desired world-state back to the current one, just applied to files and timestamps instead of a game character's preconditions and effects.
Choosing (and Composing) the Tool
Real architectures rarely stay inside one paradigm; they compose them. A typical modern service is an object-oriented shell (modules, dependency boundaries, SOLID discipline) around functional stream processing (pure transformations over immutable events), driven by an event loop, with a declarative configuration layer on top. The skill is not picking a winner — it is noticing which paradigm's guarantees each layer of the problem needs: auditability wants immutable events; complex domain invariants want encapsulating objects; concurrency wants purity; interaction wants events.
The practical advice: learn one language from an unfamiliar paradigm properly — far enough to feel its idioms stop being weird. Each paradigm permanently adds a lens, and code in your home language improves because you now see which of its features are borrowed lenses too.
Related Topics
- Event-Driven Programming — takes this page's event-driven row and follows the messaging idea to its logical conclusion, from method call to message bus.
- Prototype-Based Programming: Objects Without Classes — the delegation-based alternative to class-based OO mentioned here, worked through in full.
- Goal-Oriented Action Planning — the STRIPS-derived planning idea introduced here, repackaged for real-time NPC behaviour.
- GOAP vs Rule-Based Reasoning — the same small domain solved with both the goal-oriented and logic-programming paradigms named on this page, compared side by side.
References
Van Roy, P., & Haridi, S. (2004). Concepts, Techniques, and Models of Computer Programming. MIT Press. ↩
Kowalski, R. (1974). Predicate logic as a programming language. Proceedings of IFIP Congress 1974, Stockholm. North-Holland, 569–574. ↩
Fikes, R. E., & Nilsson, N. J. (1971). STRIPS: A new approach to the application of theorem proving to problem solving. Artificial Intelligence, 2(3–4), 189–208. https://doi.org/10.1016/0004-3702(71)90010-5 ↩