functional

8
3
Source

Functional programming patterns with immutable data. Use when writing logic or data transformations.

Install

mkdir -p .claude/skills/functional && curl -L -o skill.zip "https://mcp.directory/api/skills/download/3587" && unzip -o skill.zip -d .claude/skills/functional && rm skill.zip

Installs to .claude/skills/functional

About this skill

Functional Patterns

Core Principles

  • No data mutation - immutable structures only
  • Pure functions wherever possible
  • Composition over inheritance
  • No comments - code should be self-documenting
  • Array methods over loops
  • Options objects over positional parameters

Why Immutability Matters

Immutable data is the foundation of functional programming. Understanding WHY helps you embrace it:

  • Predictable: Same input always produces same output (no hidden state changes)
  • Debuggable: State doesn't change unexpectedly - easier to trace bugs
  • Testable: No hidden mutable state makes tests straightforward
  • React-friendly: React's reconciliation and memoization optimizations work correctly
  • Concurrency-safe: No race conditions when data can't change

Example of the problem:

// ❌ WRONG - Mutation creates unpredictable behavior
const user = { name: 'Alice', permissions: ['read'] };
grantPermission(user, 'write'); // Mutates user.permissions internally
console.log(user.permissions); // ['read', 'write'] - SURPRISE! user changed
// ✅ CORRECT - Immutable approach is predictable
const user = { name: 'Alice', permissions: ['read'] };
const updatedUser = grantPermission(user, 'write'); // Returns new object
console.log(user.permissions); // ['read'] - original unchanged
console.log(updatedUser.permissions); // ['read', 'write'] - new version

Functional Light

We follow "Functional Light" principles - practical functional patterns without heavy abstractions:

What we DO:

  • Pure functions and immutable data
  • Composition and declarative code
  • Array methods over loops
  • Type safety and readonly

What we DON'T do:

  • Category theory or monads
  • Heavy FP libraries (fp-ts, Ramda)
  • Over-engineering with abstractions
  • Functional for the sake of functional

Why: The goal is maintainable, testable code - not academic purity. If a functional pattern makes code harder to understand, don't use it.

Example - Keep it simple:

// ✅ GOOD - Simple, clear, functional
const activeUsers = users.filter(u => u.active);
const userNames = activeUsers.map(u => u.name);

// ❌ OVER-ENGINEERED - Unnecessary abstraction
const compose = <T>(...fns: Array<(arg: T) => T>) => (x: T) =>
  fns.reduceRight((v, f) => f(v), x);
const activeUsers = compose(
  filter((u: User) => u.active),
  map((u: User) => u.name)
)(users);

No Comments / Self-Documenting Code

Code should be clear through naming and structure. Comments indicate unclear code.

Exception: JSDoc for public APIs when generating documentation.

Examples

WRONG - Comments explaining unclear code

// Get the user and check if active and has permission
function check(u: any) {
  // Check user exists
  if (u) {
    // Check if active
    if (u.a) {
      // Check permission
      if (u.p) {
        return true;
      }
    }
  }
  return false;
}

CORRECT - Self-documenting code

function canUserAccessResource(user: User | undefined): boolean {
  if (!user) return false;
  if (!user.isActive) return false;
  if (!user.hasPermission) return false;
  return true;
}

// Even better - compose predicates
function canUserAccessResource(user: User | undefined): boolean {
  return user?.isActive && user?.hasPermission;
}

When Code Needs Explaining

If code requires comments to understand, refactor instead:

  • Extract functions with descriptive names
  • Use meaningful variable names
  • Break complex logic into steps
  • Use type aliases for domain concepts

Acceptable JSDoc for public APIs

/**
 * Registers a scenario for runtime switching.
 * @param definition - The scenario configuration including mocks and metadata
 * @throws {ValidationError} if scenario ID is duplicate
 */
export function registerScenario(definition: ScenaristScenario): void {
  // Implementation
}

Array Methods Over Loops

Prefer map, filter, reduce for transformations. They're declarative (what, not how) and naturally immutable.

Map - Transform Each Element

WRONG - Imperative loop

const scenarioIds = [];
for (const scenario of scenarios) {
  scenarioIds.push(scenario.id);
}

CORRECT - Functional map

