Files
freemo d9e5668cec fix(skills): comprehensive final audit pass for programming-patterns skill
Fixes and improvements from exhaustive audit:

Consistency fixes in SKILL.md:
- 'Pipe & Filter' → 'Pipe and Filter' (one stray '&' found and corrected)
- 'Singleton for factory instance' → clarified to 'register factory as
  singleton-scoped via DI container' (less misleading wording)
- Documentation Format section updated with note that SKILL.md itself is the
  authoritative source for related-pattern combinations

Coverage fix — Related Patterns sections:
- Added '## Related Patterns' to ALL 94 pattern files (was 0/94)
- Each section lists 3–6 related patterns with relationship descriptions
- Covers: why they're related, when to prefer one vs the other,
  and which are often confused

SOLID principles → Creational → Structural → Behavioral → Architectural →
Concurrency → Functional → Resilience → Data Access → Messaging →
Testing → Error Handling → Microservice — all 13 categories covered

Code verification:
- Python: 0 failures (all 85 testable blocks pass)
- Go: 0 failures (all 76 testable blocks pass)
- JavaScript: 0 failures (all 78 testable blocks pass)
- All 239 code blocks verified correct after edits

Final skill state:
- 108 files, 36,524 lines across 13 reference categories
- 94/94 pattern files have Related Patterns sections
- 2,815-line SKILL.md with 67 decision trees, 23 scenarios,
  0 broken references, 0 naming inconsistencies
2026-04-15 13:22:13 -04:00

10 KiB

Monad Pattern

Problem

Operations can fail (return null, throw exceptions) or produce optional values. Chaining such operations leads to deeply nested null checks or try/catch blocks, obscuring the actual logic.

Solution

Wrap values in a monadic context (like Maybe/Option or Result/Either) that carries success or failure information. Provide bind/flatMap/then operations that chain computations, automatically propagating the failure case without explicit checks at each step.

When to Use

  • Chaining operations where any step may fail
  • Replacing null checks with a type-safe alternative
  • Modeling computations with context (errors, async, optional values)
  • When you want explicit error handling without exceptions

When to Avoid

  • Simple code where null checks are trivial and clear
  • Languages with robust exception handling when exceptions are preferred
  • When the team finds monadic abstractions confusing (adoption cost)

Pseudocode

// Maybe monad
Maybe.of(value)           // wraps value in Just
Maybe.nothing()           // represents absence

just(5).map(x => x * 2)          // Just(10)
nothing().map(x => x * 2)        // Nothing

just(5).flatMap(x => just(x + 1))  // Just(6)
just(5).flatMap(x => nothing())     // Nothing

// Result monad
Ok(value).map(fn)         // Ok(fn(value))
Err(error).map(fn)        // Err(error) -- fn is not called

Ok(5).flatMap(x => Ok(x * 2))     // Ok(10)
Ok(5).flatMap(x => Err("fail"))   // Err("fail")

Python

from __future__ import annotations
from typing import TypeVar, Generic, Callable, Optional

T = TypeVar("T")
U = TypeVar("U")
E = TypeVar("E")

# --- Maybe Monad ---
class Maybe(Generic[T]):
    def __init__(self, value: Optional[T], is_nothing: bool = False):
        self._value = value
        self._is_nothing = is_nothing

    @staticmethod
    def just(value: T) -> Maybe[T]:
        return Maybe(value, is_nothing=False)

    @staticmethod
    def nothing() -> Maybe:
        return Maybe(None, is_nothing=True)

    def map(self, fn: Callable[[T], U]) -> Maybe[U]:
        if self._is_nothing:
            return Maybe.nothing()
        return Maybe.just(fn(self._value))

    def flat_map(self, fn: Callable[[T], Maybe[U]]) -> Maybe[U]:
        if self._is_nothing:
            return Maybe.nothing()
        return fn(self._value)

    def get_or_else(self, default: T) -> T:
        return default if self._is_nothing else self._value

    def __repr__(self) -> str:
        return "Nothing" if self._is_nothing else f"Just({self._value})"

