skills/fp-skills/skills/fp-async-patterns/SKILL.md
Apply functional programming to asynchronous operations. Lazy evaluation with generators/iterators, async pipelines, parallel vs sequential execution, retry with backoff, fallback chains, and stream composition. Activate when: building async workflows, processing data streams, implementing retry logic, handling concurrent operations, working with generators/iterators, or when the user mentions lazy evaluation, async pipeline, parallel execution, retry, backoff, stream processing, or iterator patterns. Works in any language (TypeScript, Python, Go, Rust, Java).
npx skillsauth add olion500/skills fp-async-patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Apply FP principles to async code. Keep async operations composable, testable, and recoverable.
Promise.all, asyncio.gather, goroutines)Defer computation until consumed. Critical for large datasets and infinite sequences.
| Scenario | Eager (wasteful) | Lazy (efficient) | |----------|-----------------|-----------------| | First match in large dataset | Process all, then find | Stop at first match | | Paginated API results | Fetch all pages upfront | Fetch pages on demand | | Log file processing | Load entire file | Stream line by line | | Data pipeline with early filter | Transform all, then filter | Filter first, transform survivors |
// TypeScript: generator composition
function* filterGen<T>(pred: (x: T) => boolean, gen: Iterable<T>) {
for (const x of gen) if (pred(x)) yield x;
}
function* mapGen<T, U>(fn: (x: T) => U, gen: Iterable<T>) {
for (const x of gen) yield fn(x);
}
// Compose — nothing executes until iterated
const pipeline = filterGen(isActive, mapGen(parse, readLines(path)));
for (const item of pipeline) { /* process one at a time */ }
Python: generator expressions + itertools (native lazy). Rust: .iter().map().filter().collect() (lazy until .collect()).
Combine Result pattern with async. If any step fails, skip the rest.
const asyncAndThen = async <T, U, E>(
result: Promise<Result<T, E>>,
fn: (value: T) => Promise<Result<U, E>>
): Promise<Result<U, E>> => {
const r = await result;
return r.ok ? fn(r.value) : r;
};
const processOrder = (id: string) =>
asyncAndThen(asyncAndThen(fetchOrder(id), validateOrder), chargePayment);
| Situation | Use | Pattern |
|---|---|---|
| Independent fetches | Parallel | Promise.all([a(), b(), c()]) |
| Dependent chain | Sequential | a().then(b).then(c) |
| Independent but want all results even if some fail | Parallel + collect | Promise.allSettled(...) |
| Fan-out then aggregate | Parallel + reduce | Promise.all(items.map(fn)).then(merge) |
Separate pure configuration from impure execution.
// Pure: configuration as data
type RetryConfig = Readonly<{ maxAttempts: number; baseDelay: number; maxDelay: number }>;
const defaultRetry: RetryConfig = { maxAttempts: 3, baseDelay: 1000, maxDelay: 10000 };
// Pure: compute delay
const retryDelay = (config: RetryConfig, attempt: number): number =>
Math.min(config.baseDelay * 2 ** attempt, config.maxDelay);
// Impure: execute with retry (boundary)
const withRetry = async <T>(config: RetryConfig, fn: () => Promise<T>): Promise<Result<T, Error>> => {
for (let i = 0; i < config.maxAttempts; i++) {
try { return ok(await fn()); }
catch (e) {
if (i === config.maxAttempts - 1) return err(e instanceof Error ? e : new Error(String(e)));
await sleep(retryDelay(config, i));
}
}
return err(new Error("Unreachable"));
};
Key insight: RetryConfig is pure data. retryDelay is a pure function. Only withRetry is impure. The pure parts are independently testable.
Try alternatives in order until one succeeds. Collect all errors if all fail.
const withFallback = async <T>(...fns: Array<() => Promise<T>>): Promise<Result<T, Error[]>> => {
const errors: Error[] = [];
for (const fn of fns) {
try { return ok(await fn()); }
catch (e) { errors.push(e instanceof Error ? e : new Error(String(e))); }
}
return err(errors);
};
// Try primary → replica → cache
const data = await withFallback(
() => fetchFromPrimary(id),
() => fetchFromReplica(id),
() => fetchFromCache(id),
);
// Node.js
createReadStream(path).pipe(split2()).pipe(filterTransform(isActive)).pipe(outputStream);
# Python: generators are native streams
def process_file(path):
with open(path) as f:
yield from (json.loads(line) for line in f if '"active"' in line)
BufReader::new(File::open(path)?)
.lines()
.filter_map(|l| l.ok())
.filter_map(|l| serde_json::from_str(&l).ok())
.filter(|r: &Record| r.status == "active")
[...generator] forces eager evaluation, defeating the purposemaxAttempts and maxDelayPromise.all when any single failure should NOT cancel the others — use Promise.allSettled insteaddevelopment
Search and query Elasticsearch/Kibana database models using curl API. Use for querying database models, searching Kibana indices, checking Elasticsearch data, investigating data in Kibana, finding records by ID, searching documents. Supports multiple environments (dev, qa, stage, production-us, production-au, production-eu).
development
Search and analyze Datadog logs and metrics using API for cupixworks-api and cupixworks-worker services. Use when debugging errors, investigating issues, searching logs, analyzing worker jobs, checking Sidekiq logs, querying metrics, or finding specific log entries by class/function names. Supports error/warn/info log levels with 14-day retention for logs.
tools
Create, update, search, transition, link, and read comments on Jira issues via CLI. MUST use this skill whenever the user pastes or mentions any atlassian.net URL (Jira issues, Confluence pages, focusedCommentId links, board links — anything from *.atlassian.net). Also use for: TSLA-* ticket references, JQL searches, issue status changes, reading comments, creating bugs, updating descriptions. This is the ONLY way to interact with Jira/Atlassian — there is no MCP Atlassian available.
development
Search Cupix Watch (Kibana/Elasticsearch) application logs at watch.cupix.com. Use when the user asks to search logs, find errors, debug processing issues, or investigate service behavior. Triggers on keywords like "log", "watch", "kibana", "error log", service names (skat, pano, api, worker, vista), or mentions of cupix processing pipelines.