const scenarioIds = scenarios.map(s => s.id);

Filter - Select Subset

WRONG - Imperative loop

const activeScenarios = [];
for (const scenario of scenarios) {
  if (scenario.active) {
    activeScenarios.push(scenario);
  }
}

CORRECT - Functional filter

const activeScenarios = scenarios.filter(s => s.active);

Reduce - Aggregate Values

WRONG - Imperative loop

let total = 0;
for (const item of items) {
  total += item.price * item.quantity;
}

CORRECT - Functional reduce

const total = items.reduce((sum, item) => sum + item.price * item.quantity, 0);

Chaining Multiple Operations

CORRECT - Compose array methods

const total = items
  .filter(item => item.active)
  .map(item => item.price * item.quantity)
  .reduce((sum, price) => sum + price, 0);

When Loops Are Acceptable

Imperative loops are fine when:

  • Early termination is essential (use for...of with break)
  • Performance critical (measure first!)
  • Side effects are necessary (logging, DOM manipulation)

But even then, consider:

  • Array.find() for early termination
  • Array.some() / Array.every() for boolean checks

Options Objects Over Positional Parameters

Default to options objects for function parameters. This improves readability and reduces ordering dependencies.

Why Options Objects?

Benefits:

  • Named parameters (clear what each argument means)
  • No ordering dependencies
  • Easy to add optional parameters
  • Self-documenting at call site
  • TypeScript autocomplete

Examples

WRONG - Positional parameters

function createPayment(
  amount: number,
  currency: string,
  cardId: string,
  cvv: string,
  saveCard: boolean,
  sendReceipt: boolean
): Payment {
  // ...
}

// Call site - unclear what parameters mean
createPayment(100, 'GBP', 'card_123', '123', true, false);

CORRECT - Options object

type CreatePaymentOptions = {
  amount: number;
  currency: string;
  cardId: string;
  cvv: string;
  saveCard?: boolean;
  sendReceipt?: boolean;
};

function createPayment(options: CreatePaymentOptions): Payment {
  const { amount, currency, cardId, cvv, saveCard = false, sendReceipt = true } = options;
  // ...
}

// Call site - crystal clear
createPayment({
  amount: 100,
  currency: 'GBP',
  cardId: 'card_123',
  cvv: '123',
  saveCard: true,
});

When Positional Parameters Are OK

Use positional parameters when:

  • 1-2 parameters max
  • Order is obvious (e.g., add(a, b))
  • High-frequency utility functions
// ✅ OK - Obvious ordering, few parameters
function add(a: number, b: number): number {
  return a + b;
}

function updateUser(user: User, changes: Partial<User>): User {
  return { ...user, ...changes };
}

Pure Functions

Pure functions have no side effects and always return the same output for the same input.

What Makes a Function Pure?

  1. No side effects

    • Doesn't mutate external state
    • Doesn't modify function arguments
    • Doesn't perform I/O (network, file system, console)
  2. Deterministic

    • Same input → same output
    • No dependency on external state (Date.now(), Math.random(), global vars)
  3. Referentially transparent

    • Can replace function call with its return value

Examples

WRONG - Impure function (mutations)

function addScenario(scenarios: Scenario[], newScenario: Scenario): void {
  scenarios.push(newScenario); // ❌ Mutates input
}

let count = 0;
function increment(): number {
  count++; // ❌ Modifies external state
  return count;
}

CORRECT - Pure functions

function addScenario(
  scenarios: ReadonlyArray<Scenario>,
  newScenario: Scenario,
): ReadonlyArray<Scenario> {
  return [...scenarios, newScenario]; // ✅ Returns new array
}

function increment(count: number): number {
  return count + 1; // ✅ No external state
}

Benefits of Pure Functions

  • Testable: No setup/teardown needed
  • Composable: Easy to combine
  • Predictable: No hidden behavior
  • Cacheable: Memoization possible
  • Parallelizable: No race conditions

When Impurity Is Necessary

Some functions must be impure (I/O, randomness, side effects). Isolate them:

// ✅ CORRECT - Isolate impure functions at edges
// Pure core
function calculateTotal(items: ReadonlyArray<Item>): number {
  return items.reduce((sum, item) => sum + item.price, 0);
}

