# Refactor Functional Patterns

> Use when reviewing code for functional programming quality — covers Pure Functions, Immutability, Map/Filter/Reduce, Function Composition, Higher-Order Functions, Currying, Functors/Monads, and Pattern Matching with red flags and anti-patterns

- **Type:** Skill
- **Install:** `agentstack add skill-mickeyyaya-refactoring-skills-refactor-functional-patterns`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [mickeyyaya](https://agentstack.voostack.com/s/mickeyyaya)
- **Installs:** 0
- **Category:** [Agent Skills](https://agentstack.voostack.com/c/agent-skills)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [mickeyyaya](https://github.com/mickeyyaya)
- **Source:** https://github.com/mickeyyaya/refactoring-skills/tree/main/skills/refactor-functional-patterns

## Install

```sh
agentstack add skill-mickeyyaya-refactoring-skills-refactor-functional-patterns
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Refactor: Functional Programming Patterns

## Overview

FP patterns produce code that is predictable, testable, and composable by eliminating shared mutable state and side effects.

**When to use:** State mutation bugs, hidden dependencies, untestable functions, complex data pipelines, repeated null-check boilerplate, functions mixing computation and I/O.

## Quick Reference

| Pattern | Core Idea | Primary Red Flag |
|---------|-----------|-----------------|
| Pure Functions | Same input → same output, no side effects | Accessing global state inside business logic |
| Immutability | Never mutate, always create new | `push`, `pop`, or property assignment on existing objects |
| Map/Filter/Reduce | Declarative collection transforms | Manual loops accumulating results |
| Function Composition | Build pipelines from small functions | Deeply nested function calls `f(g(h(x)))` |
| Higher-Order Functions | Functions that take/return functions | Copy-pasted blocks differing by one operation |
| Currying / Partial Application | Fix some arguments, defer the rest | Repeatedly passing the same first argument |
| Functors / Monads | Chainable containers for optional/error values | Nested null checks, try-catch pyramids |
| Pattern Matching | Destructure and branch on data shape | `instanceof` chains or long type-checking conditionals |

---

## Patterns in Detail

### 1. Pure Functions

**Red Flags:** Reading `global`/`process.env` in computation; I/O mixed into business logic; parameter mutation (`arr.push(x)`); `Date.now()`/`Math.random()` in deterministic functions.

```typescript
// BEFORE — reads global state + side effect
let taxRate = 0.2;
function calculateTotal(price: number): number {
  const total = price * (1 + taxRate);
  console.log(`total: ${total}`);
  return total;
}

// AFTER — pure: only uses arguments
function calculateTotal(price: number, taxRate: number): number {
  return price * (1 + taxRate);
}
```

```python
# Python equivalent
def calculate_total(price: float, tax_rate: float) -> float:
    return price * (1 + tax_rate)
```

---

### 2. Immutability

**Red Flags:** `.push()`/`.pop()`/`.splice()`/`.sort()` mutate in place; `object.field = value`; `Object.assign` on original; shallow spread missing nested immutability; missing `readonly`.

```typescript
// BEFORE — mutates caller's array
function addItem(cart: CartItem[], item: CartItem): CartItem[] {
  cart.push(item);
  return cart;
}

// AFTER — new array, original untouched
function addItem(cart: readonly CartItem[], item: CartItem): readonly CartItem[] {
  return [...cart, item];
}
```

```python
# Python — tuples are immutable
def add_item(cart: tuple, item) -> tuple:
    return (*cart, item)
```

Cross-reference: `refactor-composing-methods` — "Remove Assignments to Parameters".

---

### 3. Map / Filter / Reduce

**Red Flags:** Loop building accumulator → `map`; with condition → `filter`; single value → `reduce`; nested loops → `flatMap`; index used only to access element.

```typescript
// BEFORE
function getActiveUserEmails(users: User[]): string[] {
  const result: string[] = [];
  for (let i = 0; i  u.active).map(u => u.email.toLowerCase());
}
```

```python
# Python — list comprehension
def get_active_user_emails(users: list[User]) -> list[str]:
    return [u.email.lower() for u in users if u.active]
```

---

### 4. Function Composition

**Red Flags:** Nested calls `f(g(h(x)))`; variables named `temp`/`step1`/`result1`; single function doing three things separated by comments.

```typescript
// BEFORE
function processUserInput(raw: string): string {
  const trimmed = raw.trim();
  const lower = trimmed.toLowerCase();
  return lower.replace(/\s+/g, '_');
}

// AFTER — pipeline
const pipe = (...fns: Array T>) => (x: T): T =>
  fns.reduce((acc, fn) => fn(acc), x);

const processUserInput = pipe(
  (s: string) => s.trim(),
  (s: string) => s.toLowerCase(),
  (s: string) => s.replace(/\s+/g, '_'),
);
```

```python
from functools import reduce
def pipe(*fns):
    return lambda x: reduce(lambda acc, f: f(acc), fns, x)

process_user_input = pipe(str.strip, str.lower, lambda s: s.replace(' ', '_'))
```

---

### 5. Higher-Order Functions

**Red Flags:** Two functions identical except one operation; callback code abstractable into retry/cache/log wrapper.

```typescript
// BEFORE — duplicated sort logic
function sortByName(users: User[]): User[] {
  return [...users].sort((a, b) => a.name.localeCompare(b.name));
}
function sortByAge(users: User[]): User[] {
  return [...users].sort((a, b) => a.age - b.age);
}

// AFTER — parameterized
function sortBy(key: (item: T) => number | string) {
  return (items: readonly T[]): readonly T[] =>
    [...items].sort((a, b) => {
      const ka = key(a), kb = key(b);
      return ka  kb ? 1 : 0;
    });
}
const sortByName = sortBy((u: User) => u.name);
const sortByAge  = sortBy((u: User) => u.age);
```

---

### 6. Currying / Partial Application

**Red Flags:** Repeated calls like `formatDate(locale, date1)`, `formatDate(locale, date2)`; config objects threaded when only the last arg varies.

```typescript
// BEFORE — caller repeatedly specifies locale and currency
formatCurrency('en-US', 'USD', 9.99);
formatCurrency('en-US', 'USD', 19.99);

// AFTER — curried
const formatCurrency =
  (locale: string) => (currency: string) => (amount: number): string =>
    new Intl.NumberFormat(locale, { style: 'currency', currency }).format(amount);

const formatUSD = formatCurrency('en-US')('USD');
formatUSD(9.99);
formatUSD(19.99);
```

```python
from functools import partial
format_usd = partial(format_currency, 'en-US', 'USD')
format_usd(9.99)
```

---

### 7. Functors / Monads (Optional, Result, Promise)

**Red Flags:** `if (a !== null && a.b !== null && ...)` chains; nested try-catch; functions returning `null | undefined | T` without a container type.

```typescript
// BEFORE — null pyramid
function getCity(user: User | null): string | null {
  if (user !== null) {
    if (user.address !== null) {
      if (user.address.city !== null) {
        return user.address.city.toUpperCase();
      }
    }
  }
  return null;
}

// AFTER — optional chaining
function getCity(user: User | null): string | null {
  return user?.address?.city?.toUpperCase() ?? null;
}
```

```typescript
// Result type for error handling
type Result = { ok: true; value: T } | { ok: false; error: E };

function parseAge(raw: string): Result {
  const n = Number(raw);
  if (isNaN(n) || n  Ok | Err:
    try:
        n = int(raw)
        return Ok(n) if n >= 0 else Err(f"Negative age: {raw}")
    except ValueError:
        return Err(f"Not a number: {raw}")
```

---

### 8. Pattern Matching

**Red Flags:** `instanceof` chains — use discriminated unions; type conditionals without exhaustiveness check; switch on union with no `default`.

```typescript
// BEFORE — instanceof chain
function area(shape: Circle | Rectangle | Triangle): number {
  if (shape instanceof Circle) return Math.PI * shape.radius ** 2;
  if (shape instanceof Rectangle) return shape.width * shape.height;
  if (shape instanceof Triangle) return 0.5 * shape.base * shape.height;
  throw new Error('Unknown shape');
}

// AFTER — discriminated union with exhaustiveness
type Shape =
  | { kind: 'circle';    radius: number }
  | { kind: 'rectangle'; width: number; height: number }
  | { kind: 'triangle';  base: number;  height: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':    return Math.PI * shape.radius ** 2;
    case 'rectangle': return shape.width * shape.height;
    case 'triangle':  return 0.5 * shape.base * shape.height;
    default: {
      const _exhaustive: never = shape;
      throw new Error(`Unhandled shape: ${JSON.stringify(_exhaustive)}`);
    }
  }
}
```

```python
# Python 3.10+
def area(shape: dict) -> float:
    match shape:
        case {'kind': 'circle', 'radius': r}:    return 3.14159 * r ** 2
        case {'kind': 'rectangle', 'width': w, 'height': h}: return w * h
        case {'kind': 'triangle', 'base': b, 'height': h}:  return 0.5 * b * h
        case _: raise ValueError(f"Unknown shape: {shape}")
```

---

## FP Anti-Patterns

| Anti-Pattern | Description | Fix |
|-------------|-------------|-----|
| **Impure function masquerading as pure** | Reads a closure variable that changes over time | Make mutable dependency an explicit parameter |
| **Shallow-copy illusion** | `{...obj}` — nested objects still shared | Deep-clone or use Immer/Immutable.js |
| **Point-free everything** | Removing all named parameters hurts readability | Use point-free only when it clarifies |
| **Monadic overkill** | Full Maybe monad for simple null check | Prefer optional chaining for shallow checks |
| **Impure reduce** | Accumulator mutated inside callback | Return new accumulator each iteration |
| **Shared state in curried functions** | Captures mutable outer scope value | Ensure captured values are constants |

**Shallow-copy illusion — example:**
```typescript
// WRONG — nested address object is still shared
const updated = { ...user, name: 'Alice' };
updated.address.city = 'Boston';  // mutates original user.address!

// CORRECT — spread nested objects too, or use structuredClone
const updated = { ...user, name: 'Alice', address: { ...user.address, city: 'Boston' } };
```

---

## Cross-References

- `refactor-composing-methods` — "Remove Assignments to Parameters" (immutability); "Substitute Algorithm" (loops → declarative)
- `refactor-simplifying-conditionals` — Simplifying `if`/`else` chains that pattern matching replaces
- `review-solid-clean-code` — SRP aligns with pure functions; OCP aligns with HOFs
- `detect-code-smells` — "Data Clumps" → missing algebraic data types; "Long Method" → missing composition

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [mickeyyaya](https://github.com/mickeyyaya)
- **Source:** [mickeyyaya/refactoring-skills](https://github.com/mickeyyaya/refactoring-skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-mickeyyaya-refactoring-skills-refactor-functional-patterns
- Seller: https://agentstack.voostack.com/s/mickeyyaya
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
