← Back to list

functional-patterns
by aiskillstore
Security-audited skills for Claude, Codex & Claude Code. One-click install, quality verified.
⭐ 102🍴 3📅 Jan 23, 2026
SKILL.md
name: functional-patterns description: Functional programming patterns that promote testability, composability, and maintainability.
Functional Patterns for Node.js/TypeScript
Overview
Functional programming patterns that promote testability, composability, and maintainability.
Pure Functions
Definition
A pure function:
- Always returns the same output for the same input
- Has no side effects (no I/O, no mutation)
Examples
// Pure: Deterministic, no side effects
const add = (a: number, b: number): number => a + b;
const calculateTotal = (items: OrderItem[]): number =>
items.reduce((sum, item) => sum + item.price * item.quantity, 0);
const filterActiveUsers = (users: User[]): User[] =>
users.filter((user) => user.isActive);
// Impure: Has side effects
const saveUser = async (user: User): Promise<void> => {
await database.save(user); // I/O side effect
};
const logMessage = (msg: string): void => {
console.log(msg); // Side effect
};
const generateId = (): string => {
return crypto.randomUUID(); // Non-deterministic
};
Pure Core, Impure Shell
// Pure core - all business logic
const validateOrder = (order: Order): Result<Order, ValidationError> => {
if (!order.items.length) return Result.fail({ code: 'EMPTY_ORDER' });
if (order.total < 0) return Result.fail({ code: 'INVALID_TOTAL' });
return Result.ok(order);
};
const calculateDiscount = (order: Order, user: User): number => {
const baseDiscount = user.isPremium ? 0.15 : 0;
const volumeDiscount = order.items.length > 10 ? 0.05 : 0;
return baseDiscount + volumeDiscount;
};
const applyDiscount = (order: Order, discountRate: number): Order => ({
...order,
total: order.total * (1 - discountRate),
});
// Impure shell - handles I/O
const processOrderHandler = (deps: Dependencies) => async (order: Order) => {
const validation = validateOrder(order);
if (validation.isFailure) return validation;
const user = await deps.userRepo.findById(order.userId); // I/O
const discount = calculateDiscount(validation.value, user);
const discounted = applyDiscount(validation.value, discount);
await deps.orderRepo.save(discounted); // I/O
await deps.notifier.send(user.email, 'Order confirmed'); // I/O
return Result.ok(discounted);
};
Immutability
Immutable Updates
// Bad: Mutation
const addItemToCart = (cart: Cart, item: Item) => {
cart.items.push(item);
cart.total += item.price;
return cart;
};
// Good: Immutable update
const addItemToCart = (cart: Cart, item: Item): Cart => ({
...cart,
items: [...cart.items, item],
total: cart.total + item.price,
});
// Good: Nested immutable update
const updateUserAddress = (user: User, address: Partial<Address>): User => ({
...user,
address: {
...user.address,
...address,
},
});
// Good: Using readonly types
type ReadonlyUser = {
readonly id: string;
readonly email: string;
readonly addresses: readonly Address[];
};
// Good: Immutable array operations
const addItem = <T>(arr: readonly T[], item: T): readonly T[] => [...arr, item];
const removeAt = <T>(arr: readonly T[], index: number): readonly T[] =>
arr.filter((_, i) => i !== index);
const updateAt = <T>(arr: readonly T[], index: number, item: T): readonly T[] =>
arr.map((existing, i) => (i === index ? item : existing));
Function Composition
Pipe and Compose
// Pipe: left to right
type Fn<A, B> = (a: A) => B;
const pipe = <A, B, C>(
f: Fn<A, B>,
g: Fn<B, C>
): Fn<A, C> => (a) => g(f(a));
const pipe3 = <A, B, C, D>(
f: Fn<A, B>,
g: Fn<B, C>,
h: Fn<C, D>
): Fn<A, D> => (a) => h(g(f(a)));
// Usage
const processInput = pipe3(
trim,
toLowerCase,
validateEmail
);
// Variadic pipe
const pipeAll = <T>(...fns: Array<(x: T) => T>) =>
(initial: T): T => fns.reduce((acc, fn) => fn(acc), initial);
const processString = pipeAll(
(s: string) => s.trim(),
(s: string) => s.toLowerCase(),
(s: string) => s.replace(/\s+/g, '-')
);
Higher-Order Functions
// Function that returns a function
const withLogging = <T extends (...args: any[]) => any>(fn: T) =>
(...args: Parameters<T>): ReturnType<T> => {
console.log('Calling with:', args);
const result = fn(...args);
console.log('Result:', result);
return result;
};
// Function that takes a function
const retry = <T>(
fn: () => Promise<T>,
attempts: number = 3
): Promise<T> =>
fn().catch((error) =>
attempts > 1 ? retry(fn, attempts - 1) : Promise.reject(error)
);
// Currying
const multiply = (a: number) => (b: number): number => a * b;
const double = multiply(2);
const triple = multiply(3);
// Partial application
const createLogger = (prefix: string) =>
(message: string): void => console.log(`[${prefix}] ${message}`);
const infoLog = createLogger('INFO');
const errorLog = createLogger('ERROR');
Result Pattern (Monadic Error Handling)
Basic Result Type
type Result<T, E> =
| { readonly _tag: 'Ok'; readonly value: T }
| { readonly _tag: 'Err'; readonly error: E };
const ok = <T>(value: T): Result<T, never> => ({ _tag: 'Ok', value });
const err = <E>(error: E): Result<never, E> => ({ _tag: 'Err', error });
const isOk = <T, E>(result: Result<T, E>): result is { _tag: 'Ok'; value: T } =>
result._tag === 'Ok';
const isErr = <T, E>(result: Result<T, E>): result is { _tag: 'Err'; error: E } =>
result._tag === 'Err';
Result Operations
// Map: transform success value
const map = <T, U, E>(
result: Result<T, E>,
fn: (value: T) => U
): Result<U, E> =>
isOk(result) ? ok(fn(result.value)) : result;
// MapError: transform error value
const mapError = <T, E, F>(
result: Result<T, E>,
fn: (error: E) => F
): Result<T, F> =>
isErr(result) ? err(fn(result.error)) : result;
// FlatMap (chain): compose Result-returning functions
const flatMap = <T, U, E>(
result: Result<T, E>,
fn: (value: T) => Result<U, E>
): Result<U, E> =>
isOk(result) ? fn(result.value) : result;
// Match: exhaustive handling
const match = <T, E, R>(
result: Result<T, E>,
handlers: { ok: (value: T) => R; err: (error: E) => R }
): R =>
isOk(result) ? handlers.ok(result.value) : handlers.err(result.error);
Chaining Results
type ValidationError = { field: string; message: string };
type ProcessingError = { code: string; details: string };
type AppError = ValidationError | ProcessingError;
const validateInput = (input: unknown): Result<ValidInput, ValidationError> => {
// ...
};
const processData = (input: ValidInput): Result<ProcessedData, ProcessingError> => {
// ...
};
const formatOutput = (data: ProcessedData): Output => {
// ...
};
// Compose the pipeline
const handleRequest = (input: unknown): Result<Output, AppError> => {
const validated = validateInput(input);
if (isErr(validated)) return validated;
const processed = processData(validated.value);
if (isErr(processed)) return processed;
return ok(formatOutput(processed.value));
};
// Or with flatMap
const handleRequestFunctional = (input: unknown): Result<Output, AppError> =>
flatMap(validateInput(input), (valid) =>
map(processData(valid), formatOutput)
);
Option/Maybe Pattern
type Option<T> =
| { readonly _tag: 'Some'; readonly value: T }
| { readonly _tag: 'None' };
const some = <T>(value: T): Option<T> => ({ _tag: 'Some', value });
const none: Option<never> = { _tag: 'None' };
const isSome = <T>(opt: Option<T>): opt is { _tag: 'Some'; value: T } =>
opt._tag === 'Some';
const isNone = <T>(opt: Option<T>): opt is { _tag: 'None' } =>
opt._tag === 'None';
// Operations
const mapOption = <T, U>(opt: Option<T>, fn: (value: T) => U): Option<U> =>
isSome(opt) ? some(fn(opt.value)) : none;
const getOrElse = <T>(opt: Option<T>, defaultValue: T): T =>
isSome(opt) ? opt.value : defaultValue;
const fromNullable = <T>(value: T | null | undefined): Option<T> =>
value != null ? some(value) : none;
// Usage
const findUser = (id: string): Option<User> => {
const user = users.find((u) => u.id === id);
return fromNullable(user);
};
const getUserEmail = (id: string): string =>
getOrElse(
mapOption(findUser(id), (user) => user.email),
'unknown@example.com'
);
Dependency Injection via Functions
// Define dependencies as a type
type Dependencies = {
userRepo: UserRepository;
orderRepo: OrderRepository;
logger: Logger;
clock: () => Date; // Even time can be injected
};
// Create service factory
const createOrderService = (deps: Dependencies) => ({
createOrder: async (data: CreateOrderData): Promise<Result<Order, OrderError>> => {
const user = await deps.userRepo.findById(data.userId);
if (!user) {
return err({ code: 'USER_NOT_FOUND', userId: data.userId });
}
const order: Order = {
id: generateId(),
...data,
createdAt: deps.clock(),
status: 'pending',
};
deps.logger.info({ orderId: order.id }, 'Creating order');
await deps.orderRepo.save(order);
return ok(order);
},
});
// Test with fake dependencies
describe('OrderService', () => {
it('should create order', async () => {
const deps: Dependencies = {
userRepo: { findById: jest.fn().mockResolvedValue({ id: '1' }) },
orderRepo: { save: jest.fn() },
logger: { info: jest.fn() },
clock: () => new Date('2024-01-01'),
};
const service = createOrderService(deps);
const result = await service.createOrder({ userId: '1', items: [] });
expect(isOk(result)).toBe(true);
});
});
Score
Total Score
60/100
Based on repository quality metrics
✓SKILL.md
SKILL.mdファイルが含まれている
+20
○LICENSE
ライセンスが設定されている
0/10
○説明文
100文字以上の説明がある
0/10
✓人気
GitHub Stars 100以上
+5
✓最近の活動
1ヶ月以内に更新
+10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
✓タグ
1つ以上のタグが設定されている
+5
Reviews
💬
Reviews coming soon
