{"id":22328591,"url":"https://github.com/qrailibs/functir","last_synced_at":"2026-02-04T17:31:33.259Z","repository":{"id":230411051,"uuid":"779170863","full_name":"qrailibs/functir","owner":"qrailibs","description":"✨ Functional programming library for JavaScript. Fully type-safe.","archived":false,"fork":false,"pushed_at":"2025-08-27T06:54:11.000Z","size":126,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-09-29T01:39:18.309Z","etag":null,"topics":["functional","functional-programming","functor","immutable","io","monad","trait"],"latest_commit_sha":null,"homepage":"","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/qrailibs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2024-03-29T07:44:55.000Z","updated_at":"2025-08-27T06:54:15.000Z","dependencies_parsed_at":null,"dependency_job_id":"8ff45832-433c-4cee-88be-6799dfe6fc5f","html_url":"https://github.com/qrailibs/functir","commit_stats":null,"previous_names":["qrailibs/functir"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/qrailibs/functir","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qrailibs%2Ffunctir","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qrailibs%2Ffunctir/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qrailibs%2Ffunctir/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qrailibs%2Ffunctir/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/qrailibs","download_url":"https://codeload.github.com/qrailibs/functir/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/qrailibs%2Ffunctir/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29091820,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-04T03:31:03.593Z","status":"ssl_error","status_checked_at":"2026-02-04T03:29:50.742Z","response_time":62,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: 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","functional-programming","functor","immutable","io","monad","trait"],"created_at":"2024-12-04T03:13:04.418Z","updated_at":"2026-02-04T17:31:33.246Z","avatar_url":"https://github.com/qrailibs.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# About\n\n_Functir_ is a functional programming library for JavaScript/TypeScript. True functional programming in JS! Or probably \"mix of things from Scala that now works in JS!\".\n\nAlso _Functir_ is actually production-ready library with zero dependencies, native implementations, full support of TypeScript and of course with full immutability.\n\n### Installation\n\n- [Installation](#installation)\n\n### Data types\n\n- [`Box`](#box)\n- [`Option` (`None`, `Some`)](#option)\n- [`Either` (`Left`, `Right`)](#either)\n- [`LikeBox`](#likebox)\n- [`Seq`](#seq)\n- [`Trait`](#trait)\n- [`IO`, `AsyncIO`](#io)\n- [`Throwable`](#throwable)\n- [`Action`](#action)\n\n### Features\n\n- [Pattern matching](#pattern-matching)\n- [Piping](#piping)\n\n# Installation\n\nvia pnpm:\n\n```bash\npnpm add functir\n```\n\nvia npm:\n\n```bash\nnpm i functir\n```\n\n# Core data types\n\n## `Box`\n\nBox is super simple – it's class that containing some immutable value.\nYou cannot change box wrapped value directly, but you can transform it and get new Box instance.\n\n```ts\nimport { Box } from 'functir'\n\n// 1. Let's create wrapped number value\nconst wrapped = new Box(1)\nconsole.log(wrapped.value) // 1\n\n// 2. Let's change it (immutable!)\nconst wrappedTransformed = wrapped.mutate(_ =\u003e _ + 100)\nconsole.log(wrappedTransformed.value) // 101\n```\n\nOther types described in future also will also use `Box` to contain some value via class construction.\n\n## `Option`\nMostly `Option` data type is used in pattern matching (described in future). `Option` is primary designed to be one of two values: `None` or `Some(value)`. `Some` value is works same as `Box` – wraps some value.\n\n```ts\nimport { Option, None, Some } from 'functir'\n\n// Both of values is Option\u003cnumber\u003e\nconst valueOne: Option\u003cnumber\u003e = None()\nconst valueTwo: Option\u003cnumber\u003e = Some(200)\n\n// You can mutate value of `Some` by converting to `Box`\nconsole.log(valueTwo.asBox) // Box(200)\nconsole.log(valueTwo.asBox.mutate(_ =\u003e _ + 5)) // Box(205)\n```\n\n## `Either`\n\nThe `Either\u003cTLeft, TRight\u003e` is very similar to `Option`, but the value can be `Left(value: TLeft)` or `Right(value: TRight)`:\n\n```ts\nimport { Either, Left, Right } from 'functir'\n\n// Either\u003cnumber, string\u003e = Left\u003cnumber\u003e | Right\u003cstring\u003e\n// Both of values is Either\u003cnumber, string\u003e\nconst valueOne: Either\u003cnumber, string\u003e = Left(100)\nconst valueTwo: Either\u003cnumber, string\u003e = Right('200')\n\n// You can mutate value by converting to `Box`\nconsole.log(valueOne.asBox) // Box(100)\nconsole.log(valueOne.asBox.mutate(_ =\u003e _ + 5)) // Box(105)\n```\n\n## `LikeBox`\nWhat does `Box`, `Option`, `Either` have in common? - they are all implements `LikeBox` interface, but in different variations.\nWe have 3 main variations of `LikeBox` interface:\n- `LikeBox` (`None` type uses it, it doesn't have wrapped value inside)\n- `LikeFilledBox` (`Box` type uses it, inherits `LikeBox` but have wrapped value inside)\n- `LikeConvertibleFilledBox` (`Option` \u0026 `Either` types uses it, inherits `LikeFilledBox` but have `asBox` converter inside)\n\nHow you can use those interfaces? Like that:\n\n```ts\nimport { Box } from 'functir'\n\nclass SomeValue extends Box.filled\u003cnumber\u003e {}\n\nconst wrapped = new SomeValue(200)\nconsole.log(wrapped.value) // 200\nconsole.log(wrapped.asBox) // Box(200)\n```\n\nIllustrated code produces for you a `LikeConvertibleFilledBox` class with wrapped `number`, that you can use. The same thing does `None`, `Some`, `Left`, `Right` implementation.\n\nNow let's see what functions does `LikeBox` provides:\n\n### Usage: `match`/`pipe`\nShorthand for using pattern matching or piping:\n\n```ts\nimport { Option, None, Some, match, is } from 'functir'\n\nconst boxNone: Option\u003cnumber\u003e = None()\nconst boxSome: Option\u003cnumber\u003e = Some(200)\n\n// `.match`/`.pipe` – this functions is works on\n// `Box`, `Option`, `Either`, `Box.filled\u003cT\u003e`\n// becase they are `LikeBox` implementations\n\n// Pattern matching\nboxNone.match([\n\tis(None, _ =\u003e \"none\"),\n\tis(Some, _ =\u003e \"some\")\n]) // none\nboxSome.match([\n\tis(None, _ =\u003e \"none\"),\n\tis(Some, _ =\u003e \"some\")\n]) // some\n\n// Piping\nboxSome.pipe([\n\t_ =\u003e _ + 100, // 200 + 100 = 300\n\t_ =\u003e _ + 50 // 300 + 50 = 350\n]) // 350\n```\n\n### Usage: `flatten`\nThis magic function is just flattens wrapped `LikeBox` values:\n\n```ts\nimport { Box, Some } from 'functir'\n\n// `Box` inside `Box` nested \nconst deep = new Box(new Box(5))\nconsole.log(deep.flatten()) // 5\n\n// Different `LikeBox` implementations nested\nconst complexDeep = new Box(new Some(new Box(new Some(20))))\nconsole.log(complexDeep.flatten()) // 20\n```\n\n## `Seq`\n`Seq` (Sequence) is helpful data type that you can use as alternative for arrays. Why `Seq` instead of arrays?\n1. `Seq` is fully immutable-safe (doesn't provides any mutable methods)\n2. `Seq` provides a lot of methods that arrays doesn't\n\nUsage is simple:\n```ts\nimport { Seq } from 'functir'\n\nconst seq = new Seq\u003cnumber\u003e(1, 2, 3)\n\nconsole.log(seq.asArray) // [1, 2, 3]\n```\n\n## Usage: methods\n`Seq` provides many different immutable methods you can use:\n\n```ts\n// Just copies current sequence\nseq.copy() // Seq(1, 2, 3)\n```\n\n```ts\n// Converting into array, set, map\nseq.asArray // [1, 2, 3]\nseq.asSet // Set(1, 2, 3)\nseq.asMap // Map({ 0: 1, 1: 2, 2: 3 })\n```\n\n```ts\n// Adds value to start of seq\nseq.prepended(10) // Seq(10, 1, 2, 3)\n\n// Adds value to end of seq\nseq.appended(10) // Seq(1, 2, 3, 10)\n\n// Updates value at index\nseq.updated(0, 10) // Seq(10, 2, 3)\n```\n\n```ts\n// Auto sorting, like [].sort()\nseq.autoSorted() // Seq(1, 2, 3)\n\n// Sort using predicate\nseq.sorted((a, b) =\u003e (a \u003e b ? -1 : 1)) // Seq(3, 2, 1)\n```\n\n```ts\n// Reverses values\nseq.reversed() // Seq(3, 2, 1)\n```\n\n```ts\n// Maps values\nseq.mapped(_ =\u003e _ + 10) // Seq(11, 12, 13)\n```\n\n```ts\n// Accumulated values (For example, sum of values)\nseq.accumulated(([acc, _]) =\u003e acc + _, 0) // 6\n```\n\n```ts\n// Filters values\nseq.filtered(_ =\u003e _ \u003e 1) // Seq(2, 3)\n\n// Excludes values\nseq.exluded(2) // Seq(1, 3)\n```\n\n```ts\n// Pads from start with value (to length of 6)\nseq.padStart(6, -1) // Seq(-1, -1, -1, 1, 2, 3)\n\n// Pads from end with value (to length of 6)\nseq.padEnd(6, -1) // Seq(1, 2, 3, -1, -1, -1)\n```\n\n```ts\n// Patches some amount of values from index with value\n// From index 1, Amount of 2, With value -1\nseq.patched(1, 2, -1) // Seq(1, -1, -1)\n```\n\n```ts\n// Get index of item (from start)\nnew Seq(1, 1, 1).indexOf(1) // 0\n\n// Get index of item (from end)\nnew Seq(1, 1, 1).lastIndexOf(1) // 2\n```\n\n## `Trait`\nTrait is the concept from `Scala`/`Rust` languages. Using `Trait` you can easily create class (*DTO/DAO*) for some data model:\n\n```ts\nimport { Trait } from 'functir'\n\n// Create a class using trait\n// Trait will automatically create immutable fields, constructor for the class\nconst UserDTO = Trait\u003c{\n\treadonly nickname: string\n\treadonly age: number\n}\u003e();\n\n// Create instance of trait\n// All fields of trait should be passed to constructor\nconst jake = new UserDTO({\n\tnickname: 'Jake',\n\tage: 20\n})\n\n// You can convert trait instances into objects or `Box`\nconsole.log(jake.asObject) // { nickname: 'Jake', age: 20 }\nconsole.log(jake.asBox) // Box({ nickname: 'Jake', age: 20 )\n```\n\n`Trait` of course produces immutable classes (with `copy` method), you can use that for creating services:\n```ts\nimport { Trait, Seq } from 'functir'\n\n/**\n * Immutable service that builds pizza\n */\nclass PizzaService extends Trait\u003c{\n    readonly size: 'sm' | 'md' | 'lg';\n    readonly toppings: Seq\u003c'meat' | 'pineapple' | 'cheese'\u003e;\n}\u003e() {\n\tconstructor() {\n\t\tsuper({\n\t\t\tsize: 'sm',\n\t\t\ttoppings: new Seq()\n\t\t})\n\t}\n\n    public setSize = (size: 'sm' | 'md' | 'lg') =\u003e\n\t\tthis.copy({ size })\n\t\n    public addTopping = (topping: 'meat' | 'pineapple' | 'cheese') =\u003e\n        this.copy({ toppings: this.toppings.appended(topping) })\n}\n\n// Immutability! Let's build pizza\nconst myPizza = new PizzaService()\n\t.setSize('md')\n\t.addTopping('meat')\n\t.addTopping('cheese')\n\t.asObject\n\nconsole.log(myPizza) // { size: 'md', toppings: ['meat', 'cheese'] }\n```\n\n## `IO`\n`IO` is also another helpful type for defining input and output of function:\n\n```ts\nimport { IO } from 'functir'\n\n// Operation that converts string to number\nconst toNumber: IO\u003cstring, number\u003e = \n\t_ =\u003e parseInt(_)\n\nconsole.log(toNumber('123')) // 123\n```\n\nAlso if function is async you can use `AsyncIO`:\n\n```ts\nimport { AsyncIO } from 'functir'\n\n// Operation that async (just return input as output)\nconst someFunction: AsyncIO\u003cstring, string\u003e =\n\tasync _ =\u003e _\n```\n\nThere's third parameter in IO that we didn't mentioned, it's used to define what `Throwable` (described below) can be given by function:\n\n```ts\nimport { IO, ThrowableTrait } from 'functir'\n\n// Our own throwable error\nclass TooLongError extends ThrowableTrait('MyOwnError', 'Value was too long') {}\n\n// Our operation described:\n// input = string, output = string, throws = MyOwnError\n// if input length \u003c 5 we return it, otherwise TooLongError given\nconst someFunction: IO\u003cstring, string, TooLongError\u003e =\n\t_ =\u003e _.length \u003c 5 ? _ : new TooLongError\n```\n\n## `Throwable`\nThe `Throwable` is the helpful type used with `IO` to annotate what error can happen in a function:\n\n```ts\nimport { Throwable, ThrowableTrait } from 'functir'\n\n// Simple error\nconst someError: Throwable = new Error('Some custom throwed error')\n```\n\nAlso you can create own throwable error class using `ThrowableTrait`:\n\n```ts\nimport { Throwable, ThrowableTrait } from 'functir'\n\n// Create own throwable error\nclass CustomError extends ThrowableTrait('CustomError') {}\n\n// Our custom error\nconst someError2: Throwable = new CustomError('Some super custom error')\n```\n\n## `Safecall`\n`Safecall` is a new safe way to run a functions that may throw a error:\n\n```ts\nimport { Safecall, Success, Failure, match, is } from 'functir'\n\n// Function that throws a error if value \u003c 100\nconst fn = (v: number) =\u003e {\n\tif (v \u003c 100) throw new Error('Something happend...')\n\telse return v\n}\n\nmatch(Safecall(() =\u003e fn(1)))([\n\tis(Success, _ =\u003e `success (${_} \u003e 100)`),\n\tis(Failure, _ =\u003e `failed (${_} \u003c 100)`),\n]) // failed (1 \u003c 100)\n\nmatch(Safecall(() =\u003e fn(200)))([\n\tis(Success, _ =\u003e `success (${_} \u003e 100)`),\n\tis(Failure, _ =\u003e `failed (${_} \u003c 100)`),\n]) // success (200 \u003e 100)\n```\n\n\n# Core features\n\n## Pattern matching\nPattern matching if well-known pattern that allows to match value to a different cases like in a `switch-case`, but more clean syntax and ability to check for instances of classes.\n\nExample:\n```ts\nimport { match, is, _, Some, None } from 'functir'\n\n// 1 if Some\n// 0 if None\n// -1 otherwise\nconst result = match(Some(100))([\n\tis(Some, _ =\u003e 1),\n\tis(None, _ =\u003e 0),\n\tis(_, _ =\u003e -1)\n])\n\nconsole.log(result) // 1\n```\n\nThe `_` is special symbol used to handle case none of cases is matched.\n\nThe case matching is perfectly works for:\n– `Either`\n– `Option`\n– `Trait`\n– Classes\n– Primitives\n– RegExps\n\n## Piping\nWill be described later.","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqrailibs%2Ffunctir","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fqrailibs%2Ffunctir","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fqrailibs%2Ffunctir/lists"}