{"id":15866249,"url":"https://github.com/kanitsharma/io","last_synced_at":"2025-04-01T20:48:49.347Z","repository":{"id":57124776,"uuid":"172474292","full_name":"kanitsharma/io","owner":"kanitsharma","description":"Lightweight monadic abstraction to \"purely\" handle side effects in javascript.","archived":false,"fork":false,"pushed_at":"2019-03-13T11:08:22.000Z","size":327,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-03-18T03:58:15.470Z","etag":null,"topics":["asynchronous-programming","functional-programming","javascript","side-effects"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/kanitsharma.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"license.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-02-25T09:23:41.000Z","updated_at":"2023-04-11T15:25:20.000Z","dependencies_parsed_at":"2022-08-31T17:01:22.961Z","dependency_job_id":null,"html_url":"https://github.com/kanitsharma/io","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kanitsharma%2Fio","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kanitsharma%2Fio/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kanitsharma%2Fio/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kanitsharma%2Fio/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kanitsharma","download_url":"https://codeload.github.com/kanitsharma/io/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246709914,"owners_count":20821298,"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","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":["asynchronous-programming","functional-programming","javascript","side-effects"],"created_at":"2024-10-05T23:05:18.807Z","updated_at":"2025-04-01T20:48:49.311Z","avatar_url":"https://github.com/kanitsharma.png","language":"JavaScript","readme":"\u003ch1 align=\"center\"\u003e\n  \u003cbr\u003e\n  \u003ca href=\"https://github.com/kanitsharma/io\"\u003e\u003cimg src=\"logo.png\" alt=\"io\" width=\"200\"\u003e\u003c/a\u003e\n\u003c/h1\u003e\n\n\u003ch4 align=\"center\"\u003eLightweight monadic abstraction to \"purely\" handle side effects in javascript.\u003c/h4\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/prettier/prettier\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/code_style-prettier-ff69b4.svg\" alt=\"prettier\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://github.com/rajatsharma/hellpack\"\u003e\n        \u003cimg src=\"https://img.shields.io/badge/uses-hellpack%20%F0%9F%94%A5-%23414770.svg\" alt=\"hellpack\"/\u003e\n  \u003c/a\u003e\n    \u003ca href=\"https://github.com/kanitsharma/io/blob/license.md\"\u003e\n        \u003cimg src=\"https://badgen.net/badge/license/MIT/blue\" alt=\"license\"/\u003e\n  \u003c/a\u003e\n  \u003ca href=\"https://travis-ci.com/kanitsharma/io\"\u003e\n        \u003cimg src=\"https://travis-ci.com/kanitsharma/io.svg?token=sGsp6ken9AnVBDihTPmf\u0026branch=master\" alt=\"travis\"/\u003e\n  \u003c/a\u003e\n\n### An IO provides\n- Lazy evaluation of side effects.\n- Easy cancellation at any point during its computation and resource cleanup after that.\n- Clean API for easy resource management while doing side effects.\n- Ultra lightweight, Gzipped ~ 1kb.\n- Friendly Error Messages.\n- Follows Haskell laws for Functors, Applicatives and Monads [(See the tests for these laws)](https://github.com/kanitsharma/io/tree/master/__tests__).\n\n## Getting Started\n\n```\n  yarn add @kimera/io\n```\nor\n```\n  npm install --save @kimera/io\n```\n\n## Basic Examples\n\n```javascript\nimport IO from '@kimera/io'\n\nIO.of(1)\n  .map(x =\u003e x + 10)\n  .chain(x =\u003e IO.of(x + '!')) // JavaScript is awesome\n  .fork(\n    err =\u003e console.log(err),\n    x =\u003e console.log(x) // 11!\n  )\n\n// Fetching data from API\nconst pureFetch = IO.encaseP(fetch);\n\npureFetch('https://jsonplaceholder.typicode.com/todos/1')\n  .chain(IO.encaseP(response =\u003e response.json()))\n  .fork(\n    console.error,\n    console.log\n  });\n```\n\n## API\n\n\u003cimg align=\"right\" width=\"100\"  src=\"https://camo.githubusercontent.com/b76c9e0a143a4dab12bbf5e9796d8eaa1b4d757a/68747470733a2f2f7261772e6769746875622e636f6d2f66616e746173796c616e642f66616e746173792d6c616e642f6d61737465722f6c6f676f2e706e67\" alt=\"travis\"/\u003e\n\nIO implements Fantasy Land and Static Land -compatible Functor, Bifunctor, Applicative and  Monad (of, ap, map, bimap, chain).\nAll versions of Fantasy Land are supported.\n\n## Creating IOs\n\n#### IO\nCreates an IO with the given computation. A computation is a function which takes two callbacks. Both are continuations for the computation. The first is reject, commonly abbreviated to rej; The second is resolve, or res. When the computation is finished (possibly asynchronously) it may call the appropriate continuation with a failure or success value.\n\nAdditionally, the computation may return a function containing resource management logic.\n\n```javascript\nIO((reject, resolve) =\u003e {\n  setTimeout(resolve, 3000, 'Hello world');\n});\n```\n\n#### of\nCreates an IO which immediately resolves with the given value.\n\n```javascript\nIO.of('Hello')\n  .map(x =\u003e `${x} World!`)\n  .fork(console.err, console.log)\n```\n\n####  rejected\nCreates an IO which immediately rejects with the given value.\n\n```javascript\nIO.rejected('Hello')\n  .map(x =\u003e `${x} World!`)\n  .fork(console.err, console.log) // Hello\n```\n\n#### encaseP\nAllows Promise-returning functions to be turned into IO-returning functions.\n\nTakes a function which returns a Promise, and a value, and returns an IO. When forked, the IO calls the function with the value to produce the Promise, and resolves with its resolution value, or rejects with its rejection reason.\n\n```javascript\nconst pureFetch = IO.encaseP(fetch);\n\npureFetch('https://jsonplaceholder.typicode.com/todos/1')\n  .chain(IO.encaseP(response =\u003e response.json()))\n  .fork(\n    console.error,\n    console.log\n  });\n```\n\n### Transforming IO\n\n#### map\nTransforms the resolution value inside the OP, and returns an IO with the new value. The transformation is only applied to the resolution branch: if the IO is rejected, the transformation is ignored.\n\n```javascript\n  IO.of(1)\n    .map(x =\u003e x + 1)\n    .fork(console.error, console.log);\n```\n\n#### bimap\nMaps the left function over the rejection value, or the right function over the resolution value, depending on which is present.\n\n```javascript\nIO.of(1)\n  .bimap(x =\u003e x + '!', x =\u003e x + 1)\n  .fork(console.error, console.log);\n  //\u003e 2\n\nIO.reject('error')\n  .bimap(x =\u003e x + '!', x =\u003e x + 1)\n  .fork(console.error, console.log);\n  //! \"error!\"\n```\n\n#### chain\nSequence a new IO using the resolution value from another. Similarly to map, chain expects a function to transform the resolution value of an IO. But instead of returning the new value, chain expects an IO to be returned.\n\nThe transformation is only applied to the resolution branch: if the IO is rejected, the transformation is ignored.\n\n```javascript\nIO.of(1)\n  .chain(x =\u003e IO.of(x + 1))\n  .fork(console.error, console.log);\n  //\u003e 2\n```\n\n#### ap\nApplies the function contained in the left-hand IO or Apply to the value contained in the right-hand IO or Apply. If one of the IO rejects the resulting IO will also be rejected.\n\n```javascript\nIO.of(x =\u003e y =\u003e x + y)\n  .ap(IO.of(1))\n  .ap(IO.of(2))\n  .fork(console.error, console.log);\n//\u003e 3\n```\n\n#### fold\nApplies the left function to the rejection value, or the right function to the resolution value, depending on which is present, and resolves with the result.\nCan be used with other type constructors like Left | Right from Either.\n\n```javascript\nIO.of('hello')\n  .fold(Left, Right)\n  .fork(() =\u003e {}, console.log);\n  //\u003e Right('hello')\n\nIO.reject('it broke')\n  .fold(Left, Right)\n  .fork(() =\u003e {}, console.log);\n  //\u003e Left('it broke')\n```\n\n### Running IOs in parallel using Applicatives (Parallelism)\nIf an IO contains a function with order greater than 1, then the IOs applied to it will run parallely.\n\n```javascript\n  const f = x =\u003e y =\u003e x + y;\n  let firstLoaded = false;\n  let secondLoaded = false;\n\n  const M1 = IO((_, resolve) =\u003e {\n    setTimeout(() =\u003e {\n      firstLoaded = true;\n      resolve(10);\n    }, 1000);\n  });\n\n  const M2 = IO((_, resolve) =\u003e {\n    setTimeout(() =\u003e {\n      secondLoaded = true;\n      resolve(10);\n    }, 1000);\n  });\n\n  IO.of(f)\n    .ap(M1)\n    .ap(M2)\n    .fork(() =\u003e {}, () =\u003e {});\n\n  setTimeout(() =\u003e {\n    console.log(firstLoaded, secondLoaded) // true, true\n  }, 1100);\n```\n\n### Running IOs\n\n#### fork\nExecute the computation represented by an IO, passing reject and resolve callbacks to continue once there is a result.\n\nThis function is called fork because it literally represents a fork in our program: a point where a single code-path splits in two. It is recommended to keep the number of calls to fork at a minimum for this reason. The more forks, the higher the code complexity.\n\nGenerally, one only needs to call fork in a single place in the entire program.\n\n```javascript\nIO.of('world').fork(\n  err =\u003e console.log(`Oh no! ${err.message}`),\n  thing =\u003e console.log(`Hello ${thing}!`)\n);\n```\n\n### Cancelling IOs\n\nOnce forked, an IO can be cancelled at any point during its computation.\nNote that if cancelled, handler functions passed into fork will not run, instead the clean up functions returned from the side effecty functions will run.\n\n```javascript\nconst run = () =\u003e IO((_, resolve) =\u003e {\n  let timeout setTimeout(() =\u003e {\n    resolve('Finished');\n  }, 1000);\n\n  return () =\u003e clearTimeout(timeout);\n});\n\nconst cancelAndCleanup = run()\n  .map(x =\u003e {\n    cancelAndCleanup();\n    return x;\n  })\n  .chain(() =\u003e\n    IO((_, resolve) =\u003e {\n      setTimeout(() =\u003e {\n        resolve('Failed');\n      }, 1000);\n    }),\n  )\n  .fork(console.log, console.log);\n```\n\n### Cleaning up after running sideEffects\n\nAdditionally functions inside IO can return a cleanup or resource management.\nIf your computation chain is composed together with multiple IOs returning these cleanup functions, then if cancellation/cleanup function is called, all the cleanup functions will run according to their respective order,\n\n```javascript\nconst computation = IO((reject, resolve) =\u003e {\n    resolve('Running');\n\n    return () =\u003e console.log('First Cleanup');\n  })\n  .chain(x =\u003e IO((reject, resolve) =\u003e {\n    resolve(x + ' Second Running');\n\n    return () =\u003e console.log('Second Cleanup')\n  }))\n\nconst cleanup = computaion.fork(console.err, console.log)\n\ncleanup()\n// First Cleanup\n// Second Cleanup\n```\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkanitsharma%2Fio","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkanitsharma%2Fio","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkanitsharma%2Fio/lists"}