{"id":21662525,"url":"https://github.com/jordanburke/functype","last_synced_at":"2026-04-18T19:01:23.084Z","repository":{"id":57243054,"uuid":"56967111","full_name":"jordanburke/functype","owner":"jordanburke","description":"A Functional Library for TypeScript based on Scala","archived":false,"fork":false,"pushed_at":"2026-04-12T18:32:38.000Z","size":4560,"stargazers_count":8,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-04-12T19:24:58.247Z","etag":null,"topics":["functional-programming","typescript"],"latest_commit_sha":null,"homepage":"https://functype.org/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/jordanburke.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":"docs/ROADMAP.md","authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2016-04-24T11:02:13.000Z","updated_at":"2026-04-12T18:32:38.000Z","dependencies_parsed_at":"2024-09-16T23:38:24.666Z","dependency_job_id":"bde0612b-1273-4e39-8fc0-2d6649eadf69","html_url":"https://github.com/jordanburke/functype","commit_stats":null,"previous_names":["jordanburke/functype","jordanburke/functionaltypescript"],"tags_count":31,"template":false,"template_full_name":null,"purl":"pkg:github/jordanburke/functype","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jordanburke%2Ffunctype","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jordanburke%2Ffunctype/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jordanburke%2Ffunctype/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jordanburke%2Ffunctype/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/jordanburke","download_url":"https://codeload.github.com/jordanburke/functype/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/jordanburke%2Ffunctype/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31980783,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T17:30:12.329Z","status":"ssl_error","status_checked_at":"2026-04-18T17:29:59.069Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["functional-programming","typescript"],"created_at":"2024-11-25T10:16:43.317Z","updated_at":"2026-04-18T19:01:23.046Z","avatar_url":"https://github.com/jordanburke.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Functype\n\n![NPM Version](https://img.shields.io/npm/v/functype?link=https%3A%2F%2Fwww.npmjs.com%2Fpackage%2Ffunctype)\n[![Node.js Build](https://github.com/jordanburke/functype/actions/workflows/pnpm-build.yml/badge.svg)](https://github.com/jordanburke/functype/actions/workflows/pnpm-build.yml)\n\n## A Functional Programming Library for TypeScript\n\nFunctype is a lightweight functional programming library for TypeScript, drawing inspiration from functional programming paradigms, the Scala Standard Library, and ZIO. It provides a comprehensive set of utilities and abstractions designed to facilitate functional programming within TypeScript applications.\n\n[API Documentation](https://jordanburke.github.io/functype/)\n\n### CLI Documentation\n\nGet LLM-optimized API reference directly in your terminal:\n\n```bash\nnpx functype              # Overview of all types\nnpx functype Option       # Detailed info for Option type\nnpx functype interfaces   # All interface definitions\nnpx functype --json       # JSON output for programmatic use\n```\n\n## Core Principles\n\n- **Zero Dependencies**: No third-party runtime dependencies — just TypeScript\n- **Immutability**: All data structures are immutable, promoting predictable and side-effect-free code\n- **Type Safety**: Leverages TypeScript's type system to ensure compile-time safety\n- **Composability**: Provides abstractions for building complex programs from simple components\n- **Functional Paradigms**: Embraces concepts like monads, functors, and type classes\n- **Unified Interface**: All data structures implement a common hierarchy of interfaces for consistency\n\n## Key Features\n\n- **Option Type**: Handle nullable values with `Some` and `None` types\n- **Either Type**: Express computation results with potential failures using `Left` and `Right`\n- **List, Set, Map**: Immutable collection types with functional operators\n- **Try Type**: Safely execute operations that might throw exceptions\n- **Do-notation**: Scala-like for-comprehensions with **optimized List performance** (up to 12x faster than traditional flatMap)\n- **Task**: Handle synchronous and asynchronous operations with error handling\n- **Http**: Typed HTTP fetch wrapper returning IO effects with HttpError ADT, auto content-type detection, and configurable clients\n- **Lazy**: Deferred computation with memoization\n- **Tuple**: Type-safe fixed-length arrays\n- **Typeable**: Runtime type identification with compile-time safety\n- **Branded Types**: Nominal typing in TypeScript's structural type system\n- **Error Formatting**: Utilities for improved error visualization and logging\n- **Unified Type Classes**: Consistent interfaces across all data structures\n\n## Installation\n\n```bash\n# NPM\nnpm install functype\n\n# Yarn\nyarn add functype\n\n# PNPM\npnpm add functype\n\n# Bun\nbun add functype\n```\n\n### Bundle Size Optimization\n\nFunctype is optimized for tree-shaking and offers multiple import strategies to minimize bundle size:\n\n```typescript\n// Selective module imports (recommended for production)\nimport { Option } from \"functype/option\"\nimport { Either } from \"functype/either\"\n\n// Direct constructor imports (smallest bundle)\nimport { some, none } from \"functype/option\"\n```\n\nFor detailed optimization strategies, see the [Bundle Optimization Guide](docs/BUNDLE_OPTIMIZATION.md).\n\n## Usage Examples\n\n### Option\n\n{@includeCode test/docs/documentation-examples.spec.ts#readme-option-basic}\n\n### Either\n\n{@includeCode test/docs/documentation-examples.spec.ts#readme-either-basic}\n\n### List\n\n```typescript\nimport { List } from \"functype\"\n\nconst numbers = List([1, 2, 3, 4])\n\n// Transform\nconst doubled = numbers.map((x) =\u003e x * 2) // List([2, 4, 6, 8])\n\n// Filter\nconst evens = numbers.filter((x) =\u003e x % 2 === 0) // List([2, 4])\n\n// Reduce\nconst sum = numbers.foldLeft(0)((acc, x) =\u003e acc + x) // 10\n\n// Add/remove elements (immutably)\nconst withFive = numbers.add(5) // List([1, 2, 3, 4, 5])\nconst without3 = numbers.remove(3) // List([1, 2, 4])\n\n// Universal container operations\nconst hasEven = numbers.exists((x) =\u003e x % 2 === 0) // true\nconst firstEven = numbers.find((x) =\u003e x % 2 === 0) // Some(2)\nconst evenCount = numbers.count((x) =\u003e x % 2 === 0) // 2\n```\n\n### Try\n\n```typescript\nimport { Try } from \"functype\"\n\n// Safely execute code that might throw\nconst result = Try(() =\u003e {\n  // Potentially throwing operation\n  return JSON.parse('{\"name\": \"John\"}')\n})\n\n// Handle success/failure\nif (result.isSuccess()) {\n  console.log(\"Result:\", result.get())\n} else {\n  console.error(\"Error:\", result.error)\n}\n\n// Transform with map (only applies on Success)\nconst name = result.map((obj) =\u003e obj.name)\n\n// Convert to Either\nconst either = result.toEither()\n```\n\n### Lazy\n\n```typescript\nimport { Lazy } from \"functype\"\n\n// Create lazy computations\nconst expensive = Lazy(() =\u003e {\n  console.log(\"Computing...\")\n  return Math.random() * 1000\n})\n\n// Value is computed on first access and memoized\nconst value1 = expensive.get() // Logs \"Computing...\", returns number\nconst value2 = expensive.get() // Returns same number, no log\n\n// Transform lazy values\nconst doubled = expensive.map((x) =\u003e x * 2)\nconst formatted = doubled.map((x) =\u003e `Value: ${x}`)\n\n// Chain computations\nconst result = Lazy(() =\u003e 10)\n  .flatMap((x) =\u003e Lazy(() =\u003e x + 5))\n  .map((x) =\u003e x * 2)\n  .get() // 30\n```\n\n### Do-notation (High-Performance For-Comprehensions)\n\nFunctype provides generator-based Do-notation for monadic composition, similar to Scala's for-comprehensions, with **significant performance advantages for List operations**:\n\n```typescript\nimport { Do, DoAsync, $ } from \"functype\"\nimport { Option, Right, Left, List, Try } from \"functype\"\n\n// Chain multiple Option operations\nconst result = Do(function* () {\n  const x = yield* $(Option(5)) // Extract value from Option\n  const y = yield* $(Option(10)) // Extract value from another Option\n  const z = x + y // Regular computation\n  return z * 2 // Return final result\n})\n// result: Option\u003cnumber\u003e with value 30\n\n// Mix different monad types (with Reshapeable)\nconst mixed = Do(function* () {\n  const a = yield* $(Option(5)) // From Option\n  const b = yield* $(Right\u003cstring, number\u003e(10)) // From Either\n  const c = yield* $(List([15])) // From List\n  const d = yield* $(Try(() =\u003e 20)) // From Try\n  return a + b + c + d\n})\n// Convert result to desired type\nconst asOption = mixed.toOption() // Option\u003cnumber\u003e with value 50\n\n// Error propagation - short-circuits on failure\nconst validation = Do(function* () {\n  const email = yield* $(validateEmail(\"user@example.com\")) // Returns Option\n  const user = yield* $(fetchUser(email)) // Returns Either\n  const profile = yield* $(loadProfile(user.id)) // Returns Try\n  return profile\n})\n// If any step fails, the entire computation short-circuits\n\n// List comprehensions - up to 12x FASTER than traditional flatMap!\nconst pairs = Do(function* () {\n  const x = yield* $(List([1, 2, 3]))\n  const y = yield* $(List([10, 20]))\n  return { x, y, product: x * y }\n})\n// pairs: List with 6 elements (all combinations)\n\n// Performance comparison:\n// Traditional: list.flatMap(x =\u003e list.flatMap(y =\u003e List([{x, y}]))) - slower\n// Do-notation: 2.5x to 12x faster for cartesian products!\n\n// Async operations with DoAsync\nconst asyncResult = await DoAsync(async function* () {\n  const user = yield* $(await fetchUserAsync(userId)) // Async Option\n  const score = yield* $(await getScoreAsync(user.id)) // Async Either\n  const bonus = yield* $(await calculateBonus(score)) // Async Try\n  return score + bonus\n})\n```\n\n**Performance Advantages:**\n\n- **List Comprehensions**: 2.5x to 12x faster than nested flatMap chains\n- **Optimized for Cartesian Products**: Efficient handling of multiple List yields\n- **Smart Caching**: Constructor lookups cached after first type detection\n- **Inline Helpers**: Reduced overhead from repeated type checks\n\n**When to Use Do-notation:**\n\n✅ **Best for:**\n\n- Complex List comprehensions (huge performance win!)\n- Cartesian products and filtered combinations\n- Mixed monad types (leveraging Reshapeable)\n- Improved readability for multi-step operations\n\n⚠️ **Consider alternatives for:**\n\n- Simple 2-3 step Option/Either chains (traditional flatMap is ~2x faster)\n- Performance-critical hot paths with simple monads\n- Early termination scenarios (flatMap auto-short-circuits more efficiently)\n\n**Key Differences from Scala:**\n\n- Uses `yield* $(monad)` instead of `x \u003c- monad`\n- No native guard syntax (use conditions with early return)\n- Always returns the type of the first yielded monad\n- Mixed types supported via Reshapeable interface\n\n### Task\n\nTask v2 provides structured error handling with the **Ok/Err pattern**, returning `TaskOutcome\u003cT\u003e` for all operations:\n\n```typescript\nimport { Task, Ok, Err, type TaskOutcome } from \"functype\"\n\n// Task v2: All operations return TaskOutcome\u003cT\u003e\nconst syncResult = Task().Sync(() =\u003e \"success\")\n// Returns: TaskSuccess\u003cstring\u003e (extends TaskOutcome\u003cstring\u003e)\n\nconst asyncResult = await Task().Async(async () =\u003e \"value\")\n// Returns: TaskOutcome\u003cstring\u003e\n\n// Explicit Ok/Err returns for precise control\nconst explicitResult = await Task().Async(async (): Promise\u003cTaskOutcome\u003cstring\u003e\u003e =\u003e {\n  if (Math.random() \u003e 0.5) {\n    return Ok(\"success\") // Explicit success\n  }\n  return Err\u003cstring\u003e(\"failed\") // Explicit failure\n})\n\n// Auto-wrapping: raw values become Ok, thrown errors become Err\nconst autoWrapped = await Task().Async(async () =\u003e {\n  if (condition) {\n    return \"raw value\" // Auto-wrapped as Ok(\"raw value\")\n  }\n  throw new Error(\"failed\") // Auto-wrapped as Err(error)\n})\n\n// Error recovery: error handlers can return Ok\nconst recovered = await Task().Async(\n  async () =\u003e {\n    throw new Error(\"initial error\")\n  },\n  async (error) =\u003e Ok(\"recovered from error\"), // Recovery!\n)\n\n// Working with results\nif (asyncResult.isSuccess()) {\n  console.log(asyncResult.value) // Access the success value\n} else {\n  console.error(asyncResult.error) // Access the error (Throwable)\n}\n\n// Chaining with TaskOutcome\nconst chainedResult = await Task().Async(async () =\u003e {\n  const firstResult = await Task().Async(async () =\u003e \"first\")\n  if (firstResult.isFailure()) {\n    return firstResult // Propagate failure\n  }\n\n  const secondResult = await Task().Async(async () =\u003e \"second\")\n  if (secondResult.isFailure()) {\n    return secondResult\n  }\n\n  return Ok(`${firstResult.value} + ${secondResult.value}`)\n})\n\n// Converting promise-based functions to Task\nconst fetchUserAPI = (userId: string): Promise\u003cUser\u003e =\u003e fetch(`/api/users/${userId}`).then((r) =\u003e r.json())\n\nconst fetchUser = Task.fromPromise(fetchUserAPI)\n// Returns: (userId: string) =\u003e Promise\u003cTaskOutcome\u003cUser\u003e\u003e\n\nconst userResult = await fetchUser(\"user123\")\nif (userResult.isSuccess()) {\n  console.log(userResult.value) // User object\n}\n\n// Convert TaskOutcome back to Promise (for interop)\nconst promise = Task.toPromise(asyncResult)\n// Success → resolves with value\n// Failure → rejects with error\n```\n\n### Branded Types\n\n```typescript\nimport { Brand, ValidatedBrand } from \"functype/branded\"\n\n// Create branded types for stronger type safety\ntype UserId = Brand\u003c\"UserId\", string\u003e\ntype Email = Brand\u003c\"Email\", string\u003e\n\n// Simple branding - branded values ARE primitives!\nconst userId = Brand(\"UserId\", \"U123456\")\nconsole.log(userId) // \"U123456\" - it IS a string\nconsole.log(typeof userId) // \"string\"\nconsole.log(userId.toUpperCase()) // \"U123456\" - string methods work!\n\n// Runtime-validated branding for safer input handling\nconst EmailValidator = ValidatedBrand(\"Email\", (s: string) =\u003e /^[^@]+@[^@]+\\.[^@]+$/.test(s))\nconst UserIdValidator = ValidatedBrand(\"UserId\", (s: string) =\u003e /^U\\d{6}$/.test(s))\n\n// Safe creation with Option/Either return types\nconst email = EmailValidator.of(\"user@example.com\") // Some(Brand\u003c\"Email\", string\u003e)\nconst invalidEmail = EmailValidator.of(\"invalid\") // None\n\nconst userResult = UserIdValidator.from(\"U123456\") // Right(Brand\u003c\"UserId\", string\u003e)\nconst userError = UserIdValidator.from(\"invalid\") // Left(\"Invalid UserId: validation failed\")\n\n// Type safety in action\nfunction getUserByEmail(email: Email): User {\n  /* ... */\n}\n\n// These calls are type-safe\nconst userId = UserId(\"U123456\")\nconst email = Email(\"user@example.com\")\nconst user = getUserByEmail(email) // Works\n\n// These would be type errors\ngetUserByEmail(\"invalid\") // Type error: Argument of type 'string' is not assignable to parameter of type 'Email'\ngetUserByEmail(userId) // Type error: Argument of type 'UserId' is not assignable to parameter of type 'Email'\n```\n\n## Conditional Programming\n\nFunctype provides `Cond` and `Match` for functional conditional logic without early returns:\n\n### Cond\n\n```typescript\nimport { Cond } from \"functype\"\n\n// Replace if-else chains with Cond\nconst grade = Cond\u003cnumber, string\u003e()\n  .case((score) =\u003e score \u003e= 90, \"A\")\n  .case((score) =\u003e score \u003e= 80, \"B\")\n  .case((score) =\u003e score \u003e= 70, \"C\")\n  .case((score) =\u003e score \u003e= 60, \"D\")\n  .default(\"F\")\n\nconsole.log(grade(85)) // \"B\"\nconsole.log(grade(55)) // \"F\"\n\n// With transformation\nconst discount = Cond\u003cnumber, number\u003e()\n  .case(\n    (qty) =\u003e qty \u003e= 100,\n    (qty) =\u003e qty * 0.2, // 20% off for 100+\n  )\n  .case(\n    (qty) =\u003e qty \u003e= 50,\n    (qty) =\u003e qty * 0.1, // 10% off for 50+\n  )\n  .case(\n    (qty) =\u003e qty \u003e= 10,\n    (qty) =\u003e qty * 0.05, // 5% off for 10+\n  )\n  .default(0)\n\nconsole.log(discount(150)) // 30 (20% of 150)\n```\n\n### Match\n\n```typescript\nimport { Match } from \"functype\"\n\n// Pattern matching with Match\ntype Status = \"pending\" | \"approved\" | \"rejected\" | \"cancelled\"\n\nconst statusMessage = Match\u003cStatus, string\u003e()\n  .case(\"pending\", \"Your request is being processed\")\n  .case(\"approved\", \"Your request has been approved!\")\n  .case(\"rejected\", \"Sorry, your request was rejected\")\n  .case(\"cancelled\", \"Your request was cancelled\")\n  .exhaustive()\n\nconsole.log(statusMessage(\"approved\")) // \"Your request has been approved!\"\n\n// Match with predicates\nconst numberType = Match\u003cnumber, string\u003e()\n  .case(0, \"zero\")\n  .case((n) =\u003e n \u003e 0, \"positive\")\n  .case((n) =\u003e n \u003c 0, \"negative\")\n  .exhaustive()\n\nconsole.log(numberType(42)) // \"positive\"\nconsole.log(numberType(-5)) // \"negative\"\n```\n\n### Advanced Pattern Matching\n\nMatch supports exhaustive matching, nested patterns, and guards:\n\n```typescript\nimport { Match } from \"functype\"\n\n// Exhaustive matching with compile-time checking\ntype Status = \"idle\" | \"loading\" | \"success\" | \"error\"\nconst result = Match\u003cStatus, string\u003e(\"success\")\n  .case(\"idle\", \"Waiting...\")\n  .case(\"loading\", \"Loading...\")\n  .case(\"success\", \"Done!\")\n  .case(\"error\", \"Failed!\")\n  .exhaustive() // Compile error if any case is missing\n\n// Nested pattern matching\ntype User = {\n  name: string\n  age: number\n  role: \"admin\" | \"user\"\n  preferences?: { theme: \"light\" | \"dark\" }\n}\n\nconst message = Match\u003cUser, string\u003e(user)\n  .case({ role: \"admin\", age: (n) =\u003e n \u003e= 18, preferences: { theme: \"dark\" } }, \"Adult admin with dark mode\")\n  .case({ role: \"user\" }, (u) =\u003e `Regular user: ${u.name}`)\n  .when((u) =\u003e u.age \u003c 18, \"Minor user - restricted access\")\n  .default(\"Unknown user type\")\n\n// Reusable pattern matchers\nconst classifier = Match.builder\u003cAnimal, string\u003e()\n  .when((a) =\u003e a.canFly, \"Flying creature\")\n  .case({ legs: 0 }, \"Legless\")\n  .case({ legs: 2 }, \"Biped\")\n  .case({ legs: 4 }, \"Quadruped\")\n  .default(\"Other\")\n  .build()\n```\n\n## Fold\n\nFunctype includes a powerful `fold` operation for pattern matching and extracting values:\n\n```typescript\nimport { Option, Either, Try, List } from \"functype\"\n\n// Option fold\nconst opt = Option(5)\nconst optResult = opt.fold(\n  () =\u003e \"None\",\n  (value) =\u003e `Some(${value})`,\n) // \"Some(5)\"\n\n// Either fold\nconst either = Right\u003cstring, number\u003e(42)\nconst eitherResult = either.fold(\n  (left) =\u003e `Left(${left})`,\n  (right) =\u003e `Right(${right})`,\n) // \"Right(42)\"\n\n// Try fold\nconst tryValue = Try(() =\u003e 10)\nconst tryResult = tryValue.fold(\n  (error) =\u003e `Error: ${error.message}`,\n  (value) =\u003e `Success: ${value}`,\n) // \"Success: 10\"\n\n// List fold\nconst list = List([1, 2, 3])\nconst listResult = list.foldLeft(0)((acc, num) =\u003e acc + num) // 6\n```\n\n## Foldable\n\nFunctype includes a `Foldable` type class that all data structures implement:\n\n```typescript\nimport { FoldableUtils, Option, List, Try } from \"functype\"\n\n// All data structures implement the Foldable interface\nconst option = Option(5)\nconst list = List([1, 2, 3, 4, 5])\nconst tryVal = Try(() =\u003e 10)\n\n// Use fold to pattern-match on data structures\noption.fold(\n  () =\u003e console.log(\"Empty option\"),\n  (value) =\u003e console.log(`Option value: ${value}`),\n)\n\n// Use foldLeft for left-associative operations\nconst sum = list.foldLeft(0)((acc, value) =\u003e acc + value) // 15\n\n// Use foldRight for right-associative operations\nconst product = list.foldRight(1)((value, acc) =\u003e value * acc) // 120\n\n// Use FoldableUtils to work with any Foldable\nconst isEmpty = FoldableUtils.isEmpty(option) // false\nconst size = FoldableUtils.size(list) // 5\nconst convertedToList = FoldableUtils.toList(option) // List([5])\nconst convertedToEither = FoldableUtils.toEither(tryVal, \"Error\") // Right(10)\n```\n\n## Matchable\n\nFunctype includes a `Matchable` type class for enhanced pattern matching:\n\n```typescript\nimport { Option, Either, Try, List, MatchableUtils } from \"functype\"\n\n// Pattern matching on Option\nconst opt = Option(42)\nconst optResult = opt.match({\n  Some: (value) =\u003e `Found: ${value}`,\n  None: () =\u003e \"Not found\",\n}) // \"Found: 42\"\n\n// Pattern matching on Either\nconst either = Either.fromNullable(null, \"Missing value\")\nconst eitherResult = either.match({\n  Left: (error) =\u003e `Error: ${error}`,\n  Right: (value) =\u003e `Value: ${value}`,\n}) // \"Error: Missing value\"\n\n// Pattern matching on Try\nconst tryVal = Try(() =\u003e JSON.parse('{\"name\":\"John\"}'))\nconst tryResult = tryVal.match({\n  Success: (data) =\u003e `Name: ${data.name}`,\n  Failure: (error) =\u003e `Parse error: ${error.message}`,\n}) // \"Name: John\"\n\n// Pattern matching on List\nconst list = List([1, 2, 3])\nconst listResult = list.match({\n  NonEmpty: (values) =\u003e `Values: ${values.join(\", \")}`,\n  Empty: () =\u003e \"No values\",\n}) // \"Values: 1, 2, 3\"\n\n// Using MatchableUtils for advanced pattern matching\nconst isPositive = MatchableUtils.when(\n  (n: number) =\u003e n \u003e 0,\n  (n) =\u003e `Positive: ${n}`,\n)\n\nconst defaultCase = MatchableUtils.default((n: number) =\u003e `Default: ${n}`)\n\n// Using pattern guards in custom matching logic\nconst num = 42\nconst result = isPositive(num) ?? defaultCase(num) // \"Positive: 42\"\n```\n\n## Interface Hierarchy\n\nAll data structures in Functype implement a unified hierarchy of interfaces, providing consistent behavior across the library:\n\n### Type Classes\n\nFunctype leverages type classes to provide common operations:\n\n- **Functor**: Supports `map` operation for transforming wrapped values\n- **Applicative**: Extends Functor with `ap` for applying wrapped functions\n- **Monad**: Extends Applicative with `flatMap` for chaining operations\n- **AsyncMonad**: Extends Monad with `flatMapAsync` for async operations\n- **ContainerOps**: Universal operations for all containers (single-value and collections)\n- **CollectionOps**: Operations specific to collections like List and Set\n\n### Unified Interfaces\n\nAll data structures implement the `Functype` hierarchy:\n\n```typescript\n// Base interface for all data structures\ninterface FunctypeBase\u003cA, Tag\u003e\n  extends AsyncMonad\u003cA\u003e, Traversable\u003cA\u003e, Serializable\u003cA\u003e, Foldable\u003cA\u003e, Typeable\u003cTag\u003e, ContainerOps\u003cA\u003e {\n  readonly _tag: Tag\n}\n\n// For single-value containers (Option, Either, Try)\ninterface Functype\u003cA, Tag\u003e extends FunctypeBase\u003cA, Tag\u003e, Extractable\u003cA\u003e, Pipe\u003cA\u003e, Matchable\u003cA, Tag\u003e {\n  toValue(): { _tag: Tag; value: A }\n}\n\n// For collections (List, Set, Map)\ninterface FunctypeCollection\u003cA, Tag\u003e\n  extends FunctypeBase\u003cA, Tag\u003e, Iterable\u003cA\u003e, Pipe\u003cA[]\u003e, Collection\u003cA\u003e, CollectionOps\u003cA, FunctypeCollection\u003cA, Tag\u003e\u003e {\n  toValue(): { _tag: Tag; value: A[] }\n  // Collections work with Iterable instead of Monad\n  flatMap\u003cB\u003e(f: (value: A) =\u003e Iterable\u003cB\u003e): FunctypeCollection\u003cB, Tag\u003e\n}\n```\n\n### Container Operations\n\nAll containers (Option, Either, Try, List, Set) support these universal operations:\n\n```typescript\nimport { Option, List } from \"functype\"\n\nconst opt = Option(42)\nconst list = List([1, 2, 3, 4, 5])\n\n// Universal operations work on both single-value and collections\nopt.count((x) =\u003e x \u003e 40) // 1\nlist.count((x) =\u003e x \u003e 3) // 2\n\nopt.find((x) =\u003e x \u003e 40) // Some(42)\nlist.find((x) =\u003e x \u003e 3) // Some(4)\n\nopt.exists((x) =\u003e x === 42) // true\nlist.exists((x) =\u003e x === 3) // true\n\nopt.forEach(console.log) // Logs: 42\nlist.forEach(console.log) // Logs: 1, 2, 3, 4, 5\n```\n\n## Feature Matrix\n\nFor a comprehensive overview of which interfaces are supported by each data structure, see the [Functype Feature Matrix](docs/FUNCTYPE_FEATURE_MATRIX.md).\n\n## Type Safety\n\nFunctype leverages TypeScript's advanced type system to provide compile-time safety for functional patterns, ensuring that your code is both robust and maintainable.\n\n```typescript\n// Type inference works seamlessly\nconst option = Option(42)\n// Inferred as number\nconst mappedValue = option.map((x) =\u003e x.toString())\n// Inferred as string\n```\n\n## Error Formatting\n\nFunctype provides utilities for improved error visualization and logging:\n\n```typescript\nimport { formatError, createErrorSerializer } from \"functype/error\"\n\n// Create a nested task error\nconst innerTask = Task({ name: \"DbQuery\" }).Sync(() =\u003e {\n  throw new Error(\"Database connection failed\")\n})\n\nconst outerTask = Task({ name: \"UserFetch\" }).Sync(() =\u003e {\n  return innerTask.value\n})\n\n// Format the error for console display\nconsole.error(\n  formatError(outerTask.value as Error, {\n    includeTasks: true,\n    includeStackTrace: true,\n    colors: true,\n  }),\n)\n\n// Create a serializer for structured logging libraries like Pino\nconst errorSerializer = createErrorSerializer()\n\n// Use with Pino\nconst logger = pino({\n  serializers: { err: errorSerializer },\n})\n\n// Log the error with full context\nlogger.error(\n  {\n    err: outerTask.value,\n    requestId: \"req-123\",\n  },\n  \"Failed to fetch user data\",\n)\n```\n\nFor more details, see the [Error Formatting Guide](docs/error-formatting.md).\n\n## Roadmap / TODO\n\n### High Priority\n\n- [x] Complete LazyList Implementation\n  - ✓ Add Foldable interface (fold, foldLeft, foldRight)\n  - ✓ Add Pipe interface for composition\n  - ✓ Add Serializable for persistence\n  - ✓ Add Typeable support\n- [ ] Implement NonEmptyList\u003cA\u003e\n  - List guaranteed to have at least one element\n  - Prevents empty list errors at compile time\n  - Full standard interface implementation\n  - Methods like `head` return `A` instead of `Option\u003cA\u003e`\n\n### Medium Priority\n\n- [ ] Implement ValidatedNel\u003cE, A\u003e for validation with error accumulation\n  - Unlike Either, collects multiple errors\n  - Uses NonEmptyList for error collection\n  - Applicative instance combines errors\n- [x] Enhance Pattern Matching\n  - ✓ Add exhaustiveness checking at compile time\n  - ✓ Support nested pattern matching\n  - ✓ Add guard clauses (when conditions)\n  - ✓ Support destructuring patterns\n  - ✓ Consolidated into unified Match implementation\n- [x] Implement IO\u003cR,E,A\u003e effect type\n  - ✓ Lazy execution with typed errors\n  - ✓ Dependency injection (Tag, Context, Layer)\n  - ✓ Composable operations (map, flatMap, zip, race)\n  - ✓ Resource management (bracket/acquireRelease)\n  - ✓ Generator do-notation (IO.gen) and builder (IO.Do)\n\n### Low Priority\n\n- [x] Complete Tuple Implementation\n  - ✓ Add Foldable for tuple operations\n  - ✓ Add Pipe interface for composition\n  - ✓ Add Serializable for persistence\n  - ✓ Add Companion pattern with utility methods\n  - ✓ Added specialized pair() and triple() constructors\n- [ ] Implement Lens\u003cS, A\u003e for immutable updates\n  - Composable property access\n  - Type-safe nested updates\n  - Works with all functype data structures\n- [ ] Add Reader/State monads for dependency injection and state management\n\n### Completed Functionality\n\n- [x] Add lazy evaluation structures (LazyList implemented, needs interface completion)\n- [x] Add a proper Foldable type class interface\n- [x] Implement Matchable type class for pattern matching\n- [x] Implement Applicative and other functional type classes (for most types)\n\n### Performance Optimizations\n\n- [ ] Add memoization utilities\n- [ ] Improve recursive operations for large collections\n- [ ] Implement immutable data structures with structural sharing\n- [ ] Add performance benchmarks\n- [x] Optimize TreeShaking with sideEffects flag in package.json\n- [x] Support selective module imports for smaller bundles\n- [x] Add bundle size monitoring to CI/CD\n\n### API Consistency\n\n- [ ] Ensure all modules follow the Scala-inspired pattern:\n  - Constructor functions that return objects with methods\n  - Object methods for common operations\n  - Companion functions for additional utilities\n- [x] Align Task API with other monadic structures\n- [ ] Standardize import patterns (@ imports vs relative paths)\n- [x] Implement consistent error handling strategy for async operations\n\n### Testing and Documentation\n\n- [ ] Add observable test coverage metrics\n- [x] Implement property-based testing\n- [ ] Expand error handling tests\n- [ ] Add interoperability tests with other libraries\n\n### TypeScript Improvements\n\n- [x] Enable stricter TypeScript settings (noImplicitAny: true)\n- [x] Add noUncheckedIndexedAccess for safer array indexing\n- [ ] Improve support for higher-kinded types:\n  - Current type parameters work well for first-order types\n  - Expand to support type constructors as parameters (F\u003cA\u003e =\u003e F\u003cB\u003e)\n- [x] Add branded/nominal types for stronger type safety\n- [ ] Implement more type-level utilities (conditional types, template literals)\n- [ ] Leverage newer TypeScript features (const type parameters, tuple manipulation)\n\n## Claude Code Skills\n\nFunctype provides two specialized Claude Code skills to enhance your development experience:\n\n### Functype User Guide\n\nHelp for developers using functype in their projects:\n\n- Pattern conversion (imperative → functional)\n- API lookup and examples\n- Common use cases and debugging tips\n\n**Install**: Download from [dist/skills/functype-user.zip](./dist/skills/functype-user.zip)\n\n### Functype Library Developer\n\nGuide for contributors developing functype itself:\n\n- Architecture patterns and development workflow\n- Creating new data structures\n- Testing strategies and interface implementation\n\n**Install**: Download from [dist/skills/functype-developer.zip](./dist/skills/functype-developer.zip)\n\nSee [.claude/skills/README.md](./.claude/skills/README.md) for complete installation and usage instructions.\n\n## Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n## License\n\nMIT License\n\nCopyright (c) 2025 Jordan Burke\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjordanburke%2Ffunctype","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjordanburke%2Ffunctype","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjordanburke%2Ffunctype/lists"}