{"id":18627350,"url":"https://github.com/pete-murphy/fp-ts-foldl","last_synced_at":"2025-10-16T08:22:46.202Z","repository":{"id":52033897,"uuid":"436481668","full_name":"pete-murphy/fp-ts-foldl","owner":"pete-murphy","description":"(WIP) `fp-ts` port of https://hackage.haskell.org/package/foldl","archived":false,"fork":false,"pushed_at":"2022-10-16T19:15:28.000Z","size":338,"stargazers_count":7,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-30T02:46:32.977Z","etag":null,"topics":["foldable","fp-ts","transducers"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/pete-murphy.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-12-09T04:26:21.000Z","updated_at":"2023-04-12T20:25:46.000Z","dependencies_parsed_at":"2023-01-20T03:03:42.501Z","dependency_job_id":null,"html_url":"https://github.com/pete-murphy/fp-ts-foldl","commit_stats":null,"previous_names":["pete-murphy/fp-ts-foldl","ptrfrncsmrph/fp-ts-foldl"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/pete-murphy/fp-ts-foldl","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pete-murphy%2Ffp-ts-foldl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pete-murphy%2Ffp-ts-foldl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pete-murphy%2Ffp-ts-foldl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pete-murphy%2Ffp-ts-foldl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/pete-murphy","download_url":"https://codeload.github.com/pete-murphy/fp-ts-foldl/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/pete-murphy%2Ffp-ts-foldl/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279169466,"owners_count":26118428,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-16T02:00:06.019Z","response_time":53,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["foldable","fp-ts","transducers"],"created_at":"2024-11-07T04:42:00.208Z","updated_at":"2025-10-16T08:22:46.172Z","avatar_url":"https://github.com/pete-murphy.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# fp-ts-foldl\n\nThis is a WIP port of the [`foldl`](https://hackage.haskell.org/package/foldl) library in Haskell. Most of the documentation here has been adapted from the Hackage docs.\n\nThis library provides efficient left folds that you can combine using `Applicative` style.\n\n- [💾 Install](#install)\n- [📖 Tutorial](#tutorial)\n- [🚀 Benchmarks](#benchmarks)\n\n## Install\n\nUses `fp-ts` as a peer dependency.\n\n```bash\nyarn add fp-ts fp-ts-foldl\n```\n\nor\n\n```bash\nnpm install fp-ts fp-ts-foldl\n```\n\n## Tutorial\n\nThis tutorial assumes the following imports:\n\n```ts\nimport * as L from \"fp-ts-foldl\";\nimport { pipe } from \"fp-ts/function\";\nimport * as N from \"fp-ts/number\";\nimport * as RA from \"fp-ts/ReadonlyArray\";\nimport * as RNEA from \"fp-ts/ReadonlyNonEmptyArray\";\nimport * as RR from \"fp-ts/ReadonlyRecord\";\n```\n\nA `Fold` is a representation of a left fold that preserves the fold's step function, initial accumulator, and extraction function. This allows the `Applicative` instance to assemble derived folds that traverse the container only once.\n\nA `Fold\u003cA, B\u003e` processes elements of type `A` and results in a value of type `B`.\n\nWe can use the `fold` function to _apply_ a `Fold` to a `ReadonlyArray`:\n\n```ts\nL.fold(RA.Foldable)(L.sum)([1, 2, 3]); //-\u003e 6\n```\n\n`fold` works with any type that implements `fp-ts`'s [`Foldable` type class](https://github.com/gcanti/fp-ts/blob/ed2b205db201e79f91a1125273508eb27d4b879d/src/Foldable.ts#L14-L23) (or any type that has a `reduce` function matching the method from that class), but we can use `foldArray` for the common use case where the `Foldable` instance is for `ReadonlyArray`:\n\n```ts\nL.foldArray(L.sum)([1, 2, 3]);\n```\n\n`Fold`s are `Applicative`s, so you can combine them using `Applicative` combinators:\n\n```ts\n// `average` has the inferred type `Fold\u003cnumber, number\u003e`\nconst average = pipe(\n  L.Do,\n  L.apS(\"sum\", L.sum),\n  L.apSW(\"length\", L.length),\n  L.map(({ sum, length }) =\u003e sum / length)\n);\n\n// Taking the sum, the sum of squares, ..., up to the sum of `x ** 5`\nconst powerSums = pipe(\n  [1, 2, 3, 4, 5],\n  RA.traverse(L.Applicative)(n =\u003e\n    pipe(\n      L.sum,\n      L.premap((x: number) =\u003e x ** n)\n    )\n  )\n);\nL.foldArray(powerSums)(RNEA.range(1, 10));\n//-\u003e [ 55, 385, 3025, 25333, 220825 ]\n```\n\nThese combined folds will still traverse the array only once:\n\n```ts\nL.foldArray(average)(RNEA.range(1, 10_000_000));\n//-\u003e 5000000.5\n\npipe(\n  RNEA.range(1, 10_000_000),\n  L.foldArray(\n    L.Do,\n    L.apS(\"minimum\", L.minimum(N.Ord)),\n    L.apS(\"maximum\", L.maximum(N.Ord))\n  )\n);\n//-\u003e { minimum: O.some(1), maximum: O.some(10_000_000) }\n```\n\nNow that we have the basics, let's look at [a dataset](https://archive.ics.uci.edu/ml/datasets/iris) of `Flower` measurements.\n\n```ts\ntype Flower = {\n  sepalLength: number;\n  sepalWidth: number;\n  petalLength: number;\n  petalWidth: number;\n  species: \"setosa\" | \"versicolor\" | \"virginica\";\n};\n\nconst flowers = [\n  {\n    sepalLength: 5.1,\n    sepalWidth: 3.5,\n    petalLength: 1.4,\n    petalWidth: 0.2,\n    species: \"setosa\",\n  },\n  {\n    sepalLength: 4.9,\n    sepalWidth: 3,\n    petalLength: 1.4,\n    petalWidth: 0.2,\n    species: \"setosa\",\n  },\n  {\n    sepalLength: 4.7,\n    sepalWidth: 3.2,\n    petalLength: 1.3,\n    petalWidth: 0.2,\n    species: \"setosa\",\n  },\n  // ...\n];\n```\n\nWe can get the mean petal-length of all flowers:\n\n```ts\npipe(\n  flowers,\n  L.foldArray(\n    L.mean,\n    L.premap((flower: Flower) =\u003e flower.petalLength),\n    L.map(n =\u003e n.toPrecision(3)) // `map` transforms the final result of the `Fold`\n  )\n);\n//-\u003e \"3.76\"\n```\n\nWe can also use `prefilter` to just look at the petal-lengths of the _virginica_ species:\n\n```ts\npipe(\n  flowers,\n  L.foldArray(\n    L.mean,\n    L.premap((flower: Flower) =\u003e flower.petalLength),\n    L.prefilter(flower =\u003e flower.species === \"virginica\"),\n    L.map(n =\u003e n.toPrecision(3))\n  )\n);\n//-\u003e \"5.55\"\n```\n\nFinally we can use `Applicative` combinators to get the standard deviation of all flower attributes, while only traversing the array once:\n\n```ts\npipe(\n  flowers,\n  L.foldArray(\n    L.Do,\n    L.apS(\n      \"petalLength\",\n      pipe(\n        L.std,\n        L.premap((flower: Flower) =\u003e flower.petalLength)\n      )\n    ),\n    L.apS(\n      \"petalWidth\",\n      pipe(\n        L.std,\n        L.premap(flower =\u003e flower.petalWidth)\n      )\n    ),\n    L.apS(\n      \"sepalLength\",\n      pipe(\n        L.std,\n        L.premap(flower =\u003e flower.sepalLength)\n      )\n    ),\n    L.apS(\n      \"sepalWidth\",\n      pipe(\n        L.std,\n        L.premap(flower =\u003e flower.sepalWidth)\n      )\n    ),\n    L.map(RR.map(n =\u003e n.toPrecision(3)))\n  )\n);\n//-\u003e { petalLength: '1.76', petalWidth: '0.761', sepalLength: '0.825', sepalWidth: '0.432' }\n```\n\n## Benchmarks\n\n`foldl` performs favorably against transducer implementations in [`ramda`](https://github.com/ramda/ramda) and [`transducers-js`](https://github.com/cognitect-labs/transducers-js) in the [following benchmarks](./benchmark). (Note that this library does not support early termination, unlike most transducer implementations, so will perform much worse in cases where that's a requirement. See: https://github.com/Gabriella439/foldl/issues/85.)\n\n`map`, `filter`, `sum` on an `Array` of 1,000,000 numbers\n![array4 (2)](https://user-images.githubusercontent.com/26548438/182533139-3ab2d482-6d71-4f57-9775-08d57228837f.png)\n\n`map`, `filter`, `sum` on an [`immutable/List`](https://github.com/immutable-js/immutable-js/) of 1,000,000 numbers\n![imm4 (1)](https://user-images.githubusercontent.com/26548438/182533186-6ee6dec8-90a9-4599-b67c-d7ae598e4656.png)\n\n`map`, `filter`, `sum` on a [`funkia/List`](https://github.com/funkia/list) of 1,000,000 numbers\n![funkia4 (1)](https://user-images.githubusercontent.com/26548438/182533188-6672e3c9-da7f-4539-81ab-183c286615dd.png)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpete-murphy%2Ffp-ts-foldl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpete-murphy%2Ffp-ts-foldl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpete-murphy%2Ffp-ts-foldl/lists"}