{"id":"bee18a2a-2d85-4db5-9414-669b1b5ebff2","shortId":"3Vn6r2","kind":"skill","title":"fp-pragmatic","tagline":"A practical, jargon-free guide to functional programming - the 80/20 approach that gets results without the academic overhead","description":"# Pragmatic Functional Programming\n\n**Read this first.** This guide cuts through the academic jargon and shows you what actually matters. No category theory. No abstract nonsense. Just patterns that make your code better.\n\n## When to Use\n- You want a pragmatic starting point for fp-ts or functional programming in TypeScript.\n- The task is exploratory or educational and needs an 80/20 view of what is actually worth adopting.\n- You need guidance on when FP helps and when it is better to keep code simple.\n\n## The Golden Rule\n\n> **If functional programming makes your code harder to read, don't use it.**\n\nFP is a tool, not a religion. Use it when it helps. Skip it when it doesn't.\n\n---\n\n## The 80/20 of FP\n\nThese five patterns give you most of the benefits. Master these before exploring anything else.\n\n### 1. Pipe: Chain Operations Clearly\n\nInstead of nesting function calls or creating intermediate variables, chain operations in reading order.\n\n```typescript\nimport { pipe } from 'fp-ts/function'\n\n// Before: Hard to read (inside-out)\nconst result = format(validate(parse(input)))\n\n// Before: Too many variables\nconst parsed = parse(input)\nconst validated = validate(parsed)\nconst result = format(validated)\n\n// After: Clear, linear flow\nconst result = pipe(\n  input,\n  parse,\n  validate,\n  format\n)\n```\n\n**When to use pipe:**\n- 3+ transformations on the same data\n- You find yourself naming throwaway variables\n- Logic reads better top-to-bottom\n\n**When to skip pipe:**\n- Just 1-2 operations (direct call is fine)\n- The operations don't naturally chain\n\n### 2. Option: Handle Missing Values Without null Checks\n\nStop writing `if (x !== null && x !== undefined)` everywhere.\n\n```typescript\nimport * as O from 'fp-ts/Option'\nimport { pipe } from 'fp-ts/function'\n\n// Before: Defensive null checking\nfunction getUserCity(user: User | null): string {\n  if (user === null) return 'Unknown'\n  if (user.address === null) return 'Unknown'\n  if (user.address.city === null) return 'Unknown'\n  return user.address.city\n}\n\n// After: Chain through potential missing values\nconst getUserCity = (user: User | null): string =>\n  pipe(\n    O.fromNullable(user),\n    O.flatMap(u => O.fromNullable(u.address)),\n    O.flatMap(a => O.fromNullable(a.city)),\n    O.getOrElse(() => 'Unknown')\n  )\n```\n\n**Plain language translation:**\n- `O.fromNullable(x)` = \"wrap this value, treating null/undefined as 'nothing'\"\n- `O.flatMap(fn)` = \"if we have something, apply this function\"\n- `O.getOrElse(() => default)` = \"unwrap, or use this default if nothing\"\n\n### 3. Either: Make Errors Explicit\n\nStop throwing exceptions for expected failures. Return errors as values.\n\n```typescript\nimport * as E from 'fp-ts/Either'\nimport { pipe } from 'fp-ts/function'\n\n// Before: Hidden failure mode\nfunction parseAge(input: string): number {\n  const age = parseInt(input, 10)\n  if (isNaN(age)) throw new Error('Invalid age')\n  if (age < 0) throw new Error('Age cannot be negative')\n  return age\n}\n\n// After: Errors are visible in the type\nfunction parseAge(input: string): E.Either<string, number> {\n  const age = parseInt(input, 10)\n  if (isNaN(age)) return E.left('Invalid age')\n  if (age < 0) return E.left('Age cannot be negative')\n  return E.right(age)\n}\n\n// Using it\nconst result = parseAge(userInput)\nif (E.isRight(result)) {\n  console.log(`Age is ${result.right}`)\n} else {\n  console.log(`Error: ${result.left}`)\n}\n```\n\n**Plain language translation:**\n- `E.right(value)` = \"success with this value\"\n- `E.left(error)` = \"failure with this error\"\n- `E.isRight(x)` = \"did it succeed?\"\n\n### 4. Map: Transform Without Unpacking\n\nTransform values inside containers without extracting them first.\n\n```typescript\nimport * as O from 'fp-ts/Option'\nimport * as E from 'fp-ts/Either'\nimport * as A from 'fp-ts/Array'\nimport { pipe } from 'fp-ts/function'\n\n// Transform inside Option\nconst maybeUser: O.Option<User> = O.some({ name: 'Alice', age: 30 })\nconst maybeName: O.Option<string> = pipe(\n  maybeUser,\n  O.map(user => user.name)\n)\n\n// Transform inside Either\nconst result: E.Either<Error, number> = E.right(5)\nconst doubled: E.Either<Error, number> = pipe(\n  result,\n  E.map(n => n * 2)\n)\n\n// Transform arrays (same concept!)\nconst numbers = [1, 2, 3]\nconst doubled = pipe(\n  numbers,\n  A.map(n => n * 2)\n)\n```\n\n### 5. FlatMap: Chain Operations That Might Fail\n\nWhen each step might fail, chain them together.\n\n```typescript\nimport * as E from 'fp-ts/Either'\nimport { pipe } from 'fp-ts/function'\n\nconst parseJSON = (s: string): E.Either<string, unknown> =>\n  E.tryCatch(() => JSON.parse(s), () => 'Invalid JSON')\n\nconst extractEmail = (data: unknown): E.Either<string, string> => {\n  if (typeof data === 'object' && data !== null && 'email' in data) {\n    return E.right((data as { email: string }).email)\n  }\n  return E.left('No email field')\n}\n\nconst validateEmail = (email: string): E.Either<string, string> =>\n  email.includes('@') ? E.right(email) : E.left('Invalid email format')\n\n// Chain all steps - if any fails, the whole thing fails\nconst getValidEmail = (input: string): E.Either<string, string> =>\n  pipe(\n    parseJSON(input),\n    E.flatMap(extractEmail),\n    E.flatMap(validateEmail)\n  )\n\n// Success path: Right('user@example.com')\n// Any failure: Left('specific error message')\n```\n\n**Plain language:** `flatMap` means \"if this succeeded, try the next thing\"\n\n---\n\n## When NOT to Use FP\n\nFunctional programming is not always the answer. Here's when to keep it simple.\n\n### Simple Null Checks\n\n```typescript\n// Just use optional chaining - it's built into the language\nconst city = user?.address?.city ?? 'Unknown'\n\n// DON'T overcomplicate it\nconst city = pipe(\n  O.fromNullable(user),\n  O.flatMap(u => O.fromNullable(u.address)),\n  O.flatMap(a => O.fromNullable(a.city)),\n  O.getOrElse(() => 'Unknown')\n)\n```\n\n### Simple Loops\n\n```typescript\n// A for loop is fine when you need early exit or complex logic\nfunction findFirst(items: Item[], predicate: (i: Item) => boolean): Item | null {\n  for (const item of items) {\n    if (predicate(item)) return item\n  }\n  return null\n}\n\n// DON'T force FP when it doesn't help\nconst result = pipe(\n  items,\n  A.findFirst(predicate),\n  O.toNullable\n)\n```\n\n### Performance-Critical Code\n\n```typescript\n// For hot paths, imperative is faster (no intermediate arrays)\nfunction sumLarge(numbers: number[]): number {\n  let sum = 0\n  for (let i = 0; i < numbers.length; i++) {\n    sum += numbers[i]\n  }\n  return sum\n}\n\n// fp-ts creates intermediate structures\nconst sum = pipe(numbers, A.reduce(0, (acc, n) => acc + n))\n```\n\n### When Your Team Doesn't Know FP\n\nIf you're the only one who can read the code, it's not good code.\n\n```typescript\n// If your team knows this pattern\nasync function getUser(id: string): Promise<User | null> {\n  try {\n    const response = await fetch(`/api/users/${id}`)\n    if (!response.ok) return null\n    return await response.json()\n  } catch {\n    return null\n  }\n}\n\n// Don't force this on them\nconst getUser = (id: string): TE.TaskEither<Error, User> =>\n  pipe(\n    TE.tryCatch(() => fetch(`/api/users/${id}`), E.toError),\n    TE.flatMap(r => r.ok ? TE.right(r) : TE.left(new Error('Not found'))),\n    TE.flatMap(r => TE.tryCatch(() => r.json(), E.toError))\n  )\n```\n\n---\n\n## Quick Wins: Easy Changes That Improve Code Today\n\n### 1. Replace Nested Ternaries with pipe + fold\n\n```typescript\n// Before: Nested ternary nightmare\nconst message = user === null\n  ? 'No user'\n  : user.isAdmin\n    ? `Admin: ${user.name}`\n    : `User: ${user.name}`\n\n// After: Clear case handling\nconst message = pipe(\n  O.fromNullable(user),\n  O.fold(\n    () => 'No user',\n    (u) => u.isAdmin ? `Admin: ${u.name}` : `User: ${u.name}`\n  )\n)\n```\n\n### 2. Replace try-catch with tryCatch\n\n```typescript\n// Before: try-catch everywhere\nlet config\ntry {\n  config = JSON.parse(rawConfig)\n} catch {\n  config = defaultConfig\n}\n\n// After: One-liner\nconst config = pipe(\n  E.tryCatch(() => JSON.parse(rawConfig), () => 'parse error'),\n  E.getOrElse(() => defaultConfig)\n)\n```\n\n### 3. Replace undefined Returns with Option\n\n```typescript\n// Before: Caller might forget to check\nfunction findUser(id: string): User | undefined {\n  return users.find(u => u.id === id)\n}\n\n// After: Type forces caller to handle missing case\nfunction findUser(id: string): O.Option<User> {\n  return O.fromNullable(users.find(u => u.id === id))\n}\n```\n\n### 4. Replace Error Strings with Typed Errors\n\n```typescript\n// Before: Just strings\nfunction validate(data: unknown): E.Either<string, User> {\n  // ...\n  return E.left('validation failed')\n}\n\n// After: Structured errors\ntype ValidationError = {\n  field: string\n  message: string\n}\n\nfunction validate(data: unknown): E.Either<ValidationError, User> {\n  // ...\n  return E.left({ field: 'email', message: 'Invalid format' })\n}\n```\n\n### 5. Use const Assertions for Error Types\n\n```typescript\n// Create specific error types without classes\nconst NotFound = (id: string) => ({ _tag: 'NotFound' as const, id })\nconst Unauthorized = { _tag: 'Unauthorized' as const }\nconst ValidationFailed = (errors: string[]) =>\n  ({ _tag: 'ValidationFailed' as const, errors })\n\ntype AppError =\n  | ReturnType<typeof NotFound>\n  | typeof Unauthorized\n  | ReturnType<typeof ValidationFailed>\n\n// Now you can pattern match\nconst handleError = (error: AppError): string => {\n  switch (error._tag) {\n    case 'NotFound': return `Item ${error.id} not found`\n    case 'Unauthorized': return 'Please log in'\n    case 'ValidationFailed': return error.errors.join(', ')\n  }\n}\n```\n\n---\n\n## Common Refactors: Before and After\n\n### Callback Hell to Pipe\n\n```typescript\n// Before\nfetchUser(id, (user) => {\n  if (!user) return handleNoUser()\n  fetchPosts(user.id, (posts) => {\n    if (!posts) return handleNoPosts()\n    fetchComments(posts[0].id, (comments) => {\n      render(user, posts, comments)\n    })\n  })\n})\n\n// After (with TaskEither for async)\nimport * as TE from 'fp-ts/TaskEither'\n\nconst loadData = (id: string) =>\n  pipe(\n    fetchUser(id),\n    TE.flatMap(user => pipe(\n      fetchPosts(user.id),\n      TE.map(posts => ({ user, posts }))\n    )),\n    TE.flatMap(({ user, posts }) => pipe(\n      fetchComments(posts[0].id),\n      TE.map(comments => ({ user, posts, comments }))\n    ))\n  )\n\n// Execute\nconst result = await loadData('123')()\npipe(\n  result,\n  E.fold(handleError, ({ user, posts, comments }) => render(user, posts, comments))\n)\n```\n\n### Multiple null Checks to Option Chain\n\n```typescript\n// Before\nfunction getManagerEmail(employee: Employee): string | null {\n  if (!employee.department) return null\n  if (!employee.department.manager) return null\n  if (!employee.department.manager.email) return null\n  return employee.department.manager.email\n}\n\n// After\nconst getManagerEmail = (employee: Employee): O.Option<string> =>\n  pipe(\n    O.fromNullable(employee.department),\n    O.flatMap(d => O.fromNullable(d.manager)),\n    O.flatMap(m => O.fromNullable(m.email))\n  )\n\n// Use it\npipe(\n  getManagerEmail(employee),\n  O.fold(\n    () => sendToDefault(),\n    (email) => sendTo(email)\n  )\n)\n```\n\n### Validation with Multiple Checks\n\n```typescript\n// Before: Throws on first error\nfunction validateUser(data: unknown): User {\n  if (!data || typeof data !== 'object') throw new Error('Must be object')\n  const obj = data as Record<string, unknown>\n  if (typeof obj.email !== 'string') throw new Error('Email required')\n  if (!obj.email.includes('@')) throw new Error('Invalid email')\n  if (typeof obj.age !== 'number') throw new Error('Age required')\n  if (obj.age < 0) throw new Error('Age must be positive')\n  return obj as User\n}\n\n// After: Returns first error, type-safe\nconst validateUser = (data: unknown): E.Either<string, User> =>\n  pipe(\n    E.Do,\n    E.bind('obj', () =>\n      typeof data === 'object' && data !== null\n        ? E.right(data as Record<string, unknown>)\n        : E.left('Must be object')\n    ),\n    E.bind('email', ({ obj }) =>\n      typeof obj.email === 'string' && obj.email.includes('@')\n        ? E.right(obj.email)\n        : E.left('Valid email required')\n    ),\n    E.bind('age', ({ obj }) =>\n      typeof obj.age === 'number' && obj.age >= 0\n        ? E.right(obj.age)\n        : E.left('Valid age required')\n    ),\n    E.map(({ email, age }) => ({ email, age }))\n  )\n```\n\n### Promise Chain to TaskEither\n\n```typescript\n// Before\nasync function processOrder(orderId: string): Promise<Receipt> {\n  const order = await fetchOrder(orderId)\n  if (!order) throw new Error('Order not found')\n\n  const validated = await validateOrder(order)\n  if (!validated.success) throw new Error(validated.error)\n\n  const payment = await processPayment(validated.order)\n  if (!payment.success) throw new Error('Payment failed')\n\n  return generateReceipt(payment)\n}\n\n// After\nconst processOrder = (orderId: string): TE.TaskEither<string, Receipt> =>\n  pipe(\n    fetchOrderTE(orderId),\n    TE.flatMap(order =>\n      order ? TE.right(order) : TE.left('Order not found')\n    ),\n    TE.flatMap(validateOrderTE),\n    TE.flatMap(processPaymentTE),\n    TE.map(generateReceipt)\n  )\n```\n\n---\n\n## The Readability Rule\n\nBefore using any FP pattern, ask: **\"Would a junior developer understand this?\"**\n\n### Too Clever (Avoid)\n\n```typescript\nconst result = pipe(\n  data,\n  A.filter(flow(prop('status'), equals('active'))),\n  A.map(flow(prop('value'), multiply(2))),\n  A.reduce(monoid.concat, monoid.empty),\n  O.fromPredicate(gt(threshold))\n)\n```\n\n### Just Right (Prefer)\n\n```typescript\nconst activeItems = data.filter(item => item.status === 'active')\nconst doubledValues = activeItems.map(item => item.value * 2)\nconst total = doubledValues.reduce((sum, val) => sum + val, 0)\nconst result = total > threshold ? O.some(total) : O.none\n```\n\n### The Middle Ground (Often Best)\n\n```typescript\nconst result = pipe(\n  data,\n  A.filter(item => item.status === 'active'),\n  A.map(item => item.value * 2),\n  A.reduce(0, (sum, val) => sum + val),\n  total => total > threshold ? O.some(total) : O.none\n)\n```\n\n---\n\n## Cheat Sheet\n\n| What you want | Plain language | fp-ts |\n|--------------|----------------|-------|\n| Handle null/undefined | \"Wrap this nullable\" | `O.fromNullable(x)` |\n| Default for missing | \"Use this if nothing\" | `O.getOrElse(() => default)` |\n| Transform if present | \"If something, change it\" | `O.map(fn)` |\n| Chain nullable operations | \"If something, try this\" | `O.flatMap(fn)` |\n| Return success | \"Worked, here's the value\" | `E.right(value)` |\n| Return failure | \"Failed, here's why\" | `E.left(error)` |\n| Wrap throwing function | \"Try this, catch errors\" | `E.tryCatch(fn, onError)` |\n| Handle both cases | \"Do this for error, that for success\" | `E.fold(onLeft, onRight)` |\n| Chain operations | \"Then do this, then that\" | `pipe(x, fn1, fn2, fn3)` |\n\n---\n\n## When to Level Up\n\nOnce comfortable with these patterns, explore:\n\n1. **TaskEither** - Async operations that can fail (replaces Promise + try/catch)\n2. **Validation** - Collect ALL errors instead of stopping at first\n3. **Reader** - Dependency injection without classes\n4. **Do notation** - Cleaner syntax for multiple bindings\n\nBut don't rush. The basics here will handle 80% of real-world scenarios. Get comfortable with these before adding more tools to your belt.\n\n---\n\n## Summary\n\n1. **Use pipe** for 3+ operations\n2. **Use Option** for nullable chains\n3. **Use Either** for operations that can fail\n4. **Use map** to transform wrapped values\n5. **Use flatMap** to chain operations that might fail\n6. **Skip FP** when it hurts readability\n7. **Keep it simple** - if your team can't read it, it's not good code\n\n## Limitations\n- Use this skill only when the task clearly matches the scope described above.\n- Do not treat the output as a substitute for environment-specific validation, testing, or expert review.\n- Stop and ask for clarification if required inputs, permissions, safety boundaries, or success criteria are missing.","tags":["pragmatic","antigravity","awesome","skills","sickn33","agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding","ai-workflows","antigravity-skills"],"capabilities":["skill","source-sickn33","skill-fp-pragmatic","topic-agent-skills","topic-agentic-skills","topic-ai-agent-skills","topic-ai-agents","topic-ai-coding","topic-ai-workflows","topic-antigravity","topic-antigravity-skills","topic-claude-code","topic-claude-code-skills","topic-codex-cli","topic-codex-skills"],"categories":["antigravity-awesome-skills"],"synonyms":[],"warnings":[],"endpointUrl":"https://skills.sh/sickn33/antigravity-awesome-skills/fp-pragmatic","protocol":"skill","transport":"skills-sh","auth":{"type":"none","details":{"cli":"npx skills add sickn33/antigravity-awesome-skills","source_repo":"https://github.com/sickn33/antigravity-awesome-skills","install_from":"skills.sh"}},"qualityScore":"0.700","qualityRationale":"deterministic score 0.70 from registry signals: · indexed on github topic:agent-skills · 34793 github stars · SKILL.md body (15,842 chars)","verified":false,"liveness":"unknown","lastLivenessCheck":null,"agentReviews":{"count":0,"score_avg":null,"cost_usd_avg":null,"success_rate":null,"latency_p50_ms":null,"narrative_summary":null,"summary_updated_at":null},"enrichmentModel":"deterministic:skill-github:v1","enrichmentVersion":1,"enrichedAt":"2026-04-24T00:50:58.295Z","embedding":null,"createdAt":"2026-04-18T21:37:28.206Z","updatedAt":"2026-04-24T00:50:58.295Z","lastSeenAt":"2026-04-24T00:50:58.295Z","tsv":"'-2':255 '/api/users':958,986 '/array':558 '/either':404,550,646 '/function':185,298,411,565,653 '/option':291,542 '/taskeither':1296 '0':436,474,886,890,910,1277,1319,1458,1523,1676,1703 '1':159,254,612,1012,1820,1881 '10':425,464 '123':1331 '2':267,605,613,622,1053,1646,1668,1701,1830,1887 '3':230,381,614,1089,1840,1885,1893 '30':576 '4':521,1132,1846,1901 '5':594,623,1177,1908 '6':1917 '7':1924 '80':1863 '80/20':14,82,141 'a.city':348,808 'a.filter':1635,1694 'a.findfirst':862 'a.map':619,1641,1698 'a.reduce':909,1647,1702 'abstract':46 'academ':21,34 'acc':911,913 'activ':1640,1662,1697 'activeitem':1658 'activeitems.map':1665 'actual':40,87 'ad':1874 'address':789 'admin':1031,1049 'adopt':89 'age':422,428,433,435,440,445,461,467,471,473,477,483,494,575,1454,1462,1517,1528,1532,1534 'alic':574 'alway':762 'answer':764 'anyth':157 'apperror':1216,1229 'appli':369 'approach':15 'array':607,878 'ask':1620,1973 'assert':1180 'async':945,1288,1541,1822 'avoid':1629 'await':956,965,1329,1549,1562,1573 'basic':1859 'belt':1879 'benefit':152 'best':1688 'better':54,101,244 'bind':1853 'boolean':834 'bottom':248 'boundari':1981 'built':782 'call':168,258 'callback':1255 'caller':1097,1116 'cannot':441,478 'case':1037,1120,1233,1240,1246,1787 'catch':967,1057,1064,1072,1780 'categori':43 'chain':161,173,266,327,625,635,708,779,1348,1536,1749,1798,1892,1912 'chang':1007,1745 'cheat':1714 'check':274,302,774,1101,1345,1401 'citi':787,790,797 'clarif':1975 'class':1190,1845 'cleaner':1849 'clear':163,216,1036,1948 'clever':1628 'code':53,104,114,868,932,937,1010,1939 'collect':1832 'comfort':1815,1870 'comment':1279,1283,1322,1325,1338,1342 'common':1250 'complex':825 'concept':609 'config':1067,1069,1073,1080 'console.log':493,498 'const':193,203,207,211,219,332,421,460,486,569,577,588,595,610,615,654,666,694,718,786,796,838,858,905,954,976,1024,1039,1079,1179,1191,1198,1200,1205,1206,1213,1226,1297,1327,1372,1424,1477,1547,1560,1571,1587,1631,1657,1663,1669,1677,1690 'contain':529 'creat':170,902,1185 'criteria':1984 'critic':867 'cut':31 'd':1381 'd.manager':1383 'data':235,668,675,677,681,684,1145,1165,1410,1414,1416,1426,1479,1489,1491,1494,1634,1693 'data.filter':1659 'default':373,378,1731,1739 'defaultconfig':1074,1088 'defens':300 'depend':1842 'describ':1952 'develop':1624 'direct':257 'doesn':138,855,918 'doubl':596,616 'doubledvalu':1664 'doubledvalues.reduce':1671 'e':399,545,641 'e.bind':1486,1503,1516 'e.do':1485 'e.either':457,590,597,658,670,698,722,1147,1167,1481 'e.flatmap':728,730 'e.fold':1334,1795 'e.getorelse':1087 'e.isright':491,516 'e.left':469,476,510,690,704,1151,1171,1499,1512,1526,1773 'e.map':602,1530 'e.right':482,504,593,683,702,1493,1510,1524,1765 'e.toerror':988,1003 'e.trycatch':661,1082,1782 'earli':822 'easi':1006 'educ':78 'either':382,587,1895 'els':158,497 'email':679,686,688,692,696,703,706,1173,1395,1397,1438,1446,1504,1514,1531,1533 'email.includes':701 'employe':1353,1354,1374,1375,1392 'employee.department':1358,1379 'employee.department.manager':1362 'employee.department.manager.email':1366,1370 'environ':1964 'environment-specif':1963 'equal':1639 'error':384,393,431,439,447,499,511,515,591,598,740,981,996,1086,1134,1138,1156,1182,1187,1208,1214,1228,1407,1420,1437,1444,1453,1461,1473,1556,1569,1580,1774,1781,1791,1834 'error._tag':1232 'error.errors.join':1249 'error.id':1237 'everywher':282,1065 'except':388 'execut':1326 'exit':823 'expect':390 'expert':1969 'explicit':385 'explor':156,1819 'exploratori':76 'extract':531 'extractemail':667,729 'fail':629,634,713,717,1153,1582,1769,1826,1900,1916 'failur':391,414,512,737,1768 'faster':875 'fetch':957,985 'fetchcom':1275,1317 'fetchord':1550 'fetchordert':1595 'fetchpost':1268,1307 'fetchus':1261,1302 'field':693,1159,1172 'find':237 'findfirst':828 'findus':1103,1122 'fine':260,818 'first':28,533,1406,1472,1839 'five':145 'flatmap':624,744,1910 'flow':218,1636,1642 'fn':364,1748,1757,1783 'fn1':1807 'fn2':1808 'fn3':1809 'fold':1018 'forc':851,972,1115 'forget':1099 'format':195,213,225,707,1176 'found':998,1239,1559,1605 'fp':2,66,95,122,143,183,289,296,402,409,540,548,556,563,644,651,757,852,900,921,1294,1618,1722,1919 'fp-pragmat':1 'fp-ts':65,182,288,295,401,408,539,547,555,562,643,650,899,1293,1721 'free':8 'function':11,24,69,110,167,303,371,416,453,758,827,879,946,1102,1121,1143,1163,1351,1408,1542,1777 'generatereceipt':1584,1611 'get':17,1869 'getmanageremail':1352,1373,1391 'getus':947,977 'getuserc':304,333 'getvalidemail':719 'give':147 'golden':107 'good':936,1938 'ground':1686 'gt':1651 'guid':9,30 'guidanc':92 'handl':269,1038,1118,1724,1785,1862 'handleerror':1227,1335 'handlenopost':1274 'handlenous':1267 'hard':187 'harder':115 'hell':1256 'help':96,133,857 'hidden':413 'hot':871 'hurt':1922 'id':948,959,978,987,1104,1112,1123,1131,1193,1199,1262,1278,1299,1303,1320 'imper':873 'import':179,284,292,397,405,535,543,551,559,639,647,1289 'improv':1009 'inject':1843 'input':198,206,222,418,424,455,463,720,727,1978 'insid':191,528,567,586 'inside-out':190 'instead':164,1835 'intermedi':171,877,903 'invalid':432,470,664,705,1175,1445 'isnan':427,466 'item':829,830,833,835,839,841,844,846,861,1236,1660,1666,1695,1699 'item.status':1661,1696 'item.value':1667,1700 'jargon':7,35 'jargon-fre':6 'json':665 'json.parse':662,1070,1083 'junior':1623 'keep':103,769,1925 'know':920,942 'languag':352,502,743,785,1720 'left':738 'let':884,888,1066 'level':1812 'limit':1940 'linear':217 'liner':1078 'loaddata':1298,1330 'log':1244 'logic':242,826 'loop':812,816 'm':1385 'm.email':1387 'make':51,112,383 'mani':201 'map':522,1903 'master':153 'match':1225,1949 'matter':41 'maybenam':578 'maybeus':570,581 'mean':745 'messag':741,1025,1040,1161,1174 'middl':1685 'might':628,633,1098,1915 'miss':270,330,1119,1733,1986 'mode':415 'monoid.concat':1648 'monoid.empty':1649 'multipl':1343,1400,1852 'multipli':1645 'must':1421,1463,1500 'n':603,604,620,621,912,914 'name':239,573 'natur':265 'need':80,91,821 'negat':443,480 'nest':166,1014,1021 'new':430,438,995,1419,1436,1443,1452,1460,1555,1568,1579 'next':751 'nightmar':1023 'nonsens':47 'notat':1848 'notfound':1192,1196,1234 'noth':362,380,1737 'null':273,279,301,307,311,316,321,336,678,773,836,848,952,963,969,1027,1344,1356,1360,1364,1368,1492 'null/undefined':360,1725 'nullabl':1728,1750,1891 'number':420,459,592,599,611,618,881,882,883,895,908,1450,1521 'numbers.length':892 'o':286,537 'o.flatmap':341,345,363,801,805,1380,1384,1756 'o.fold':1044,1393 'o.fromnullable':339,343,347,354,799,803,807,1042,1127,1378,1382,1386,1729 'o.frompredicate':1650 'o.getorelse':349,372,809,1738 'o.map':582,1747 'o.none':1683,1713 'o.option':571,579,1125,1376 'o.some':572,1681,1711 'o.tonullable':864 'obj':1425,1467,1487,1505,1518 'obj.age':1449,1457,1520,1522,1525 'obj.email':1433,1507,1511 'obj.email.includes':1441,1509 'object':676,1417,1423,1490,1502 'often':1687 'one':927,1077 'one-lin':1076 'onerror':1784 'onleft':1796 'onright':1797 'oper':162,174,256,262,626,1751,1799,1823,1886,1897,1913 'option':268,568,778,1094,1347,1889 'order':177,1548,1553,1557,1564,1598,1599,1601,1603 'orderid':1544,1551,1589,1596 'output':1958 'overcompl':794 'overhead':22 'pars':197,204,205,210,223,1085 'parseag':417,454,488 'parseint':423,462 'parsejson':655,726 'path':733,872 'pattern':49,146,944,1224,1619,1818 'payment':1572,1581,1585 'payment.success':1577 'perform':866 'performance-crit':865 'permiss':1979 'pipe':160,180,221,229,252,293,338,406,560,580,600,617,648,725,798,860,907,983,1017,1041,1081,1258,1301,1306,1316,1332,1377,1390,1484,1594,1633,1692,1805,1883 'plain':351,501,742,1719 'pleas':1243 'point':63 'posit':1465 'post':1270,1272,1276,1282,1310,1312,1315,1318,1324,1337,1341 'potenti':329 'practic':5 'pragmat':3,23,61 'predic':831,843,863 'prefer':1655 'present':1742 'processord':1543,1588 'processpay':1574 'processpaymentt':1609 'program':12,25,70,111,759 'promis':950,1535,1546,1828 'prop':1637,1643 'quick':1004 'r':990,993,1000 'r.json':1002 'r.ok':991 'rawconfig':1071,1084 're':924 'read':26,117,176,189,243,930,1933 'readabl':1613,1923 'reader':1841 'real':1866 'real-world':1865 'receipt':1593 'record':1428,1496 'refactor':1251 'religion':128 'render':1280,1339 'replac':1013,1054,1090,1133,1827 'requir':1439,1455,1515,1529,1977 'respons':955 'response.json':966 'response.ok':961 'result':18,194,212,220,487,492,589,601,859,1328,1333,1632,1678,1691 'result.left':500 'result.right':496 'return':312,317,322,324,392,444,468,475,481,682,689,845,847,897,962,964,968,1092,1108,1126,1150,1170,1235,1242,1248,1266,1273,1359,1363,1367,1369,1466,1471,1583,1758,1767 'returntyp':1217,1220 'review':1970 'right':734,1654 'rule':108,1614 'rush':1857 'safe':1476 'safeti':1980 'scenario':1868 'scope':1951 'sendto':1396 'sendtodefault':1394 'sheet':1715 'show':37 'simpl':105,771,772,811,1927 'skill':1943 'skill-fp-pragmatic' 'skip':134,251,1918 'someth':368,1744,1753 'source-sickn33' 'specif':739,1186,1965 'start':62 'status':1638 'step':632,710 'stop':275,386,1837,1971 'string':308,337,419,456,458,657,659,671,672,687,697,699,700,721,723,724,949,979,1105,1124,1135,1142,1148,1160,1162,1194,1209,1230,1300,1355,1429,1434,1482,1497,1508,1545,1590,1592 'structur':904,1155 'substitut':1961 'succeed':520,748 'success':506,732,1759,1794,1983 'sum':885,894,898,906,1672,1674,1704,1706 'sumlarg':880 'summari':1880 'switch':1231 'syntax':1850 'tag':1195,1202,1210 'task':74,1947 'taskeith':1286,1538,1821 'te':1291 'te.flatmap':989,999,1304,1313,1597,1606,1608 'te.left':994,1602 'te.map':1309,1321,1610 'te.right':992,1600 'te.taskeither':980,1591 'te.trycatch':984,1001 'team':917,941,1930 'ternari':1015,1022 'test':1967 'theori':44 'thing':716,752 'threshold':1652,1680,1710 'throw':387,429,437,1404,1418,1435,1442,1451,1459,1554,1567,1578,1776 'throwaway':240 'today':1011 'togeth':637 'tool':125,1876 'top':246 'top-to-bottom':245 'topic-agent-skills' 'topic-agentic-skills' 'topic-ai-agent-skills' 'topic-ai-agents' 'topic-ai-coding' 'topic-ai-workflows' 'topic-antigravity' 'topic-antigravity-skills' 'topic-claude-code' 'topic-claude-code-skills' 'topic-codex-cli' 'topic-codex-skills' 'total':1670,1679,1682,1708,1709,1712 'transform':231,523,526,566,585,606,1740,1905 'translat':353,503 'treat':359,1956 'tri':749,953,1056,1063,1068,1754,1778 'try-catch':1055,1062 'try/catch':1829 'trycatch':1059 'ts':67,184,290,297,403,410,541,549,557,564,645,652,901,1295,1723 'type':452,1114,1137,1157,1183,1188,1215,1475 'type-saf':1474 'typeof':674,1218,1415,1432,1448,1488,1506,1519 'typescript':72,178,283,396,534,638,775,813,869,938,1019,1060,1095,1139,1184,1259,1349,1402,1539,1630,1656,1689 'u':342,802,1047,1110,1129 'u.address':344,804 'u.id':1111,1130 'u.isadmin':1048 'u.name':1050,1052 'unauthor':1201,1203,1219,1241 'undefin':281,1091,1107 'understand':1625 'unknown':313,318,323,350,660,669,791,810,1146,1166,1411,1430,1480,1498 'unpack':525 'unwrap':374 'use':57,120,129,228,376,484,756,777,1178,1388,1616,1734,1882,1888,1894,1902,1909,1941 'user':305,306,310,334,335,340,583,788,800,951,982,1026,1029,1033,1043,1046,1051,1106,1149,1169,1263,1265,1281,1305,1311,1314,1323,1336,1340,1412,1469,1483 'user.address':315 'user.address.city':320,325 'user.id':1269,1308 'user.isadmin':1030 'user.name':584,1032,1034 'user@example.com':735 'userinput':489 'users.find':1109,1128 'val':1673,1675,1705,1707 'valid':196,208,209,214,224,1144,1152,1164,1398,1513,1527,1561,1831,1966 'validated.error':1570 'validated.order':1575 'validated.success':1566 'validateemail':695,731 'validateord':1563 'validateordert':1607 'validateus':1409,1478 'validationerror':1158,1168 'validationfail':1207,1211,1247 'valu':271,331,358,395,505,509,527,1644,1764,1766,1907 'variabl':172,202,241 'view':83 'visibl':449 'want':59,1718 'whole':715 'win':1005 'without':19,272,524,530,1189,1844 'work':1760 'world':1867 'worth':88 'would':1621 'wrap':356,1726,1775,1906 'write':276 'x':278,280,355,517,1730,1806","prices":[{"id":"8123b6d2-db79-4037-8a77-93a6b436b43f","listingId":"bee18a2a-2d85-4db5-9414-669b1b5ebff2","amountUsd":"0","unit":"free","nativeCurrency":null,"nativeAmount":null,"chain":null,"payTo":null,"paymentMethod":"skill-free","isPrimary":true,"details":{"org":"sickn33","category":"antigravity-awesome-skills","install_from":"skills.sh"},"createdAt":"2026-04-18T21:37:28.206Z"}],"sources":[{"listingId":"bee18a2a-2d85-4db5-9414-669b1b5ebff2","source":"github","sourceId":"sickn33/antigravity-awesome-skills/fp-pragmatic","sourceUrl":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/fp-pragmatic","isPrimary":false,"firstSeenAt":"2026-04-18T21:37:28.206Z","lastSeenAt":"2026-04-24T00:50:58.295Z"}],"details":{"listingId":"bee18a2a-2d85-4db5-9414-669b1b5ebff2","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"sickn33","slug":"fp-pragmatic","github":{"repo":"sickn33/antigravity-awesome-skills","stars":34793,"topics":["agent-skills","agentic-skills","ai-agent-skills","ai-agents","ai-coding","ai-workflows","antigravity","antigravity-skills","claude-code","claude-code-skills","codex-cli","codex-skills","cursor","cursor-skills","developer-tools","gemini-cli","gemini-skills","kiro","mcp","skill-library"],"license":"mit","html_url":"https://github.com/sickn33/antigravity-awesome-skills","pushed_at":"2026-04-24T00:28:59Z","description":"Installable GitHub library of 1,400+ agentic skills for Claude Code, Cursor, Codex CLI, Gemini CLI, Antigravity, and more. Includes installer CLI, bundles, workflows, and official/community skill collections.","skill_md_sha":"29577c383603ad6119ab235bc804969297bc0fe5","skill_md_path":"skills/fp-pragmatic/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/fp-pragmatic"},"layout":"multi","source":"github","category":"antigravity-awesome-skills","frontmatter":{"name":"fp-pragmatic","description":"A practical, jargon-free guide to functional programming - the 80/20 approach that gets results without the academic overhead"},"skills_sh_url":"https://skills.sh/sickn33/antigravity-awesome-skills/fp-pragmatic"},"updatedAt":"2026-04-24T00:50:58.295Z"}}