Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

HowProgrammingWorks/Paradigms

Repository files navigation

Programming Paradigms Comparison

Paradigms

  • Imperative Programming
    • Characteristics: statements mutate program state directly (let, loops, assignments)
    • Architecture Influence: stateful services, command-driven architecture
  • Procedural Programming
    • Characteristics: step-by-step, linear control flow, mutable state
    • Architecture Influence: transaction script, single responsibility units, shallow call graphs
  • Object-Oriented Programming
    • Characteristics: classes/objects, encapsulation, inheritance, polymorphism
    • Architecture Influence: DDD, layered architecture, clean architecture, hexagonal
  • Prototype-based Programming
    • Characteristics: objects inherit from other objects, delegation over inheritance
    • Architecture Influence: flexible object composition, dynamic plugin systems
  • Functional Programming
    • Characteristics: pure functions, immutability, no shared state, higher-order functions, pattern matching, curry, single argument functions, composition
    • Architecture Influence: functional pipelines, stateless services, async stream processing
  • Closure-based Programming
    • Characteristics: functions hold private state via closures, encapsulated behavior
    • Architecture Influence: lightweight encapsulation, reactive units, factory patterns
  • Actor Model
    • Characteristics: message passing, no shared memory, isolated context, transparent concurrency units
    • Architecture Influence: distributed systems, concurrency-safe services, microservices
  • Structural Programming
    • Blocks, No goto
  • Declarative Programming
    • Characteristics: emphasizes what over how, side-effect free, high-level code, DSLs, self-descriptive
    • Architecture Influence: configuration-over-code, rule engines
  • Contract programming
    • Characteristics: difine contracts
    • Architecture Influence: stable and clear, self-descriptive
  • Reactive Programming
    • Characteristics: observable streams, event-driven, push-based, backpressure-aware
    • Architecture Influence: UIs, data stream processing, feedback loops, real-time pipelines
  • Finite-State Machines / Automata
    • Characteristics: explicit states, transitions, events, deterministic flow
    • Architecture Influence: workflow engines
  • Metaprogramming
    • Characteristics: code generation, reflection, macros, introspection
    • Architecture Influence: high flexibility, scaffolding

