{"id":19329936,"url":"https://github.com/marmelab/limonade","last_synced_at":"2025-04-22T22:32:43.658Z","repository":{"id":33818852,"uuid":"158283236","full_name":"marmelab/liMonade","owner":"marmelab","description":"A Monad library to make Monad simple.","archived":false,"fork":false,"pushed_at":"2023-01-03T19:52:35.000Z","size":926,"stargazers_count":9,"open_issues_count":12,"forks_count":1,"subscribers_count":9,"default_branch":"master","last_synced_at":"2024-04-13T02:08:37.074Z","etag":null,"topics":["javascript","monads"],"latest_commit_sha":null,"homepage":"https://github.com/marmelab/liMonade","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/marmelab.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":"2018-11-19T20:05:56.000Z","updated_at":"2021-12-15T09:44:23.000Z","dependencies_parsed_at":"2023-01-15T02:44:49.291Z","dependency_job_id":null,"html_url":"https://github.com/marmelab/liMonade","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/marmelab%2FliMonade","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marmelab%2FliMonade/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marmelab%2FliMonade/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marmelab%2FliMonade/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marmelab","download_url":"https://codeload.github.com/marmelab/liMonade/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":223906420,"owners_count":17223045,"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":["javascript","monads"],"created_at":"2024-11-10T02:32:32.577Z","updated_at":"2024-11-10T02:32:33.551Z","avatar_url":"https://github.com/marmelab.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"A Monad library to make Monad simple.\n\n# Why liMonade ?\n\nFunctor! Monad! Applicative! Traversable! What scary words, yet you probably use them every day without even realizing it.\nArray with its map method ? yep that's a functor.\nThe Promise object? you know what? It's a monad.\nAnd the observable from rxjs: One of the most complex monad there is.\n\nThis library is me trying to make monad accessible, and easy to use.\n\n# Why should I use this ?\n\nMonad are for modifying value with function without having to worry about context.\nThat is what the map method do. (For promise it's called then)\nHere is an example:\n\n```js\nconst toUppercase = s =\u003e s.toUpperCase();\n['hello', 'world'].map(toUpperCase);\n// ['HELLO', 'WORLD'];\nPromise.resolve('hello world').then(toUpperCase);\n// Promise\u003c'HELLO WORLD'\u003e;\n```\n\nHere toUppercase is used with multiple values, and with an asynchronous result, and yet it does not have to worry about it. It just takes a string and changes it to uppercase.\n\nEach Monad are for handling a specific context.\nMaybe are for handling null value.\nEither for error.\nList for multiple value.\nIO for side effect.\nReader for shared dependencies.\nWriter for log.\nState for stateful computation.\nTask for asynchronous operation.\nIdentity for no context at all.\n\n# Identity\n\nIdentity is the basic Monad, it just apply function nothing less nothing more.\nIt is a good starting point to familiarize with the concept.\n\nIdentity has no real use case, so the example in this section are quite abstract. But do remember other monads works the same way. Better example will be given in the following sections.\n\nIdentity is both a Functor (it has a map method), a Monad (it has a chain method) an applicative (ap method) and a traversable (sequence and traverse)\n\nLike all Monad, you create an identity by calling the Identity function, or its of method.\n\n`of` means takes a value and put it in a monad. For promise we call it `resolve`.\n\n```js\nconst identity = Identity(5);\n// is the same as\nconst identity = Identity.of(5);\n\n// on an identtiy you can retrieve the value with the value property\nidentity.value // 5\n\n// To change the value you use map\nconst identity2 = identity.map(v =\u003e v * 2);\nidentity2.value; // 10\n// notice that map create a new identity so\nidentity.value; // 5\n// is unchanged\n```\n\n## Identity is a functor with a map method\n\n`map` exists on all functor and has the following properties:\n\n- It only changes the value holded by the functor and nothing else\n\n```js\nIdentity.of(5).map(v =\u003e v);\n// is equivalent to\nIdentity.of(5);\n```\n- It allows to compose function\n\n```js\nconst toUppercase = v =\u003e v.toUppercase();\nconst split = v =\u003e v.split('');\nIdentity.of('functor').map(uppercase).map(split);\n// is the same as\nIdentity.of('functor').map(v =\u003e split(uppercase(v)));\n```\n\nSo mapping uppercase then split is the same as mapping the composition of uppercase and split hence map compose function.\n\n\n## Identity is a monad with a chain method\n\nWhen you begin to use functor extensively, you can endup to need to compose a function that returns a functor.\n\n```js\nconst doubleId = v =\u003e Identity.of(v * 2);\n\nconst idOfId = Identity.of(5).map(doubleId);\n// And now we have an identity in an identity\nid.value; // Identity\u003c5\u003e\nid.value.value; // 5\n```\n\nMonad with its chain method allows to map such a function while removing the excess monad.\n\n```js\nconst doubleId = v =\u003e Identity.of(v * 2);\n\nconst idOf10 = Identity.of(5).chain(doubleId);\nidOf10.value; // 10\n```\n\nMonad also offer a flatten method for when you already have a monad in a monad and just want to merge them.\n\n```js\nIdentity.of(Identity.of(5)).flatten(); // Identity\u003c5\u003e\n```\n## Identity is an applicative functor with an ap method\n\nHow do you map a function that take more than a parameter ?\nWell you cannot.\nBut you can map a function that returns a function.\n```js\nconst add = a =\u003e b =\u003e a + b;\nfnId = Identity.of(5).map(add); // Identity\u003cv =\u003e 5 + v\u003e\n// But now we have an identity holding a function and not a value.\nfnId.map(fn =\u003e fn(2)); // Identity\u003c7\u003e\n// is not really practical\n// The ap method to the rescue.\nfnId.ap(Identity.of(2)); // Identity\u003c7\u003e\n```\n\nThe ap method can only be called on an identity holding a function.\nIt takes another identity holding a value and call the contained function with it.\n\n## Identity is a traversable\n\nHere is a weird one.\nLet's say that you end up with an identity holding a List as its value.\nNo let's say that you'd prefer to have a List of identity.\nTraversable allow you to do just that.\nGo to the List section for an example with a List of Tasks becoming a Task of a List.\n\nThe sequence method just permute the two monads:\n```ts\nconst id = Identity.of(List([1, 2, 3])); // Identity\u003cList\u003c[1, 2, 3]\u003e\u003e\nid.sequence(Maybe.of); // List\u003c[Identity\u003c1\u003e, Identity\u003c2\u003e, Identity\u003c3\u003e]\u003e\n```\n\nThe traverse method first transforms the value to an applicative, then do the same as sequence.\n\n```js\nconst stringToList = v =\u003e List(v.split(''));\nconst id = Identity.of('hey')); // Identity\u003c'hey'\u003e\nid.traverse(stringToList, List.of); // List\u003c[Identity\u003ch\u003e, Identity\u003ce\u003e, Identity\u003cy\u003e]\u003e\n```\n\nSee List for a more practical example with a List of asynchronous Task becoming a Task of a list of value.\n\n## Identity can lift\n\nFinally Identity offer a lift function.\nlift is an helper that take a function and returns a new function that wraps its result in an Identity.\n\nWith lift when you want a function that returns an identity, you can take a normal function then lift it.\n\n```js\nconst double = v =\u003e v * 2;\ndoubleThatReturnsAnIdentity = Identity.lift(double);\n\ndoubleThatReturnsAnIdentity(5); // Identity\u003c10\u003e\n```\n\n# Maybe\n\nMaybe is used to handle null value. That is, if we have no value, then it won't call the function and so it will keep a null value. No more need to test if we have a value or not.\nMaybe has the same methods as identity.\nLet's say that we want to retrieve value from the localStorage and do computation on it only if it's here.\n\n## Example\n\n```js\nconst getUser = window.localStorage.getItem('user');\n```\n\nIf we wanted to compose getUser with other function let's say to decode the json object, the next function down the composition should check if there is a user.\nMaybe to the rescue.\nFirst we can lift getUser so that it wrap its return value in a Maybe.\n```js\nconst maybeGetUser = Maybe.lift(getUser)\nconst maybeAnUser = maybeGetUser().map(JSON.parse);\n```\n\nNow let's say that the user maybe has a basket id, and that we want to retrieve the basket with this id, also in the localStorage.\n\n```js\nconst getBasket = id =\u003e window.localStorage(id);\nconst maybeGetABasket = Maybe.lift(getBasket);\nconst maybeABasket = maybeAnUser\n    .map(user =\u003e user.basket)\n    .chain(maybeGetABasket)\n    .map(JSON.parse);\n```\n\nFinally we want to know if we have a basket or not.\nWe could use `.value` to unwrap it but Maybe offer helper for that :\n\n```js\nconst basket = maybeABasket.getOrElse('no basket');\n// { ...basketObject } || 'no basket'\n\n// or\n\nif(maybeABasket.isNothing()) {\n    // we have no value\n} else {\n    // we have a value\n}\n```\n\n## Maybe api\n\n- Maybe: takes a value and returns a maybe, if the passed value is null or undefined, all operation on the maybe that would change its value will be ignored.\n\n- Maybe.of: same as Maybe\n\n- Maybe.lift: Takes a function and wraps its return value in a Maybe\n```ts\n// Maybe.lift(fn: A =\u003e B): (v: A) =\u003e Maybe\u003cB\u003e;\nconst fn = Maybe.lift(v =\u003e v * 2);\n// is the same as\nconst fn = v =\u003e Maybe.of(v * 2);\n```\n\n- maybe.map: Takes a function and applies it to the value if there is one\n```ts\n// Maybe\u003cA\u003e.map\u003cfn: A =\u003e B\u003e: Maybe\u003cB\u003e;\nMaybe.of(5).map(v =\u003e v * 2);\n-\u003e Maybe\u003c10\u003e;\n// Maybe\u003cnull | undefined\u003e.map(fn: A =\u003e B): Maybe\u003cnull | undefined\u003e;\nMaybe.of(null).map(v =\u003e v * 2);\n-\u003e Maybe\u003cnull\u003e;\n```\n- maybe.flatten: Given a maybe holding another maybe, flatten will merge the two together.\n```ts\n// Maybe\u003cMaybe\u003cA\u003e\u003e.flatten(): Maybe\u003cA\u003e;\nMaybe.of(Maybe.of(5)).flatten();\n-\u003e Maybe\u003c5\u003e;\n```\nIf the maybe holds nothing, flatten do nothing\n```ts\n// Maybe\u003cnull | undefined\u003e.flatten(): Maybe\u003cnull | undefined\u003e;\nMaybe.of(null).flatten();\n-\u003e Maybe\u003cnull\u003e;\n```\n- maybe.chain: Takes a function returning a maybe, map it and then flatten the result.\n```ts\n//Maybe\u003cA\u003e.chain(fn: A =\u003e Maybe\u003cB\u003e): Maybe\u003cB\u003e;\nMaybe.of(5).chain(v =\u003e Maybe.of(v * 2));\n-\u003e Maybe\u003c10\u003e;\n```\nIf the maybe holds no value, chain do nothing\n```ts\n// Maybe\u003cnothing\u003e.chain(fn: A =\u003e Maybe\u003cB\u003e): Maybe\u003cnothing\u003e;\nMaybe.of(null).chain(v =\u003e Maybe.of(v * 2));\n-\u003e Maybe\u003cnull\u003e;\n```\n- maybe.ap: Given a maybe holding a function. Ap takes another maybe. It will execute the function of the first maybe with the value of the other maybe. The result is a new maybe with the result of the function.\n\n```ts\n// Maybe\u003cA =\u003e B\u003e.ap(other: Maybe\u003cA\u003e): Maybe\u003cB\u003e;\nMaybe.of(v =\u003e v * 2).ap(Maybe.of(5));\n-\u003e Maybe\u003c10\u003e;\n```\nIf the maybe holds no value, ap does nothing.\n```ts\n// Maybe\u003cnothing\u003e.ap(other: Maybe\u003cA\u003e): Maybe\u003cnothing\u003e;\nMaybe.of(null).ap(Maybe.of(5));\n-\u003e Maybe\u003cnull\u003e;\n```\nIf the maybe passed to ap holds nothing, ap will return it.\n```ts\n// Maybe\u003cA =\u003e B\u003e.ap(other: Maybe\u003cnothing\u003e): Maybe\u003cnothing\u003e;\nMaybe.of(v =\u003e v * 2).ap(Maybe.of(null));\n-\u003e Maybe\u003cnull\u003e;\n```\n- maybe.sequence: when the maybe holds an applicative, it will swap the maybe with the applicative.\n```ts\n/**\n * Maybe\u003cApplicative\u003cA\u003e\u003e.sequence(\n *     of: Value =\u003e Applicative\u003cValue\u003e,\n * ): Applicative\u003cMaybe\u003cA\u003e\u003e;\n * */\nMaybe.of(Identity.of(5)).sequence(Identity.of);\n    -\u003e Identity\u003cMaybe\u003c5\u003e\u003e;\n```\nWhen the maybe does not hold anything, it will place it inside an applicative.\n```ts\n/*\n * Maybe\u003cnothing\u003e.sequence\u003cA\u003e(\n *     of: Value =\u003e Applicative\u003cValue\u003e,\n * ): Applicative\u003cMaybe\u003cnothing\u003e\u003e;\n * */\nMaybe.of(null).sequence(Identity.of);\n    -\u003e Identity\u003cMaybe\u003cnull\u003e\u003e;\n```\n- maybe.traverse: map a function returning an applicative and then swap the applicative with the maybe.\n```ts\n/*\n * Maybe\u003cApplicative\u003cA\u003e\u003e.traverse(\n *     fn: A =\u003e Applicative\u003cB\u003e,\n *     of: Value =\u003e Applicative\u003cValue\u003e,\n * ): Applicative\u003cMaybe\u003cB\u003e\u003e;\n * */\nMaybe.of(5).traverse(v =\u003e Identity.of(v * 2));\n    -\u003e Identity\u003cMaybe\u003c10\u003e\u003e;\n```\nIf the maybe holds nothing, an Apllicative\u003cMaybe\u003cnothing\u003e\u003e is returned\n```ts\n/*\n * Maybe\u003cnothing\u003e.traverse(\n *     of: Value =\u003e Applicative\u003cValue\u003e,\n *     fn: A =\u003e Applicative\u003cB\u003e,\n * ): Applicative\u003cMaybe\u003cnothing\u003e\u003e;\n * */\nMaybe.of(null).traverse(v =\u003e Identity.of(v * 2));\n    -\u003e Identity\u003cMaybe\u003c10\u003e\u003e;\n```\n- maybe.isNothing: returns true if the maybe holds nothing (null or undefined)\n\n```ts\nMaybe\u003cA\u003e.isNothing(): false;\nMaybe\u003cnothing\u003e.isNothing(): true;\n```\n\n- maybe.isJust: returns true if the maybe holds a value\n\n```ts\nMaybe\u003cA\u003e.isJust(): true;\nMaybe\u003cnothing\u003e.isJust(): false;\n```\n- maybe.getOrElse: returns the Maybe value or the given value if the maybe holds nothing.\n\n```ts\nMaybe\u003cA\u003e.getOrElse(defaultValue: B): A;\nMaybe\u003cnothing\u003e.getOrElse(defaultValue: B): B;\n```\n\n# Either\n\nEither is used to handle possible error, if receiving an error it will preserve it, ignoring all operations. If any mapped function would throw an error, the error will be caught and placed in the Either.\nWhen the either holds a value, we say it is right, otherwise it is left.\n\n## Example\n\nLet's say we retrieved an user from localStorage, where it was json encoded.\nWe then want to parse it using JSON.parse. Problem, if the json is malformed, we will get an error. Also we want to check that the user is valid, with a name, if there is no user we want an error, and if it has no name. we want another error.\n\n```js\nconst parseJSON = JSON.parse;\nparseJSON('I am a user. I swear!'); // Error(Unexpected token I in JSON at position 0)\n\nconst validateUser = user =\u003e {\n    if (!user) {\n        throw new Error('Received no user');\n    }\n\n    if (typeof user.name !== 'string') {\n        throw new Error('Invalid User: Missing name');\n    }\n}\n```\n\nEither to the rescue :\n\n```js\nconst tryToParseJSON = Either.lift(parseJSON);\nconst tryToValidateUser = Either.lift(validateUser);\n\nconst parseUser = json =\u003e tryToParseJSON(json)\n    .chain(tryToValidateUser);\n\nparseUser('I am a user. I swear!'); // Either\u003cError(Error(Unexpected token I in JSON at position 0))\u003e\nparseUser(null); // Either\u003cError(Received no user)\u003e\nparseUser({ foo: 'bar' }); // Either\u003cError(Invalid User: Missing name)\u003e\nparseUser({ name: 'john' }); // Either\u003c{ name: 'john' }\u003e\n```\n\nNow let's say that we want a default user if parseUser failed. We can then use the catch method.\n\n```js\nparseUser(null).catch(error =\u003e ({ name: 'anonymous' })); // Either\u003c'anonymous'\u003e\n```\n\n`catch` is executed only if the either hold an error, in which case it will transform the error with the given function and return an either holding the new value.\n\nTo get the value, we can call get.\nget will either return the value, or throw the error.\n\n```js\nparseUser(null).get(); // thow Error('Received no user');\nparseUser('{ \"name\": \"john\" }').get(); // { name: 'john' }\n```\n\n## api\n\n- Either take a value and return an either, if the passed value is an error, all operation on the either to change the value will be ignored.\n- Either.of: same as Either\n- Either.Right: same as Either but accept only non error value. Return a Right Either\n- Either.Left: same as Either but accept only error value. Return a Left Either\n- Either.lift: Takes a function and wraps its return value in a Right Either. If it throws an error, it will be wrapped in a left Either instead.\n```ts\n// Either.lift(fn: A =\u003e B): (v: A) =\u003e Either\u003cB\u003e;\nEither.lift(v =\u003e v * 2);\n    -\u003e v =\u003e Either.of(v * 2);\n// Either.lift(fn: A =\u003e throw Error): (v: A) =\u003e Either\u003cError\u003e;\nEither.lift(v =\u003e throw new Error('Boom'));\n    -\u003e v =\u003e Either.of(new Error('Boom'));\n```\n\n- either.map: Take a function and apply it to the value if it's not an error.\n\n```ts\n// Either\u003cA\u003e.map(fn: A =\u003e B): Either\u003cB\u003e;\nEither.of(5).map(v =\u003e v * 2);\n-\u003e Either\u003c10\u003e;\n```\nIf Either holds an error, map does nothing\n```ts\n// Either\u003cError\u003e.map(fn: A =\u003e B): Either\u003cError\u003e;\nEither.of(new Error('Boom')).map(v =\u003e v * 2);\n-\u003e Either\u003cnew Error('Boom')\u003e;\n```\nIf the function throw an error a Left Either of the error is returned\n```ts\n// Either\u003cError\u003e.map(fn: A =\u003e B): Either\u003cError\u003e;\nEither.of(5).map(v =\u003e { throw new Error('Boom'); });\n-\u003e Either\u003cnew Error('Boom')\u003e;\n```\n\n- either.flatten: Given the either holds another either, flatten will merge the two together.\n\n```ts\n// Either\u003cEither\u003cA\u003e\u003e.flatten(): Either\u003cA\u003e;\nEither.of(Either.of(5)).flatten();\n    -\u003e Either\u003c5\u003e\n```\nIf the either holds an error it does nothing.\n```ts\n// Either\u003cError\u003e.flatten(): Either\u003cError\u003e;\nEither.of(new Error('Boom')).flatten();\n    -\u003e Either\u003cnew Error('Boom')\u003e\n```\n\n- either.chain: Takes a function returning an either, map it to the value and then flatten the resulting either.\n```ts\n// Either\u003cA\u003e.chain\u003cA, B\u003e(fn: A =\u003e Either\u003cB\u003e): Either\u003cB\u003e;\nEither.of(5).chain(v =\u003e Either(v * 2));\n    -\u003e Either\u003c10\u003e\n```\nIf the either holds an error, chain does nothing\n```ts\n// Either\u003cError\u003e.chain(fn: A =\u003e Either\u003cB\u003e): Either\u003cError\u003e;\nEither.of(5).chain(v =\u003e v.concat('uh'));\n    -\u003e Either\u003cError('v.concat is not a function')\u003e\n```\n- either.ap: Given an either holding a function. Ap takes another either. It will execute the function of the first either with the value of the other either. The result is a new either with the result of the function.\n```ts\n// Either\u003cA =\u003e B\u003e.ap(other: Either\u003cA\u003e): Either\u003cB\u003e;\nEither.of(v =\u003e v * 2).ap(Either.of(5));\n    -\u003e Either\u003c10\u003e\n```\nIf the either holds an error, ap does nothing\n```ts\n// Either\u003cError\u003e.ap(other: Either\u003cA\u003e): Either\u003cError\u003e;\nEither.of(new Error('Boom')).ap(Either.of(5));\n    -\u003e Either\u003cnew Error('Boom')\u003e\n```\nIf the passed either holds an error, ap return the other either\n```ts\n// Either\u003cA =\u003e B\u003e.ap(other: Either\u003cError\u003e): Either\u003cError\u003e;\nEither.of(v =\u003e v * 2).ap(Either.of(new Error('Boom')));\n    -\u003e Either\u003cnew Error('Boom')\u003e\n```\n- either.sequence: when the either hold an applicative, it will swap the either with the applicative.\n```ts\n/*\n * Either\u003cApplicative\u003cA\u003e\u003e.sequence(\n *     of: Value =\u003e Applicative\u003cValue\u003e,\n * ): Applicative\u003cEither\u003cA\u003e\u003e;\n * */\nEither.of(Identity.of(5)).sequence(Identity.of);\n    -\u003e Identity\u003cEither\u003c5\u003e\u003e\n```\nIf the either holds an error it will place the either inside an applicative.\n```ts\n/*\n * Either\u003cError\u003e.sequence(\n *     of: Value =\u003e Applicative\u003cValue\u003e,\n * ): Applicative\u003cEither\u003cError\u003e\u003e;\n * */\nEither.of(new Error('Boom')).sequence(Identity.of);\n    -\u003e Identity\u003cEither\u003cnew Error('Boom')\u003e\u003e\n```\n- either.traverse: Is the combination of map followed by sequence. It first modifies the holded value with the given function. The function must returns an applicative. And then it sequence, swapping the either with the applicative.\n```ts\n/*\n * Either\u003cApplicative\u003cA\u003e\u003e.traverse(\n *     fn: A =\u003e Applicative\u003cB\u003e,\n *     of: Value =\u003e Applicative\u003cValue\u003e,\n * ): Applicative\u003cEither\u003cB\u003e\u003e;\n * */\nEither.of(5).traverse(v =\u003e Identity.of(v * 2));\n    -\u003e Identity\u003cEither\u003c10\u003e\u003e\n```\nIf the function throw an error, the error will be palced in an either, and the either in an applicative (Applicative\u003cMaybe\u003cError\u003e\u003e)\n```ts\n/*\n * Either\u003cError\u003e.sequence(\n *     fn: A =\u003e Applicative\u003cB\u003e,\n *     of: Value =\u003e Applicative\u003cValue\u003e,\n * ): Applicative\u003cEither\u003cError\u003e\u003e;\n * */\nEither.of(5).traverse(v =\u003e throw new Error('Boom'));\n    -\u003e Identity\u003cEither\u003cnew Error('Boom')\u003e\u003e\n```\n- maybe.catch: Works like map, it accept a function, and will call it only if the value is an error. It Will transform the Error in a value and returns a Right.\n```ts\n// Either\u003cA\u003e.catch(fn: Error =\u003e B): Either\u003cA\u003e;\nEither.of(5).catch(error =\u003e error.message);\n    -\u003e Either\u003c5\u003e\n// Either\u003cError\u003e.catch(fn: Error =\u003e B): Either\u003cB\u003e;\nEither.of(new Error('Boom')).catch(error =\u003e error.message);\n    -\u003e Either\u003c'Boom'\u003e\n```\n- maybe.isLeft: returns true if the maybe holds an error\n```ts\nEither\u003cA\u003e.isLeft(): false;\nEither\u003cError\u003e.isLeft(): true;\n```\n- maybe.isRight: returns true if the maybe holds a value\n```ts\nEither\u003cA\u003e.isRight(): true;\nEither\u003cError\u003e.isRight(): false;\n```\n- maybe.get: returns the value or throw the error\n```ts\nEither\u003cA\u003e.get(): A;\nEither\u003cError\u003e.get(): throw Error;\n```\n\n## IO\n\nIO is a shorthand for `Input/Output` IO is used to handle side effects. Instead of holding the value directly, IO holds a function that will return the value. The important distinction compared to the previous monads is that IO is lazy. We can map all we want, nothing will be executed until you call its execute method.\nIt is useful to handle computations that depends on an external factor. Like user input for example.\n\n### Example\n\nLet's say that we have an input field and an output where we want to display what the user typed, but in uppercase\n\n```html\n\u003cinput id=\"input\" onInput=\"displayInput()\" /\u003e\n\n\u003cp id=\"output\"\u003e\u003c/p\u003e\n```\n\nA side note for previous Monad, the of method was the same as the constructor for other Monad. FOr IO that is not the xcase anymore.\nIO take the side effect function that will return the value.\nIO.of take a value and wrap it in a function, creating a side effect from it. Sort of.\n\n```js\nconst inputIO = IO(() =\u003e document.getElementById('input').value);\nconst outputIO = IO.of(\n    value =\u003e document.getElementById('output').innerText = value\n); // notice how outputIO hold a function and not a value\nconst toUpperCase = v =\u003e v.toUpperCase()\n\nconst displayInput = outputIO.ap(\n    inputIO.map(toUpperCase)\n).execute;\n```\n// TODO add a codepen link once the library is published\n\n### IO api\n\n- IO takes a side effect function and return an IO that will allow to operate on the side effect return value\n\n```ts\n// IO(fn: () =\u003e Value): IO\u003cValue\u003e\nIO(() =\u003e 5);\n    -\u003e IO\u003c5\u003e\n```\n\n- IO.fromSideEffect: Same as IO\n\n- IO.of: take a value and return an IO that will allow to operate on the value\n\n```ts\n// IO.of(v: Value): IO\u003cValue\u003e\nIO.of(5)\n    -\u003e IO\u003c5\u003e\n```\n\n- IO.lift: Take a function and wrap its return value in a IO\n```ts\n// IO.lift(fn: A =\u003e B): (v: A) =\u003e IO\u003cB\u003e;\nIO.lift(v =\u003e v * 2);\n    -\u003e v =\u003e IO.of(v * 2);\n```\n\n- io.map: Take a function and apply it to the side effect return value (this is lazy and won't execute the side effect)\n```ts\n// IO\u003cA\u003e.map(fn: A =\u003e B): IO\u003cB\u003e;\nconst io = IO(() =\u003e 5).map(v =\u003e v * 2);\n    -\u003e IO\u003c10\u003e\n```\n\n- io.flatten: If the value is another IO merge the two together. It's lazy.\n```ts\n// IO\u003cIO\u003cA\u003e\u003e.flatten(): IO\u003cA\u003e;\nIO.of(IO.of(5)).flatten();\n    -\u003e IO\u003c5\u003e\n```\n\n- io.chain\n```ts\n// IO\u003cA\u003e.chain(fn: A =\u003e IO\u003cB\u003e): IO\u003cB\u003e;\nIO.of(5).chain(v =\u003e IO.of(v * 2));\n    -\u003e IO\u003c10\u003e\n```\n\n- io.ap\n```ts\n// IO\u003cA =\u003e B\u003e.ap(other: IO\u003cA\u003e): IO\u003cB\u003e;\nIO.of(v =\u003e v * 2).ap(IO.of(5));\n    -\u003e IO\u003c10\u003e\n```\n\n- io.execute: trigger the side effect and all the added computation and returns the result\n```ts\n// IO\u003cA\u003e.execute(): A;\nIO(() =\u003e 'result').execute();\n    -\u003e 'result'\nIO.of('result').execute();\n    -\u003e 'result'\n```\n\n## Task\n\nTask is like Promise. It handle asynchronous task, but with a key difference: like IO it is lazy.\nThis means that you decide when the Task is executed. You can even execute it several times.\n\nExample:\n\nLet's say we want to be able to fetch but with Task instead of Promise.\n\n```js\nconst fetchUrl = url =\u003e Task((reject, resolve) =\u003e {\n    fetch(url)\n        .then(response =\u003e response.json())\n        .then(resolve)\n        .catch(reject);\n});\n\nconst getJokeTask = number =\u003e Task.of(number)\n    .map(value =\u003e `https://api.icndb.com/jokes/${value}`)\n    .chain(fetchUrl)\n    .map(({ value: { joke } }) =\u003e joke)\n    .map(value =\u003e value.toUpperCase())\n    .catch(error =\u003e 'joke not found');\n\ngetJoke42Task = getJoke(42) // nothing is executed yet: Task\u003c?\u003e\n// You can either add additional computation or execute it\n\ngetJoke42Task.then(console.log, console.log); // let's trigger the task\n// \"CHUCK NORRIS DOESN'T CHURN BUTTER. HE ROUNDHOUSE KICKS THE COWS AND THE BUTTER COMES STRAIGHT OUT.\"\n\ngetJoke(9999).then(console.log, console.log); // \"joke not found\"\n```\n\n### Task api\n\n- Task takes an asynchronous function and return a Task\n\n```ts\n/*\n * Task(\n *     cps: (resolve: Value =\u003e void, reject: Error =\u003e void) =\u003e void,\n * ): Task\u003cValue\u003e\n * */\nTask((resolve, reject) =\u003e resolve('success'));\n    -\u003e Task\u003c'success'\u003e\n```\n\n- Task.of: take a value and return a Task that will allow to operate on the value\n\n```ts\n// Task.of\u003cValue\u003e(value: Value): Task\u003cValue\u003e\nTask.of(5);\n    -\u003e Task\u003c5\u003e\n```\n\n- Task.reject: takes a value and returns a Task rejected with it. A rejected Task like a left either will ignore all operations on it except catch.\n\n```ts\nTask.reject('error').map(v =\u003e v * 2);\n    -\u003e RejectedTask\u003c'error'\u003e\n```\n\n- Task.lift: Takes a function and wrap its return value in a Task\n```ts\n// Task.lift(fn: A =\u003e B): A =\u003e Task\u003cB\u003e;\nTask.lift(v =\u003e v * 2);\n    -\u003e v =\u003e Task.of(v * 2);\n```\n\n- task.map: Takes a function and apply it to the async return value (this is lazy)\n```ts\n// Task\u003cA\u003e.map(fn: A =\u003e B): Task\u003cB\u003e;\nTask.of(5).map(v =\u003e v * 2);\n    -\u003e Task\u003c10\u003e\nTask.reject('error').map(v =\u003e v * 2);\n    -\u003e Task\u003c'error'\u003e\n```\n\n- task.catch: Takes a function and apply it to the async error value if there is one. (this is lazy)\n```ts\n// Task\u003cA\u003e.catch(fn: Error =\u003e B): Task\u003cA\u003e;\nTask.of(5).catch(v =\u003e v * 2);\n    -\u003eTask\u003c5\u003e\n// RejectedTask\u003cA\u003e.catch(fn: A =\u003e B): Task\u003cB\u003e;\nTask.reject(5).catch(v =\u003e v * 2);\n    -\u003eTask\u003c10\u003e\n```\n\n- task.flatten: If the value is another Task merge the two together. It's lazy.\n```ts\n// Task\u003cTask\u003cA\u003e\u003e.flatten(): Task\u003cA\u003e;\nTask.of(Task.of(5)).flatten();\n    -\u003e Task\u003c5\u003e\n// RejectedTask\u003cA\u003e.flatten(): RejectedTask\u003cA\u003e;\n\nTask.reject(5).flatten();\n    -\u003e RejectedTask\u003c5\u003e\n```\n\n- task.chain\n```ts\n// Task\u003cA\u003e.chain(fn: A =\u003e Task\u003cB\u003e): Task\u003cB\u003e;\nTask.of(5).chain(v =\u003e Task.of(v * 2));\n    -\u003e Task\u003c10\u003e\n// RejectedTask\u003cA\u003e.chain(fn: A =\u003e Task\u003cB\u003e): RejectedTask\u003cA\u003e;\nTask.reject(5).chain(v =\u003e Task.of(v * 2));\n    -\u003e RejectedTask\u003c5\u003e\n```\n\n- task.then: Takes a resolve and a reject callback and call resolve with the async operation result, or reject with the error.\n```ts\n// Task\u003cValue\u003e.then(resolve: Value =\u003e void, reject?: Error =\u003e void): void\nTask.of(5).then(\n    value =\u003e // will be called with 5\n    error =\u003e // will not be called\n);\n\nTask.reject(5).then(\n    value =\u003e  // will not be called\n    error =\u003e // will be called with 5\n);\n\n```\n\n- task.toPromise convert the task into a promise. This will trigger the asynchronous operation\n```ts\n// Task\u003cValue\u003e.toPromise(): Promise\u003cValue\u003e\n```\n\n## List\n\nList is used to handle multiple value just like Array. It is a Functor like Array, but it is also a Monad, an Applicative Functor and a traversable. You probably frowned upon the sequence and traverse method of the previous monad, but believe me on the List, it is one hell of a powerfull feature.\n\n## Example\n\nTo create a List you either call List.of or List.fromArray. List.of take a single value, and List.fromArray, well it takes an array.\n\nLet's say we want to fetch a list of joke from the previous example\n```js\nconst list = List.fromArray([42, 47, 77]);\nconst listOfTask = list.map(getJoke); // List\u003c[Task\u003c?\u003e, Task\u003c?\u003e, Task\u003c?\u003e]\u003e\n\n// hmm we have a list of Task, not too practical\n\nconst taskOfList = listOfTask.sequence(Task.of); // Task\u003cList[?, ?, ?]\u003e\u003e\n\n// Better, and nothing is executed yet.\n\ntaskOfList.then((listOfJoke) =\u003e {\n    listOfJoke.toArray(); // [\n    //    'CHUCK NORRIS DOESN'T CHURN BUTTER. HE ROUNDHOUSE KICKS THE COWS AND THE BUTTER COMES STRAIGHT OUT.',\n    //    'THERE IS NO THEORY OF EVOLUTION, JUST A LIST OF CREATURES CHUCK NORRIS ALLOWS TO LIVE.',\n    //    'CHUCK NORRIS CAN DIVIDE BY ZERO.',\n// ]\n}), console.log);\n```\n\nshorter example using traverse\n```js\nList.fromArray([42, 47, 77])\n    .traverse(getJoke, Task.of)\n    .then(listOfJoke =\u003e {\n        // do something with it\n    })\n\n```\n@TODO: Add codepen link to prove it works\n\n### List api\n\n- List takes an array of value and return a List\n\n```ts\n// List(values: Value[]): List\u003cValue\u003e\nList([1, 2, 3]);\n    -\u003e List\u003c[1, 2, 3]\u003e\n```\n\n- List.of: take a single value and return a List that will allow to operate on this value\n\n```ts\n// List.of(value: Value): List\u003cValue\u003e\nList.of(5);\n    -\u003e List\u003c[5]\u003e\n```\n\n- List.lift: Takes a function and wrap its return value in a List\n```ts\n// List.lift(fn: A =\u003e B): A =\u003e List\u003cB\u003e;\nList.lift(v =\u003e v * 2);\n    -\u003e v =\u003e List.of(v * 2);\n```\n\n- list.map: Takes a function and apply it to all the value in the list\n```ts\n// List\u003cA\u003e.map(fn: A =\u003e B): List\u003cB\u003e;\nList([1, 2, 3]).map(v =\u003e v * 2);\n    -\u003e List\u003c[2, 4, 6]\u003e\n```\n\n- list.flatten: If the values are other List merge the two together. concatening the values from each list.\n```ts\n// List\u003cList\u003cA\u003e\u003e.flatten(): List\u003cA\u003e;\nList([List([1, 2]), List([3, 4], List([5, 6]))]);\n    -\u003e List([1, 2, 3, 4, 5, 6]);\n```\n\n- list.chain\n```ts\n// List\u003cA\u003e.chain\u003cA, B\u003e(fn: A =\u003e List\u003cB\u003e): List\u003cB\u003e;\nList(['hello', 'world']).chain(v =\u003e List(v.split('')));\n    -\u003e List(['h', 'e', 'l', 'l', 'o', 'w', 'o', 'r', 'l', 'd'])\n```\n\n- list.toArray: convert the list into an array\n```ts\n// List\u003cA\u003e.toArray(): A[];\nList([1, 2, 3]).toArray();\n    -\u003e [1, 2, 3]\n```\n\n- list.concat: same as Array.concat\n```ts\n// List\u003cA\u003e.concat(otherList: List\u003cA\u003e): List\u003cA\u003e;\nList([1, 2, 3]).concat(4);\n    -\u003e List([1, 2, 3, 4])\n```\n\n- list.sequence: convert a list of applicative into an applicative of a list.\n```ts\n/*\n * List\u003cApplicative\u003cA\u003e\u003e.sequence(\n *      of: Value =\u003e Applicative\u003cValue\u003e\n * ): Applicative\u003cList\u003cB\u003e\u003e;\n * */\nList([Identity.of(1), Identity.of(2), Identity.of(3)]).sequence(Identity.of);\n    -\u003e Identity\u003cList\u003c1,2,3\u003e\u003e\n```\n\n- list.traverse: like list.sequence, but first take a function transforming the values into Applicative.\n```ts\n/*\n * List\u003cA\u003e.traverse(\n *     fn: A =\u003e Applicative\u003cB\u003e,\n *     of: Value =\u003e Applicative\u003cValue\u003e\n * ): Applicative\u003cList\u003cB\u003e\u003e;\n * */\nList([1, 2, 3]).traverse(v =\u003e Identity.of(v =\u003e v * 2));\n    -\u003e Identity\u003cList\u003c[2, 4, 6]\u003e\u003e\n```\n\n\n## Reader\n\nThe reader monad allow to share value between all composed function. Think of it like dependency injection.\nIt resemble a lot to IO, but instead of taking a side effect with no argument, it takes a function that take the dependencies as an argument.\n\n## Example\n\nLet's say that we want to retrieve the basket of a user from the browser storage, but we want to be able to change the storage at the time of execution.\n\n```js\nconst getUser = dependencies =\u003e dependencies.storage.getItem('user');\nconst getBasket = basketId =\u003e dependencies =\u003e dependencies.storage.getItem(basketId);\n\nconst basket = Reader\n    .ask() // ask create a reader that return its dependencies as its value\n    .map(getUser) // with map you access only the value\n    .map(user =\u003e user.basket)\n    // but with chain you can access the dependencies while initializing the Reader\n    .chain(value =\u003e Reader(dependencies =\u003e getBasket(basketId, dependencies))\n    // additionnaly you could have lifted getBasket and use the resulting function\n    .execute({ storage: window.localStorage }); // execute the Reader with the given dependencies\n```\n\nI did not handle null case for simplicity sake. Here is the same example, but using maybe too\n\n// TODO add codepen link\n\n### Reader api\n\n- Reader takes a function `(v: Dependencies) =\u003e Value` and returns a `Reader\u003cValue, Dependencies\u003e`\n\n```ts\n/*\n * Reader(\n *     fn: Dependencies =\u003e Value,\n * ): Reader\u003cValue, Dependencies\u003e\n * */\nconst reader = Reader(dependencies =\u003e dependencies * 2);\nreader.execute(5); // 10\nreader.execute(4); // 8\n```\n\n- Reader.of: take a single value and return a Reader that will allow to operate on this value\n\n```ts\n// Reader.of\u003cValue\u003e(value: Value): Reader\u003cValue, any\u003e;\nconst reader = Reader.of(5);\n    -\u003e Reader\u003c5\u003e\nreader.execute('ignored'); // 5\n```\n\n- Reader.ask: return a reader where the value is equal to the dependencies\n\n```ts\n// Reader.ask(): Reader\u003cDependencies, Dependencies\u003e;\nReader.ask().execute('gimme back'); // 'gimme back'\n```\n\n- Reader.lift: Takes a function returning a function and wrap its return value in a Reader\n```ts\n// Reader.lift(fn: A =\u003e Dependencies =\u003e B): A =\u003e Reader\u003cB, Dependencies\u003e;\nconst multiplyReader = Reader.lift(v =\u003e multiplicator =\u003e v * multiplicator);\n    -\u003e v =\u003e Reader(multiplicator =\u003e v * multiplicator);\n\nmultiplyReader(5).execute(2); // 10\n```\n\n- list.execute: execute the operation with the given dependencies and return the result\n\n```ts\n// Reader\u003cValue, Dependencies\u003e.execute(deps: Dependencies): Value\n```\n\n- reader.map: Takes a function and apply it to the reader value. FUnction has no access to the dependencies.\n```ts\n// Reader\u003cA, Dependencies\u003e.map(fn: A =\u003e B): Reader\u003cB, Dependencies\u003e;\nconst five = Reader.of(5).map(v =\u003e v * 2);\n    -\u003e Reader\u003c10\u003e\nfive.execute(5); // 10\n\nconst reader = Reader.ask().map(v =\u003e v * 2);\n    -\u003e Reader\u003c?\u003e\nreader.execute(5); // 10\nreader.execute(6); // 12\n```\n\n- reader.flatten: If the values are other Reader merge the two together.\n```ts\n// Reader\u003cReader\u003cA\u003e\u003e.flatten(): Reader\u003cA\u003e;\nReader.of(Reader.of(5)).flatten();\n    -\u003e Reader\u003c5\u003e\n```\n\n- reader.chain\n```ts\n// Reader\u003cA\u003e.chain(fn: A =\u003e Reader\u003cB\u003e): Reader\u003cB\u003e;\nReader.of(5).chain(v =\u003e Reader.of(v * 2));\n    -\u003e Reader\u003c10\u003e\n\nconst reader = Reader.of(5)\n    .chain(v =\u003e Reader(dependencies =\u003e dependencies(v)));\nreader.execute(v =\u003e v + 1); // 6\nreader.execute(v =\u003e v * 2); // 10\n\n```\n\n## Writer\n\nThe Writer Monad allows us to handle log while mapping function.\nThat is you can map function, while adding item to an array.\n\n## Example:\n\nLet's verify an user while adding error log for each bad property\n\n```js\nconst checkUserName = name =\u003e {\n    if (typeof name !== 'string') {\n        return ['name is required'];\n    }\n    if (name.length \u003c 5) {\n        return ['name must be at least 5 character long'];\n    }\n\n    return [];\n}\n\nconst checkUserPassword = password =\u003e {\n    if (typeof password !== 'string') {\n        return ['password is required'];\n    }\n    if (password.length \u003c 5) {\n        return ['password must be at least 5 character long'];\n    }\n    if (...) {\n        return ['password must contains at least one number'];\n    }\n    if (...) {\n        return ['password must contains at least one special character'];\n    }\n\n    return [];\n}\nconst verifyUser = user =\u003e Writer.of(user)\n    .chain(user =\u003e Writer(user, checkUserName(user.name)))\n    .chain(user =\u003e Writer(user, checkUserPassword(user.password)))\n    .getLog\n\nverifyUser({}); // ['name is required', 'password is required']\nverifyUser({ name: 'fred', password: 'xxx' }); // ['name must be at least 5 character long', 'password must be at least 5 character long']\n```\n\n### Writer api\n\n- Writer takes a Value and an optional array of log (the log can be of any type default to empty array)\n\n```ts\n// Writer(value: Value, logs?: []): Writer\u003cValue\u003e\nWriter(5);\n    -\u003e Writer\u003c5, []\u003e\nWriter(5, ['a five']);\n    -\u003e Writer\u003c5, ['a five']\u003e\n```\n\n- Writer.of: take a single value and return a Writer with empty logs\n\n```ts\n// Writer.of(value: Value): Writer\u003cValue\u003e\nWriter.of(5);\n    -\u003e Writer\u003c5, []\u003e\n```\n\n- Writer.lift: Takes a function and logs, and wrap the result of the function in a Writer with the given log.\n```ts\n/*\n * Writer.lift(\n *     fn: A =\u003e B,\n *     logs: Log[] = []\n * ): (v: A) =\u003e Writer\u003cB, Log\u003e;\n * */\n\nWriter.lift(v =\u003e v * 2, ['doubling the value']\u003e);\n    -\u003e v =\u003e Writer(v * 2, ['doubling the value']);\n```\n\n- writer.map: Takes a function and apply it to the writer value. The logs are untouched.\n```ts\n// Writer\u003cA\u003e.map(fn: A =\u003e B): Writer\u003cB\u003e;\nWriter(5, ['start with a five']).map(v =\u003e v * 2);\n    -\u003e Writer\u003c10, ['start with a five']\u003e\n```\n\n- writer.flatten: If the value is another Writer merge the two together. The logs of both writer will be concatened\n```ts\n// Writer\u003cReader\u003cA, Log\u003e, Log\u003e.flatten(): Reader\u003cA, Log\u003e;\nWriter(Writer(5, ['a five']), ['another writer']).flatten();\n    -\u003e Writer\u003c5, ['another writer', 'a five']\u003e\n```\n\n- writer.chain: Takes a function returning another writer and return a Writer with the value of the nested Writer, and the logs of both.\n```ts\n// Writer\u003cA\u003e.chain(fn: A =\u003e Writer\u003cB\u003e): Writer\u003cB\u003e;\nWriter.of(5, 'a five').chain(v =\u003e Writer(v * 2, 'multiply by 2'));\n    -\u003e Writer\u003c10, ['a five', 'multiply by 2']\u003e\n```\n\n- writer.read: return the contained logs and value;\n\n```ts\n// Reader\u003cValue, Log\u003e.read(): { value: Value, log: Log };\nReader(5, ['a five']).read();\n    -\u003e { value: 5, log: ['a five'] }\n```\n\n- writer.readValue return the value from the reader\n\n```ts\n// Reader\u003cValue, Log\u003e.read(): Value;\nReader(5, ['a five']).readValue();\n    -\u003e 5\n```\n\n- writer.readLog return the log from the reader\n\n```ts\n// Reader\u003cValue, Log\u003e.read(): Log;\nReader(5, ['a five']).readLog();\n    -\u003e ['a five']\n```\n\n## State\n\nThe State Monad allows us to realize computation on a value while maintaining a state on the side.\n\n### Example\n\nLet us handle a simple form, we want to be able to update the value, or to revert all the change.\n\n```js\nconst form = {\n    email: 'john@doe.com',\n    comment: 'no comment',\n};\n\nState.getState()\n    .map(form =\u003e ({\n        ...form,\n        comment: 'updated comment',\n    }))\n    .chain(currentForm =\u003e State(oldForm =\u003e ({ value: , state: oldForm })))\n    .evalState(form);\n\nstate.evalState(form);\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarmelab%2Flimonade","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarmelab%2Flimonade","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarmelab%2Flimonade/lists"}