// Impure shell (isolated)
async function saveOrder(order: Order): Promise<void> {
  const total = calculateTotal(order.items); // Pure
  await database.save({ ...order, total }); // Impure (I/O)
}

Pattern: Keep impure functions at system boundaries (adapters, ports). Keep core domain logic pure.


Composition Over Complex Logic

Compose small functions into larger ones. Each function does one thing well.

Benefits of Composition

  • Easier to understand (each piece is simple)
  • Easier to test (test pieces independently)
  • Easier to reuse (pieces work in multiple contexts)
  • Easier to maintain (change one piece without affecting others)

Examples

WRONG - Complex monolithic function

function registerScenario(input: unknown) {
  if (typeof input !== 'object' || !input) {
    throw new Error('Invalid input');
  }
  if (!('id' in input) || typeof input.id !== 'string') {
    throw new Error('Missing id');
  }
  if (!('name' in input) || typeof input.name !== 'string') {
    throw new Error('Missing name');
  }
  if (!('mocks' in input) || !Array.isArray(input.m

---

*Content truncated.*

You might also like

flutter-development

aj-geddes

Build beautiful cross-platform mobile apps with Flutter and Dart. Covers widgets, state management with Provider/BLoC, navigation, API integration, and material design.

1,5711,369

ui-ux-pro-max

nextlevelbuilder

"UI/UX design intelligence. 50 styles, 21 palettes, 50 font pairings, 20 charts, 8 stacks (React, Next.js, Vue, Svelte, SwiftUI, React Native, Flutter, Tailwind). Actions: plan, build, create, design, implement, review, fix, improve, optimize, enhance, refactor, check UI/UX code. Projects: website, landing page, dashboard, admin panel, e-commerce, SaaS, portfolio, blog, mobile app, .html, .tsx, .vue, .svelte. Elements: button, modal, navbar, sidebar, card, table, form, chart. Styles: glassmorphism, claymorphism, minimalism, brutalism, neumorphism, bento grid, dark mode, responsive, skeuomorphism, flat design. Topics: color palette, accessibility, animation, layout, typography, font pairing, spacing, hover, shadow, gradient."

1,1161,191

drawio-diagrams-enhanced

jgtolentino

Create professional draw.io (diagrams.net) diagrams in XML format (.drawio files) with integrated PMP/PMBOK methodologies, extensive visual asset libraries, and industry-standard professional templates. Use this skill when users ask to create flowcharts, swimlane diagrams, cross-functional flowcharts, org charts, network diagrams, UML diagrams, BPMN, project management diagrams (WBS, Gantt, PERT, RACI), risk matrices, stakeholder maps, or any other visual diagram in draw.io format. This skill includes access to custom shape libraries for icons, clipart, and professional symbols.

1,4181,109

godot

bfollington

This skill should be used when working on Godot Engine projects. It provides specialized knowledge of Godot's file formats (.gd, .tscn, .tres), architecture patterns (component-based, signal-driven, resource-based), common pitfalls, validation tools, code templates, and CLI workflows. The `godot` command is available for running the game, validating scripts, importing resources, and exporting builds. Use this skill for tasks involving Godot game development, debugging scene/resource files, implementing game systems, or creating new Godot components.

1,194747

nano-banana-pro

garg-aayush

Generate and edit images using Google's Nano Banana Pro (Gemini 3 Pro Image) API. Use when the user asks to generate, create, edit, modify, change, alter, or update images. Also use when user references an existing image file and asks to modify it in any way (e.g., "modify this image", "change the background", "replace X with Y"). Supports both text-to-image generation and image-to-image editing with configurable resolution (1K default, 2K, or 4K for high resolution). DO NOT read the image file first - use this skill directly with the --input-image parameter.

1,153684

pdf-to-markdown

aliceisjustplaying

Convert entire PDF documents to clean, structured Markdown for full context loading. Use this skill when the user wants to extract ALL text from a PDF into context (not grep/search), when discussing or analyzing PDF content in full, when the user mentions "load the whole PDF", "bring the PDF into context", "read the entire PDF", or when partial extraction/grepping would miss important context. This is the preferred method for PDF text extraction over page-by-page or grep approaches.

1,311614

Stay ahead of the MCP ecosystem

Get weekly updates on new skills and servers.