{"id":"f1dd56a9-4c11-4fed-9198-ad8236a22d29","shortId":"nf9yEy","kind":"skill","title":"fp-ts-pragmatic","tagline":"A practical, jargon-free guide to fp-ts functional programming - the 80/20 approach that gets results without the academic overhead. Use when writing TypeScript with fp-ts library.","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 This Skill\n\n- When starting with fp-ts and need practical guidance\n- When writing TypeScript code that handles nullable values, errors, or async operations\n- When you want cleaner, more maintainable functional code without the academic overhead\n- When refactoring imperative code to functional style\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-ts-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-ts-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,872 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.733Z","embedding":null,"createdAt":"2026-04-18T21:37:32.025Z","updatedAt":"2026-04-24T00:50:58.733Z","lastSeenAt":"2026-04-24T00:50:58.733Z","tsv":"'-2':263 '/api/users':966,994 '/array':566 '/either':412,558,654 '/function':193,306,419,573,661 '/option':299,550 '/taskeither':1304 '0':444,482,894,898,918,1285,1327,1466,1531,1684,1711 '1':167,262,620,1020,1828,1889 '10':433,472 '123':1339 '2':275,613,621,630,1061,1654,1676,1709,1838,1895 '3':238,389,622,1097,1848,1893,1901 '30':584 '4':529,1140,1854,1909 '5':602,631,1185,1916 '6':1925 '7':1932 '80':1871 '80/20':18,149 'a.city':356,816 'a.filter':1643,1702 'a.findfirst':870 'a.map':627,1649,1706 'a.reduce':917,1655,1710 'abstract':59 'academ':25,47,105 'acc':919,921 'activ':1648,1670,1705 'activeitem':1666 'activeitems.map':1673 'actual':53 'ad':1882 'address':797 'admin':1039,1057 'age':430,436,441,443,448,453,469,475,479,481,485,491,502,583,1462,1470,1525,1536,1540,1542 'alic':582 'alway':770 'answer':772 'anyth':165 'apperror':1224,1237 'appli':377 'approach':19 'array':615,886 'ask':1628,1981 'assert':1188 'async':93,953,1296,1549,1830 'avoid':1637 'await':964,973,1337,1557,1570,1581 'basic':1867 'belt':1887 'benefit':160 'best':1696 'better':67,252 'bind':1861 'boolean':842 'bottom':256 'boundari':1989 'built':790 'call':176,266 'callback':1263 'caller':1105,1124 'cannot':449,486 'case':1045,1128,1241,1248,1254,1795 'catch':975,1065,1072,1080,1788 'categori':56 'chain':169,181,274,335,633,643,716,787,1356,1544,1757,1806,1900,1920 'chang':1015,1753 'cheat':1722 'check':282,310,782,1109,1353,1409 'citi':795,798,805 'clarif':1983 'class':1198,1853 'cleaner':98,1857 'clear':171,224,1044,1956 'clever':1636 'code':66,86,102,110,122,876,940,945,1018,1947 'collect':1840 'comfort':1823,1878 'comment':1287,1291,1330,1333,1346,1350 'common':1258 'complex':833 'concept':617 'config':1075,1077,1081,1088 'console.log':501,506 'const':201,211,215,219,227,340,429,468,494,577,585,596,603,618,623,662,674,702,726,794,804,846,866,913,962,984,1032,1047,1087,1187,1199,1206,1208,1213,1214,1221,1234,1305,1335,1380,1432,1485,1555,1568,1579,1595,1639,1665,1671,1677,1685,1698 'contain':537 'creat':178,910,1193 'criteria':1992 'critic':875 'cut':44 'd':1389 'd.manager':1391 'data':243,676,683,685,689,692,1153,1173,1418,1422,1424,1434,1487,1497,1499,1502,1642,1701 'data.filter':1667 'default':381,386,1739,1747 'defaultconfig':1082,1096 'defens':308 'depend':1850 'describ':1960 'develop':1632 'direct':265 'doesn':146,863,926 'doubl':604,624 'doubledvalu':1672 'doubledvalues.reduce':1679 'e':407,553,649 'e.bind':1494,1511,1524 'e.do':1493 'e.either':465,598,605,666,678,706,730,1155,1175,1489 'e.flatmap':736,738 'e.fold':1342,1803 'e.getorelse':1095 'e.isright':499,524 'e.left':477,484,518,698,712,1159,1179,1507,1520,1534,1781 'e.map':610,1538 'e.right':490,512,601,691,710,1501,1518,1532,1773 'e.toerror':996,1011 'e.trycatch':669,1090,1790 'earli':830 'easi':1014 'either':390,595,1903 'els':166,505 'email':687,694,696,700,704,711,714,1181,1403,1405,1446,1454,1512,1522,1539,1541 'email.includes':709 'employe':1361,1362,1382,1383,1400 'employee.department':1366,1387 'employee.department.manager':1370 'employee.department.manager.email':1374,1378 'environ':1972 'environment-specif':1971 'equal':1647 'error':91,392,401,439,447,455,507,519,523,599,606,748,989,1004,1094,1142,1146,1164,1190,1195,1216,1222,1236,1415,1428,1445,1452,1461,1469,1481,1564,1577,1588,1782,1789,1799,1842 'error._tag':1240 'error.errors.join':1257 'error.id':1245 'everywher':290,1073 'except':396 'execut':1334 'exit':831 'expect':398 'expert':1977 'explicit':393 'explor':164,1827 'extract':539 'extractemail':675,737 'fail':637,642,721,725,1161,1590,1777,1834,1908,1924 'failur':399,422,520,745,1776 'faster':883 'fetch':965,993 'fetchcom':1283,1325 'fetchord':1558 'fetchordert':1603 'fetchpost':1276,1315 'fetchus':1269,1310 'field':701,1167,1180 'find':245 'findfirst':836 'findus':1111,1130 'fine':268,826 'first':41,541,1414,1480,1847 'five':153 'flatmap':632,752,1918 'flow':226,1644,1650 'fn':372,1756,1765,1791 'fn1':1815 'fn2':1816 'fn3':1817 'fold':1026 'forc':859,980,1123 'forget':1107 'format':203,221,233,715,1184 'found':1006,1247,1567,1613 'fp':2,13,33,77,130,151,191,297,304,410,417,548,556,564,571,652,659,765,860,908,929,1302,1626,1730,1927 'fp-ts':12,32,76,190,296,303,409,416,547,555,563,570,651,658,907,1301,1729 'fp-ts-pragmat':1 'free':9 'function':15,37,101,112,118,175,311,379,424,461,766,835,887,954,1110,1129,1151,1171,1359,1416,1550,1785 'generatereceipt':1592,1619 'get':21,1877 'getmanageremail':1360,1381,1399 'getus':955,985 'getuserc':312,341 'getvalidemail':727 'give':155 'golden':115 'good':944,1946 'ground':1694 'gt':1659 'guid':10,43 'guidanc':82 'handl':88,277,1046,1126,1732,1793,1870 'handleerror':1235,1343 'handlenopost':1282 'handlenous':1275 'hard':195 'harder':123 'hell':1264 'help':141,865 'hidden':421 'hot':879 'hurt':1930 'id':956,967,986,995,1112,1120,1131,1139,1201,1207,1270,1286,1307,1311,1328 'imper':109,881 'import':187,292,300,405,413,543,551,559,567,647,655,1297 'improv':1017 'inject':1851 'input':206,214,230,426,432,463,471,728,735,1986 'insid':199,536,575,594 'inside-out':198 'instead':172,1843 'intermedi':179,885,911 'invalid':440,478,672,713,1183,1453 'isnan':435,474 'item':837,838,841,843,847,849,852,854,869,1244,1668,1674,1703,1707 'item.status':1669,1704 'item.value':1675,1708 'jargon':8,48 'jargon-fre':7 'json':673 'json.parse':670,1078,1091 'junior':1631 'keep':777,1933 'know':928,950 'languag':360,510,751,793,1728 'left':746 'let':892,896,1074 'level':1820 'librari':35 'limit':1948 'linear':225 'liner':1086 'loaddata':1306,1338 'log':1252 'logic':250,834 'loop':820,824 'm':1393 'm.email':1395 'maintain':100 'make':64,120,391 'mani':209 'map':530,1911 'master':161 'match':1233,1957 'matter':54 'maybenam':586 'maybeus':578,589 'mean':753 'messag':749,1033,1048,1169,1182 'middl':1693 'might':636,641,1106,1923 'miss':278,338,1127,1741,1994 'mode':423 'monoid.concat':1656 'monoid.empty':1657 'multipl':1351,1408,1860 'multipli':1653 'must':1429,1471,1508 'n':611,612,628,629,920,922 'name':247,581 'natur':273 'need':80,829 'negat':451,488 'nest':174,1022,1029 'new':438,446,1003,1427,1444,1451,1460,1468,1563,1576,1587 'next':759 'nightmar':1031 'nonsens':60 'notat':1856 'notfound':1200,1204,1242 'noth':370,388,1745 'null':281,287,309,315,319,324,329,344,686,781,844,856,960,971,977,1035,1352,1364,1368,1372,1376,1500 'null/undefined':368,1733 'nullabl':89,1736,1758,1899 'number':428,467,600,607,619,626,889,890,891,903,916,1458,1529 'numbers.length':900 'o':294,545 'o.flatmap':349,353,371,809,813,1388,1392,1764 'o.fold':1052,1401 'o.fromnullable':347,351,355,362,807,811,815,1050,1135,1386,1390,1394,1737 'o.frompredicate':1658 'o.getorelse':357,380,817,1746 'o.map':590,1755 'o.none':1691,1721 'o.option':579,587,1133,1384 'o.some':580,1689,1719 'o.tonullable':872 'obj':1433,1475,1495,1513,1526 'obj.age':1457,1465,1528,1530,1533 'obj.email':1441,1515,1519 'obj.email.includes':1449,1517 'object':684,1425,1431,1498,1510 'often':1695 'one':935,1085 'one-lin':1084 'onerror':1792 'onleft':1804 'onright':1805 'oper':94,170,182,264,270,634,1759,1807,1831,1894,1905,1921 'option':276,576,786,1102,1355,1897 'order':185,1556,1561,1565,1572,1606,1607,1609,1611 'orderid':1552,1559,1597,1604 'output':1966 'overcompl':802 'overhead':26,106 'pars':205,212,213,218,231,1093 'parseag':425,462,496 'parseint':431,470 'parsejson':663,734 'path':741,880 'pattern':62,154,952,1232,1627,1826 'payment':1580,1589,1593 'payment.success':1585 'perform':874 'performance-crit':873 'permiss':1987 'pipe':168,188,229,237,260,301,346,414,568,588,608,625,656,733,806,868,915,991,1025,1049,1089,1266,1309,1314,1324,1340,1385,1398,1492,1602,1641,1700,1813,1891 'plain':359,509,750,1727 'pleas':1251 'posit':1473 'post':1278,1280,1284,1290,1318,1320,1323,1326,1332,1345,1349 'potenti':337 'practic':6,81 'pragmat':4,36 'predic':839,851,871 'prefer':1663 'present':1750 'processord':1551,1596 'processpay':1582 'processpaymentt':1617 'program':16,38,119,767 'promis':958,1543,1554,1836 'prop':1645,1651 'quick':1012 'r':998,1001,1008 'r.json':1010 'r.ok':999 'rawconfig':1079,1092 're':932 'read':39,125,184,197,251,938,1941 'readabl':1621,1931 'reader':1849 'real':1874 'real-world':1873 'receipt':1601 'record':1436,1504 'refactor':108,1259 'religion':136 'render':1288,1347 'replac':1021,1062,1098,1141,1835 'requir':1447,1463,1523,1537,1985 'respons':963 'response.json':974 'response.ok':969 'result':22,202,220,228,495,500,597,609,867,1336,1341,1640,1686,1699 'result.left':508 'result.right':504 'return':320,325,330,332,400,452,476,483,489,690,697,853,855,905,970,972,976,1100,1116,1134,1158,1178,1243,1250,1256,1274,1281,1367,1371,1375,1377,1474,1479,1591,1766,1775 'returntyp':1225,1228 'review':1978 'right':742,1662 'rule':116,1622 'rush':1865 'safe':1484 'safeti':1988 'scenario':1876 'scope':1959 'sendto':1404 'sendtodefault':1402 'sheet':1723 'show':50 'simpl':779,780,819,1935 'skill':72,1951 'skill-fp-ts-pragmatic' 'skip':142,259,1926 'someth':376,1752,1761 'source-sickn33' 'specif':747,1194,1973 'start':74 'status':1646 'step':640,718 'stop':283,394,1845,1979 'string':316,345,427,464,466,665,667,679,680,695,705,707,708,729,731,732,957,987,1113,1132,1143,1150,1156,1168,1170,1202,1217,1238,1308,1363,1437,1442,1490,1505,1516,1553,1598,1600 'structur':912,1163 'style':113 'substitut':1969 'succeed':528,756 'success':514,740,1767,1802,1991 'sum':893,902,906,914,1680,1682,1712,1714 'sumlarg':888 'summari':1888 'switch':1239 'syntax':1858 'tag':1203,1210,1218 'task':1955 'taskeith':1294,1546,1829 'te':1299 'te.flatmap':997,1007,1312,1321,1605,1614,1616 'te.left':1002,1610 'te.map':1317,1329,1618 'te.right':1000,1608 'te.taskeither':988,1599 'te.trycatch':992,1009 'team':925,949,1938 'ternari':1023,1030 'test':1975 'theori':57 'thing':724,760 'threshold':1660,1688,1718 'throw':395,437,445,1412,1426,1443,1450,1459,1467,1562,1575,1586,1784 'throwaway':248 'today':1019 'togeth':645 'tool':133,1884 'top':254 'top-to-bottom':253 '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':1678,1687,1690,1716,1717,1720 'transform':239,531,534,574,593,614,1748,1913 'translat':361,511 'treat':367,1964 'tri':757,961,1064,1071,1076,1762,1786 'try-catch':1063,1070 'try/catch':1837 'trycatch':1067 'ts':3,14,34,78,192,298,305,411,418,549,557,565,572,653,660,909,1303,1731 'type':460,1122,1145,1165,1191,1196,1223,1483 'type-saf':1482 'typeof':682,1226,1423,1440,1456,1496,1514,1527 'typescript':30,85,186,291,404,542,646,783,821,877,946,1027,1068,1103,1147,1192,1267,1357,1410,1547,1638,1664,1697 'u':350,810,1055,1118,1137 'u.address':352,812 'u.id':1119,1138 'u.isadmin':1056 'u.name':1058,1060 'unauthor':1209,1211,1227,1249 'undefin':289,1099,1115 'understand':1633 'unknown':321,326,331,358,668,677,799,818,1154,1174,1419,1438,1488,1506 'unpack':533 'unwrap':382 'use':27,70,128,137,236,384,492,764,785,1186,1396,1624,1742,1890,1896,1902,1910,1917,1949 'user':313,314,318,342,343,348,591,796,808,959,990,1034,1037,1041,1051,1054,1059,1114,1157,1177,1271,1273,1289,1313,1319,1322,1331,1344,1348,1420,1477,1491 'user.address':323 'user.address.city':328,333 'user.id':1277,1316 'user.isadmin':1038 'user.name':592,1040,1042 'user@example.com':743 'userinput':497 'users.find':1117,1136 'val':1681,1683,1713,1715 'valid':204,216,217,222,232,1152,1160,1172,1406,1521,1535,1569,1839,1974 'validated.error':1578 'validated.order':1583 'validated.success':1574 'validateemail':703,739 'validateord':1571 'validateordert':1615 'validateus':1417,1486 'validationerror':1166,1176 'validationfail':1215,1219,1255 'valu':90,279,339,366,403,513,517,535,1652,1772,1774,1915 'variabl':180,210,249 'visibl':457 'want':97,1726 'whole':723 'win':1013 'without':23,103,280,532,538,1197,1852 'work':1768 'world':1875 'would':1629 'wrap':364,1734,1783,1914 'write':29,84,284 'x':286,288,363,525,1738,1814","prices":[{"id":"657b9f99-9246-43c2-9d7a-c40df697e979","listingId":"f1dd56a9-4c11-4fed-9198-ad8236a22d29","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:32.025Z"}],"sources":[{"listingId":"f1dd56a9-4c11-4fed-9198-ad8236a22d29","source":"github","sourceId":"sickn33/antigravity-awesome-skills/fp-ts-pragmatic","sourceUrl":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/fp-ts-pragmatic","isPrimary":false,"firstSeenAt":"2026-04-18T21:37:32.025Z","lastSeenAt":"2026-04-24T00:50:58.733Z"}],"details":{"listingId":"f1dd56a9-4c11-4fed-9198-ad8236a22d29","quickStartSnippet":null,"exampleRequest":null,"exampleResponse":null,"schema":null,"openapiUrl":null,"agentsTxtUrl":null,"citations":[],"useCases":[],"bestFor":[],"notFor":[],"kindDetails":{"org":"sickn33","slug":"fp-ts-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":"6c51a2e0e3aec282b3ec0622e637ec7674c4c7d8","skill_md_path":"skills/fp-ts-pragmatic/SKILL.md","default_branch":"main","skill_tree_url":"https://github.com/sickn33/antigravity-awesome-skills/tree/main/skills/fp-ts-pragmatic"},"layout":"multi","source":"github","category":"antigravity-awesome-skills","frontmatter":{"name":"fp-ts-pragmatic","description":"A practical, jargon-free guide to fp-ts functional programming - the 80/20 approach that gets results without the academic overhead. Use when writing TypeScript with fp-ts library."},"skills_sh_url":"https://skills.sh/sickn33/antigravity-awesome-skills/fp-ts-pragmatic"},"updatedAt":"2026-04-24T00:50:58.733Z"}}