# --- Result Monad ---
class Result(Generic[T, E]):
    def __init__(self, value: Optional[T], error: Optional[E], is_err: bool):
        self._value = value
        self._error = error
        self._is_err = is_err

    @staticmethod
    def ok(value: T) -> Result[T, E]:
        return Result(value, None, is_err=False)

    @staticmethod
    def err(error: E) -> Result[T, E]:
        return Result(None, error, is_err=True)

    def map(self, fn: Callable[[T], U]) -> Result[U, E]:
        if self._is_err:
            return Result.err(self._error)
        return Result.ok(fn(self._value))

    def flat_map(self, fn: Callable[[T], Result[U, E]]) -> Result[U, E]:
        if self._is_err:
            return Result.err(self._error)
        return fn(self._value)

    def get_or_else(self, default: T) -> T:
        return default if self._is_err else self._value

    def __repr__(self) -> str:
        return f"Err({self._error})" if self._is_err else f"Ok({self._value})"

def main() -> None:
    # Maybe examples
    print("=== Maybe Monad ===")
    val = Maybe.just(5).map(lambda x: x * 2).map(lambda x: x + 1)
    print(f"Just(5) -> *2 -> +1 = {val}")

    empty = Maybe.nothing().map(lambda x: x * 2)
    print(f"Nothing -> *2 = {empty}")

    chained = Maybe.just(10).flat_map(
        lambda x: Maybe.just(x // 2) if x > 0 else Maybe.nothing()
    )
    print(f"Just(10) -> flatMap(//2) = {chained}")

    print(f"Get or else: {Maybe.nothing().get_or_else(42)}")

    # Result examples
    print("\n=== Result Monad ===")
    def safe_divide(a: float, b: float) -> Result:
        if b == 0:
            return Result.err("Division by zero")
        return Result.ok(a / b)

    r1 = safe_divide(10, 2).map(lambda x: x * 3)
    print(f"10/2 * 3 = {r1}")

    r2 = safe_divide(10, 0).map(lambda x: x * 3)
    print(f"10/0 * 3 = {r2}")

    # Chain results
    r3 = safe_divide(100, 5).flat_map(lambda x: safe_divide(x, 4))
    print(f"100/5 then /4 = {r3}")

    r4 = safe_divide(100, 0).flat_map(lambda x: safe_divide(x, 4))
    print(f"100/0 then /4 = {r4}")

if __name__ == "__main__":
    main()

Go

package main

import "fmt"

// --- Maybe ---
type Maybe[T any] struct {
	value     T
	isNothing bool
}

func Just[T any](v T) Maybe[T] {
	return Maybe[T]{value: v, isNothing: false}
}

func Nothing[T any]() Maybe[T] {
	return Maybe[T]{isNothing: true}
}

func MapMaybe[T, U any](m Maybe[T], fn func(T) U) Maybe[U] {
	if m.isNothing {
		return Nothing[U]()
	}
	return Just(fn(m.value))
}

func FlatMapMaybe[T, U any](m Maybe[T], fn func(T) Maybe[U]) Maybe[U] {
	if m.isNothing {
		return Nothing[U]()
	}
	return fn(m.value)
}

func (m Maybe[T]) GetOrElse(def T) T {
	if m.isNothing {
		return def
	}
	return m.value
}

func (m Maybe[T]) String() string {
	if m.isNothing {
		return "Nothing"
	}
	return fmt.Sprintf("Just(%v)", m.value)
}

// --- Result ---
type Result[T any] struct {
	value T
	err   string
	isErr bool
}

func Ok[T any](v T) Result[T] {
	return Result[T]{value: v, isErr: false}
}

func Err[T any](e string) Result[T] {
	return Result[T]{err: e, isErr: true}
}

func MapResult[T, U any](r Result[T], fn func(T) U) Result[U] {
	if r.isErr {
		return Err[U](r.err)
	}
	return Ok(fn(r.value))
}

func FlatMapResult[T, U any](r Result[T], fn func(T) Result[U]) Result[U] {
	if r.isErr {
		return Err[U](r.err)
	}
	return fn(r.value)
}

func (r Result[T]) String() string {
	if r.isErr {
		return fmt.Sprintf("Err(%s)", r.err)
	}
	return fmt.Sprintf("Ok(%v)", r.value)
}

func safeDivide(a, b float64) Result[float64] {
	if b == 0 {
		return Err[float64]("division by zero")
	}
	return Ok(a / b)
}

func main() {
	// Maybe examples
	fmt.Println("=== Maybe Monad ===")
	val := MapMaybe(MapMaybe(Just(5), func(x int) int { return x * 2 }), func(x int) int { return x + 1 })
	fmt.Printf("Just(5) -> *2 -> +1 = %s\n", val)

	empty := MapMaybe(Nothing[int](), func(x int) int { return x * 2 })
	fmt.Printf("Nothing -> *2 = %s\n", empty)

	fmt.Printf("Get or else: %d\n", Nothing[int]().GetOrElse(42))

	// Result examples
	fmt.Println("\n=== Result Monad ===")
	r1 := MapResult(safeDivide(10, 2), func(x float64) float64 { return x * 3 })
	fmt.Printf("10/2 * 3 = %s\n", r1)

	r2 := MapResult(safeDivide(10, 0), func(x float64) float64 { return x * 3 })
	fmt.Printf("10/0 * 3 = %s\n", r2)

	r3 := FlatMapResult(safeDivide(100, 5), func(x float64) Result[float64] { return safeDivide(x, 4) })
	fmt.Printf("100/5 then /4 = %s\n", r3)

	r4 := FlatMapResult(safeDivide(100, 0), func(x float64) Result[float64] { return safeDivide(x, 4) })
	fmt.Printf("100/0 then /4 = %s\n", r4)
}

JavaScript

// --- Maybe Monad ---
class Maybe {
  constructor(value) {
    this._value = value;
    this._isNothing = value === null || value === undefined;
  }

  static just(value) {
    return new Maybe(value);
  }
  static nothing() {
    return new Maybe(null);
  }

  map(fn) {
    return this._isNothing ? Maybe.nothing() : Maybe.just(fn(this._value));
  }

  flatMap(fn) {
    return this._isNothing ? Maybe.nothing() : fn(this._value);
  }

  getOrElse(defaultValue) {
    return this._isNothing ? defaultValue : this._value;
  }

  toString() {
    return this._isNothing ? "Nothing" : `Just(${this._value})`;
  }
}

// --- Result Monad ---
class Result {
  constructor(value, error, isErr) {
    this._value = value;
    this._error = error;
    this._isErr = isErr;
  }

  static ok(value) {
    return new Result(value, null, false);
  }
  static err(error) {
    return new Result(null, error, true);
  }

  map(fn) {
    return this._isErr ? Result.err(this._error) : Result.ok(fn(this._value));
  }

  flatMap(fn) {
    return this._isErr ? Result.err(this._error) : fn(this._value);
  }

  getOrElse(defaultValue) {
    return this._isErr ? defaultValue : this._value;
  }

  toString() {
    return this._isErr ? `Err(${this._error})` : `Ok(${this._value})`;
  }
}

function safeDivide(a, b) {
  return b === 0 ? Result.err("Division by zero") : Result.ok(a / b);
}

function main() {
  // Maybe examples
  console.log("=== Maybe Monad ===");
  const val = Maybe.just(5)
    .map((x) => x * 2)
    .map((x) => x + 1);
  console.log(`Just(5) -> *2 -> +1 = ${val}`);

  const empty = Maybe.nothing().map((x) => x * 2);
  console.log(`Nothing -> *2 = ${empty}`);

  const chained = Maybe.just(10).flatMap((x) =>
    x > 0 ? Maybe.just(Math.floor(x / 2)) : Maybe.nothing()
  );
  console.log(`Just(10) -> flatMap(//2) = ${chained}`);

  console.log(`Get or else: ${Maybe.nothing().getOrElse(42)}`);

  // Result examples
  console.log("\n=== Result Monad ===");
  const r1 = safeDivide(10, 2).map((x) => x * 3);
  console.log(`10/2 * 3 = ${r1}`);

  const r2 = safeDivide(10, 0).map((x) => x * 3);
  console.log(`10/0 * 3 = ${r2}`);

  const r3 = safeDivide(100, 5).flatMap((x) => safeDivide(x, 4));
  console.log(`100/5 then /4 = ${r3}`);

  const r4 = safeDivide(100, 0).flatMap((x) => safeDivide(x, 4));
  console.log(`100/0 then /4 = ${r4}`);
}

main();
  • Result Type — Result/Either is the most common Monad in application code. Monad is the theoretical foundation; Result Type is the practical application.
  • Null Object — Both avoid null checks. Monad (Maybe/Option) chains computations that may return nothing; Null Object provides a do-nothing default.
  • Chain of Responsibility — Monadic chaining is conceptually similar: each step processes if the previous succeeded.
  • Guard Clause — Guard Clause at function entry + Result Type as return type is idiomatic fail-fast with Monadic propagation.
  • Higher-Order Functions — Monad's map and flatMap are higher-order functions that apply transformations inside the context.