Basic Ideas

  1. Control Flow
  • Statements, algorithm steps
    const user = read({ id: 15 });
    if (user.name === 'marcus') {
     console.log(user.age);
    }
  • Expression
    ({ id }) => (fn) => fn({ id, name: 'marcus', age: 42 })
     ({ id: 15 })(({ name, age }) => name === 'marcus' ? (log) => log(age) : () => {})
     (console.log);
  • Do-notation
    Do({ id: 15 })
     .chain(({ id }) => ({ id, name: 'marcus', age: 42 }))
     .chain(({ name, age }) => name === 'marcus' ? (log) => log(age) : () => {})
     .run()(console.log);
  • Declarative style
    execute({
     read: { id: 15 },
     then: {
     match: { name: 'marcus' },
     success: { effect: { log: 'age' } },
     fail: { effect: 'noop' },
     },
    })(reader, console.log)();
  • Pipeline operator
    (({ id: 15 })
     |> read
     |> (({ name, age }) => name === 'marcus' ? (log) => log(age) : () => {})
    )(console.log);
  • Pipe (composition)
    pipe(
     { id: 15 },
     read,
     ({ name, age }) => name === 'marcus' ? (log) => log(age) : () => {}
    )(console.log);
  1. Identifiers
  • Assignment statement
    let a = 10; const b = 20; a = a + b;
  • Call arguments
    ((a, b) => a + b)(5, 3);
  • Only callable
    const a = () => 10;
    const b = () => 20;
    const sum = (x, y) => () => x() + y();
    const c = sum(a, b);
  1. State
  • Mutable state and form
    const counter = { value: 0 };
    counter.value += 1;
  • Mutable form
    counter.ready = true;
  • Immutable state
    const point = Object.freeze({ x: 10, y: 20 });
    const move = (p) => ({ x, y }) => ({ x: p.x + x, y: p.y + y });
    const moved = move(point)({ x: 3, y: 7 });
  • Copy-on-write
    const p1 = { x: 10, y: 20 };
    const p2 = Object.create(p1);
    p2.x = 7;
  • Stateless functions
    const twice = (x) => x * 2;
  1. Context
  • Objects
    const point = { x: 10, y: 20, move(dx, dy) { this.x += dx; this.y += dy; } };
  • Records
    const point = { x: 10, y: 20 };
    const move = (p, d) => { p.x += d.x; p.y += d.y; };
  • Closures
    const createCounter = (count = 0) => () => ++count;
  • Boxing
    const primitive = 42;
    const instance = new Number(42);
    const boxed = Box.of(42);
  • Containers
    Box.of(42); Either.right(42); Promise.resolve(42);
    let maybe: number | null = 42; type Pair = { a?: number; b?: number };
    type Option<T> = { kind: 'some'; value: T } | { kind: 'none' };
    std::optional<int>; std::tuple<int>; std::reference_wrapper<int>;
    Nullable<int> maybe = 42; new StrongBox<int>(value); Tuple.Create(myIntValue);
  • Modules
    const cache = new Map();
    export const get = (key) => cache.get(key);
    export const set = (key, value) => cache.set(key, value);
  1. Branching
  • Conditional statement
    if (x > 0) {
     console.log('positive');
    } else if (x < 0) {
     console.log('negative');
    } else {
     console.log('zero');
    }
  • Conditional expression
    const sign = x > 0 ? 'positive' : x < 0 ? 'negative' : 'zero';
  • Guards
    const process = (x) => {
     if (x === null) return null;
     if (x < 0) return null;
     return x * 2;
    };
    func process(_ x: Int?) -> Int? {
     guard let v = x else { return nil }
     guard v >= 0 else { return nil }
     return v * 2
    }
  • Pattern matching
    fn process(x: Option<i32>) -> Option<i32> {
     match x {
     None => None,
     Some(v) if v < 0 => None,
     Some(v) => Some(v * 2),
     }
    }
    const match = (variant, handlers) => handlers[variant.tag](variant);
    match({ tag: 'point', x: 10, y: 20 }, {
     point: ({ x, y }) => `(${x}, ${y})`,
     circle: ({ r }) => `radius: ${r}`
    });
  1. Iteration
  • Loops (for, while, do)
    for (let i = 0; i < 10; i++) console.log(i);
    while (condition) { /* steps */ }
    do { /* steps */ } while (condition);
  • Recursion calls (incl. tail recursion)
    const factorial = (n) => n <= 1 ? 1 : n * factorial(n - 1);
    const tailFact = (n, acc = 1) => n <= 1 ? acc : tailFact(n - 1, n * acc);
  • Iterators / Generators
    function* range(start, end) {
     for (let i = start; i < end; i++) yield i;
    }
    for (const n of range(0, 5)) console.log(n);
  • Streams
    const res = await fetch('/api/endpoint');
    for await (const chunk of res.body) console.log(new TextDecoder().decode(chunk));
    For Node.js
    const r = Readable.from(gen());
  1. Instantiation
  • Operator new
    const point = new Point(10, 20);
  • Creational patterns like Factory, Builder
    const p = Point.create(10, 20);
    const q = await Query.select('cities').where({ country: 10 }).order('population');
  • Closures
    const p = createPoint(10, 20);
    const q = await select('cities').where({ country: 10 }).order('population');
  • Containers
    class Maybe<T = unknown> {
     constructor(value?: T);
     get value(): T | undefined;
     isEmpty(): boolean;
     match<R>(
     some: (value: T) => R,
     none: () => R
     ): R;
    }
  • Cloning
    const clone1 = { ...original };
    const clone2 = Object.assign({}, original);
    const clone3 = structuredClone(original);
  • Pattern GOF:Flyweight
  1. Inheritance
  • Classes
    class SavingAccount extends Account
  • Interfaces (implements)
    class Cursor implements Iterator<Account>
  • Prototype programming
    const logger = Object.create(console, { log: { value: (s) => process.write(s) } });
  • Mixins
    const logger = {};
    logger.log = console.log;
    Object.assign(logger, { info: () => {} });
  • Structural composition
    class Logger {
     constructor(name) {
     this.stream = fs.createWriteStream(name);
     }
    }
  • Partial/Curry
    const add = (a, b) => a + b;
    { const add5 = (b) => add(5, b); }
    const curriedAdd = (a) => (b) => a + b;
    { const add5 = curriedAdd(5); }
    { const add5 = add.bind(add, null, 5); }
  • Traits
    pub trait ToJson {
     fn to_json(&self) -> String;
    }
    pub struct User {
     pub id: u32,
     pub name: String,
    }
    impl ToJson for User {
     fn to_json(&self) -> String {
     format!(r#"{{"id":{},"name":"{}"}}"#, self.id, self.name)
     }
    }
    TypeScript alternative
    interface ToJson {
     toJson: () => string
    }
    class User implements ToJson {
     readonly id: number
     readonly name: string
     constructor(id: number, name: string) {
     this.id = id
     this.name = name
     }
     toJson = (): string => {
     return `{"id":${this.id},"name":"${this.name}"}`
     }
    }
  1. Primitive values
  • Scalars
    const number = 42;
  • Boxing
    const number = 42;
    const boxed = Object(number);
    const unboxed = Number(boxed);
  • Value Objects
    class Integer {
     private readonly value: number;
     // implement constructor and math operations
    }
    const a = new Integer(7);
    const b = new Integer(3);
    const c = a.add(b);
  • Containers
  1. Asynchronity
  • Callback
    const fetchData = (callback) => setTimeout(() => callback('data'), 100);
    fetchData((result) => console.log(result));
  • Promise
    const fetchData = () => Promise.resolve('data');
    fetchData().then((result) => console.log(result));
  • Async/await
    const data = await fetchData();
  • Future, Task
    const fs = require('node:fs');
    const futurify = (fn) => (...args) => new Future((reject, resolve) =>
     fn(...args, (error, result) => error ? reject(error) : resolve(result)),
    );
    const readFuture = futurify(fs.readFile);
    const writeFuture = futurify(fs.writeFile);
    readFuture('future.js', 'utf8')
     .map((text) => text.toUpperCase())
     .chain((text) => writeFuture('future.md', text))
     .fork(
     (error) => console.error('FS error:', error),
     () => console.log('Done'),
     );
  • Async compose
    const prepareReport = pipe(read, parse, calculate, render);
    const checks = parallel(checkBalance, checkAvailability, checkFraud);
  • Observer (EventEmitter, EventTarget, Signal)
  • Streams and other abstractions
  1. Purity
  • Pure functions
    const add = (a, b) => a + b;
    const square = (x) => x * x;
  • Functions with side effects
    let counter = 0;
    const increment = () => ++counter;
  • IO monads
  1. First-class citizens
  • Higher-order functions
  • Passing behaviour as object
  1. Evaluation Flow
  • Function composition
  • Nested calls
  • Pipeline |>
    const result = input
     |> validate
     |> transform
     |> process;
  1. Point style
  • Point-free style: const f = compose(g, h)
  • Point style: const f = (x) => g(h(x))
  1. Dependencies
  • Pass all dependencies as arguments
    const createService = (db, logger) => { /* implementation */ };
  • Dependency injection
    class Service {
     constructor(db, logger) {
     this.db = db;
     this.logger = logger;
     }
    }
  • Global namespaces
    const user = application.auth.getUser('marcus');
  • Service locators
    const ServiceLocator = {
     services: {},
     register(name, service) { this.services[name] = service; },
     get(name) { return this.services[name]; }
    };
  • Module systems
  1. Structural Control (see Branching)
  • Nested if/else conditionals
    if (x > 0) {
     if (x > 10) {
     console.log('large');
     } else {
     console.log('small');
     }
    } else {
     console.log('negative');
    }
  • Pattern matching
  • Guards
  1. Error handling
  • Total functions
    const safeDivide = (a, b) => a / b;
  • Throwing exceptions
    const divide = (a, b) => {
     if (b === 0) throw new Error('Division by zero');
     return a / b;
    };
  • Error codes
  • Return null, undefined, NaN
  • Null objects
    const nullUser = {
     name: 'Guest',
     isAuthenticated: false,
     login: () => {}
    };
    const user = getUser() ?? nullUser;
  • Option / Either / Result / Promise
    class Either<L = unknown, R = unknown> {
     constructor(params: { left?: L | null; right?: R | null });
     static left<L>(value: L): Either<L, null>;
     static right<R>(value: R): Either<null, R>;
     get left(): L | null;
     get right(): R | null;
     isLeft(): boolean;
     isRight(): boolean;
     map<U>(fn: (value: R) => U): Either<L, U>;
     match<T>(
     leftFn: (left: L) => T,
     rightFn: (right: R) => T
     ): T;
    }
  1. Stability of Effects
  • Idempotent operations new Set().add(3).add(3)
  • Order-sensitive operations ((a) => (a.push(3), a.push(7), a))([])
  • Commutative / associative operations add(a, b) === add(b, a)
  1. Semantic Transparency
  • Non-Referential transparent
    class Counter {
     #value = 0;
     inc = () => (++this.#value);
    }
  • Referential transparency
    class Counter {
     #value;
     constructor(value = 0) { this.#value = value; }
     inc = () => new Counter(this.#value + 1);
     value = () => this.#value;
    }
  • Equational reasoning
    const f = (x) => x * 2;
    const g = (x) => x + 1;
    // f(g(x)) === (x + 1) * 2 === 2x + 2
    // Can reason about code as equations
  • Deterministic evaluation
    const pure = (x) => x * 2;
    const impure = () => Math.random();
  • No hidden state
  1. Caching
  • Hash-table caching
    const cache = new Map();
    const get = (key) => cache.get(key);
    const set = (key, value) => cache.set(key, value);
  • Memoization
    const fib = memoize((n) => n <= 1 ? n : fib(n - 1) + fib(n - 2));
  1. Resource Control
  • Manual destruction
    class Resource {
     constructor() { this.handle = acquire(); }
     dispose() { release(this.handle); }
    }
    const resource = new Resource();
    try {
     // use resource
    } finally {
     resource.dispose();
    }
  • RAII / disposables
    const using = (resource, fn) => {
     try {
     return fn(resource);
     } finally {
     resource.dispose();
     }
    };
    New JavaScript Disposables: Symbol.dispose, Symbol.asyncDispose
    const main = async () => {
     await using logger = await new Logger('output.log');
     await logger.log('Open');
     await logger.log('Do something');
    };
  • Region-based allocation
    const withRegion = (fn) => {
     const region = [];
     try {
     return fn(region);
     } finally {
     region.forEach(cleanup);
     }
    };
  • Ownership allocate/free
  1. Concurrency Model
  • Shared-memory concurrency
  • Stateless functions
  • Message passing (Actor model)
  • Transactional memory
  • Locking, Semaphor, Mutex
  1. Data Ownership
  • Copy semantics
  • Move semantics
  • Shared vs exclusive references
  1. Granularity
  • One-liners
  • Long code blocks
  • Moderate granularity

Memes

const getTomorrowDate = () => {
 const timeout = 86400000;
 return new Promise((resolve) => {
 setTimeout(() => {
 resolve(new Date());
 }, timeout)
 });
};
const Coin = (v) => ({
 map: (f) => Coin(f(v))
});
const flip = () =>
 crypto.getRandomValues(new Uint8Array(1))[0];
Coin(flip())
 .map((r) => (r & 1 ? 'πŸͺ™' : 'πŸ’©'))
 .map(console.log);
const findMeaningOfLife = () => {
 const offset = 0;
 const delay = Infinity;
 return new Promise((resolve) => {
 setTimeout(() => {
 resolve(42 + offset);
 }, delay);
 });
};
class Coming {
 constructor() {
 return new Promise((resolve) => 
 setTimeout(() => {
 resolve(this);
 }, DAY_OF_JUDGMENT - Date.now())
 );
 }
}
const secondComing = await new Coming();
(
 (
 <F extends () => void>(
 Function: F = {} as F
 ) => Function()
 )()
)
class Future {
 constructor() {
 const { name: key } = this.constructor;
 const value = void [].length;
 throw new Error(`${key} is ${value}`);
 }
}
new Future();

About

Programming Paradigms Comparison

Resources

License

Stars

Watchers

Forks

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /