{"id":16806024,"url":"https://github.com/haltcase/tryad","last_synced_at":"2025-03-17T07:49:31.342Z","repository":{"id":57380433,"uuid":"114615208","full_name":"haltcase/tryad","owner":"haltcase","description":"Monadic mashup of Maybe \u0026 Either that represents a value, nothing, or an error. Promise friendly.","archived":false,"fork":false,"pushed_at":"2017-12-18T08:36:21.000Z","size":15,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-05-02T04:34:56.683Z","etag":null,"topics":["either","functional","maybe","monad","option","result"],"latest_commit_sha":null,"homepage":null,"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/haltcase.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}},"created_at":"2017-12-18T08:27:58.000Z","updated_at":"2018-09-06T05:05:55.000Z","dependencies_parsed_at":"2022-09-05T13:51:33.930Z","dependency_job_id":null,"html_url":"https://github.com/haltcase/tryad","commit_stats":null,"previous_names":["citycide/tryad"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/haltcase%2Ftryad","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/haltcase%2Ftryad/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/haltcase%2Ftryad/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/haltcase%2Ftryad/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/haltcase","download_url":"https://codeload.github.com/haltcase/tryad/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243997042,"owners_count":20380980,"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":["either","functional","maybe","monad","option","result"],"created_at":"2024-10-13T09:49:54.696Z","updated_at":"2025-03-17T07:49:31.312Z","avatar_url":"https://github.com/haltcase.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tryad \u0026middot; [![Version](https://img.shields.io/npm/v/tryad.svg?style=flat-square\u0026maxAge=3600)](https://www.npmjs.com/package/tryad) [![License](https://img.shields.io/npm/l/tryad.svg?style=flat-square\u0026maxAge=3600)](https://www.npmjs.com/package/tryad) [![Travis CI](https://img.shields.io/travis/citycide/tryad.svg?style=flat-square\u0026maxAge=3600)](https://travis-ci.org/citycide/tryad) [![JavaScript Standard Style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat-square\u0026maxAge=3600)](https://standardjs.com)\n\n\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://raw.githubusercontent.com/citycide/tryad/master/.media/logo.png\" width=\"500\" alt=\"tryad\"\u003e\n\u003c/p\u003e\n\n\u003e Monadic mashup of Maybe \u0026 Either that represents a value, nothing, or an error.\n\nThere are a few major reasons for `tryad` to exist:\n\n* most existing Maybe / Option / Either / Result types in JavaScript don't\n  seamlessly interop with Promises\n* if the Maybe contains an array, you have to `map` inside a `map` because it\n  doesn't dispatch\n* `toMaybe` and `toEither` become unnecessary if the type simply has a third branch\n  to cover exceptions\n\nSo `tryad` attempts to create a new type that incorporates all of this - replacing\nboth `null`-ish checks and `try`/`catch` while working well with Promises.\n\n## installation\n\n```console\nnpm i tryad\n```\n\n## usage\n\n```js\nimport tryad from 'tryad'\nimport { resolve } from 'path'\n\nconst loadPlugin = atPath =\u003e {\n  return require(atPath).initialize()\n}\n\nconst plugin = { name: 'a-plugin' /*, path: './plugin.js' */ }\nconst loaded = tryad(plugin.path)\n  .map(path =\u003e resolve(process.cwd(), path))\n  .try(absolute =\u003e loadPlugin(absolute))\n  .unwrap(\n    // `loaded` becomes equal to the return value of\n    // whichever branch we hit here in `unwrap`\n    Some =\u003e 'loaded the plugin'\n    None =\u003e 'plugin.path was not defined, so nothing was executed'\n    Fail =\u003e 'a wild error appeared! ' + Fail.message\n  )\n\n// in this case `path` is not defined, so we get a `None`:\n// -\u003e 'plugin.path was not defined, so nothing was executed'\n```\n\nOr if you're all about that async, let's check out Promises!\n\n```js\nimport tryad from 'tryad'\nimport { readJson } from 'fs-extra'\n\n;(async () =\u003e {\n  const result = await tryad('./package.json')\n    // returning a Promise here like `readJson` does\n    // makes the chain's result become asynchronous\n    .try(path =\u003e readJson(path))\n    .map(pkg =\u003e pkg.keywords)\n    // methods dispatch to the inner value, so this\n    // maps over each keyword in the array\n    .map(key =\u003e key.toLowerCase())\n    .filter(key =\u003e key === 'maybe')\n    .unwrap(\n      Some =\u003e 'found the maybe keyword',\n      None =\u003e 'did not find the maybe keyword',\n      Fail =\u003e 'an error occurred! ' + Fail.message\n    )\n\n  /*...*/\n})()\n```\n\nAnd remember that if at any point a transformation causes a nil / falsy value\nto be returned or if `.try()` throws an Error, the following transformations\nwill not be executed. This makes null handling _very_ easy - in this case\nthe chain would 'skip forward' to the `unwrap` call.\n\n## api\n\n### `tryad`\n\n```js\ntryad(value)\n```\n\nReturns a new object that is either a `None` if `value == null` or a `Some`\ncontaining `value` otherwise.\n\n\u003e Expand the following sections to see the full documentation.\n\n\u003cdetails\u003e\n  \u003csummary\u003estatic methods\u003c/summary\u003e\n\n#### `of`\n\n```js\ntryad.of(value)\n```\n\nAlternate constructor that checks for truthiness rather than loose equality to `null`.\n\n#### `try`\n\n```js\ntryad.try(value, fn)\n```\n\nShortcut for `tryad.of(value).try(fn)`. If `value != null` it will be applied\nto the function `fn`, and any errors will be caught to transform the `tryad`\ninto a `Fail`. `fn` is only executed on a `Some`.\n\n\u003e **Arguments**\n\n  * `value: any`\n  * `fn: value -\u003e any`\n\n    Function that will receive `value` as its only parameter. All errors are\n    caught and returned in a new `Fail` object.\n\n\u003e **Returns** `Some | None | Fail`\n\n#### `async`\n\n```js\ntryad.async(value)\n```\n\nAsynchronous version of the `tryad` constructor that accepts `value` as a\nPromise, or wraps it in one if it isn't already a Promise.\n\n#### `async.of`\n\n```js\ntryad.async.of(value)\n```\n\nAsynchronous version of the `tryad.of` constructor that accepts `value` as a\nPromise, or wraps it in one if it isn't already a Promise.\n\n#### `isSomeLike`\n\n```js\ntryad.isSomeLike(value)\n```\n\nUseful for checking if an arbitrary object is a `Some`, meaning\nit is not `== null` and has an `isSome` method that returns `true`.\n\n\u003e **Arguments**\n\n  * `value: any`\n\n\u003e **Returns** `Boolean`\n\n#### `isNoneLike`\n\n```js\ntryad.isNoneLike(value)\n```\n\nUseful for checking if an arbitrary object is a `None`, meaning\nit is not `== null` and has an `isNone` method that returns `true`.\n\n\u003e **Arguments**\n\n  * `value: any`\n\n\u003e **Returns** `Boolean`\n\n#### `isFailLike`\n\n```js\ntryad.isFailLike(value)\n```\n\nUseful for checking if an arbitrary object is a `Fail`, meaning\nit is not `== null` and has an `isFail` method that returns `true`.\n\n\u003e **Arguments**\n\n  * `value: any`\n\n\u003e **Returns** `Boolean`\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n  \u003csummary\u003einstance methods\u003c/summary\u003e\n\nThese methods are callable on instances of a `Some`, `None`, or `Fail`.\nExamples will use the name `box` to represent one of these instances.\n\n#### `filter`\n\n```js\nbox.filter(fn)\n```\n\n`fn` will only be executed if `box` is a `Some`, and receives its value as its\nonly argument. If it returns falsy, a `None` will be returned. If it returns\ntruthy, the current instance is returned as-is. Dispatches to `value.filter()`\nif it is callable.\n\n\u003e **Arguments**\n\n  * `fn: (value) -\u003e Boolean`\n\n\u003e **Returns** `Some | None`\n\n#### `flatMap`\n\n```js\nbox.flatMap(fn)\n```\n\n`fn` will only be executed if `box` is a `Some`, and receives its value as its\nonly argument. Any returned `tryad` will be absorbed. Dispatches to `value.flatMap()`\nif it is callable.\n\n\u003e **Arguments**\n\n  * `fn: (value) -\u003e Some | None | Fail`\n\n\u003e **Returns** `Some | None | Fail`\n\n#### `forEach`\n\n```js\nbox.forEach(fn)\n```\n\n`fn` will only be executed if `box` is a `Some`, and receives its value as its\nonly argument. This method ends the chain. Dispatches to `value.forEach()` if\nit is callable.\n\n\u003e **Arguments**\n\n  * `fn: (value) -\u003e any`\n\n\u003e **Returns** `undefined`\n\n#### `includes`\n\n```js\nbox.includes(other)\n```\n\nIf `box` is a Some, compares `other` against the value contained within `box`\nand returns `true` if they are equal. Returns `false` if `box` is not a `Some`.\nDispatches to `value.includes()` if it is callable.\n\n\u003e **Returns** `Boolean`\n\n#### `map`\n\n```js\nbox.map(fn)\n```\n\n`fn` will only be executed if `box` is a `Some`, and receives its value as its\nonly argument. The return value will be used to construct a new `tryad`. If\n`fn` will return a `tryad`, you probably want to use `flatMap` instead.\nDispatches to `value.map()` if it is callable.\n\n\u003e **Arguments**\n\n  * `fn: (value) -\u003e any`\n\n\u003e **Returns** `Some | None`\n\n#### `orElse`\n\n```js\nbox.orElse(fn)\n```\n\n`fn` is only called if `box` is a `None` or a `Fail`. If `box` is a `Fail`,\n`fn` will receive the error contained within.\n\n\u003e **Arguments**\n\n  * `fn: (error?) -\u003e any`\n\n\u003e **Returns** `Some | any`\n\n#### `orSome`\n\n```js\nbox.orSome(other)\n```\n\nReturns the value contained within `box` if it is a `Some`, or returns\n`other` if it is a `None` or a `Fail`. This method ends the chain.\n\n\u003e **Arguments**\n\n  * `other: any`\n\n\u003e **Returns** `any`\n\n#### `some`\n\n```js\nbox.some()\n```\n\nReturns the value contained within `box`. Throws if `box` is not a `Some`, so it's safer to use [`orSome`](#-orSome-). This method ends the chain.\n\n\u003e **Returns** `any`\n\n#### `try`\n\n```js\nbox.try(fn)\n```\n\nAttempts to call `fn(value)` and catches any error that occurs, returning a\n`Fail` with the error. Not executed if `box` is not a `Some`.\n\n\u003e **Arguments**\n\n  * `fn: value -\u003e Some | None | Fail`\n\n\u003e **Returns** `Some | None | Fail`\n\n#### `unwrap`\n\n```js\nbox.unwrap(ifSome, ifNone, ifFail)\n```\n\nCalls whichever function corresponds to the instance type and returns its\nvalue.\n\n\u003e **Arguments**\n\n  * `ifSome: value -\u003e any`\n  * `ifNone: () -\u003e any`\n  * `ifFail: error -\u003e any`\n\n\u003e **Returns** `any`\n\n\u003c/details\u003e\n\n## see also\n\n- [`param.macro`](https://github.com/citycide/param.macro) - Babel plugin for compile time partial application \u0026 lambda parameters\n\n## contributing\n\nPull requests and any [issues](https://github.com/citycide/tryad/issues)\nfound are always welcome.\n\n1. Fork the project, and preferably create a branch named something like `feat-make-better`\n2. Modify the source files as needed\n3. Make sure all tests continue to pass, and it never hurts to have more tests\n4. Push \u0026 pull request! :tada:\n\n## license\n\nMIT © [Bo Lingen / citycide](https://github.com/citycide)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhaltcase%2Ftryad","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhaltcase%2Ftryad","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhaltcase%2Ftryad/lists"}