{"id":13683432,"url":"https://github.com/cbowdon/TsMonad","last_synced_at":"2025-04-30T13:30:41.867Z","repository":{"id":20220381,"uuid":"23492087","full_name":"cbowdon/TsMonad","owner":"cbowdon","description":"Little monad library designed for TypeScript","archived":true,"fork":false,"pushed_at":"2023-12-21T05:41:46.000Z","size":267,"stargazers_count":390,"open_issues_count":5,"forks_count":36,"subscribers_count":10,"default_branch":"master","last_synced_at":"2025-03-09T16:37:55.136Z","etag":null,"topics":["fantasy-land","javascript","monad","monad-library","typescript"],"latest_commit_sha":null,"homepage":null,"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/cbowdon.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-MIT","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2014-08-30T13:18:18.000Z","updated_at":"2024-12-28T07:58:31.000Z","dependencies_parsed_at":"2024-01-12T00:27:10.566Z","dependency_job_id":"add753ec-24c0-451c-92e4-853fde4e4a5c","html_url":"https://github.com/cbowdon/TsMonad","commit_stats":{"total_commits":97,"total_committers":15,"mean_commits":6.466666666666667,"dds":"0.44329896907216493","last_synced_commit":"5bcdb395327b119419c16a0591b87d859c36154d"},"previous_names":[],"tags_count":13,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbowdon%2FTsMonad","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbowdon%2FTsMonad/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbowdon%2FTsMonad/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cbowdon%2FTsMonad/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cbowdon","download_url":"https://codeload.github.com/cbowdon/TsMonad/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251712833,"owners_count":21631447,"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":["fantasy-land","javascript","monad","monad-library","typescript"],"created_at":"2024-08-02T13:02:11.238Z","updated_at":"2025-04-30T13:30:41.527Z","avatar_url":"https://github.com/cbowdon.png","language":"TypeScript","readme":"# TsMonad\n* a simple and pragmatic monad library\n* designed for TypeScript\n* with the aim of limiting errors due to unhandled nulls\n\n## Status\nSorry folks, I don't have time to actively maintain this project. Glad it has been of help to some people and thanks everyone for your contributions!\n\nI'm not seeking maintainer to take over - of course feel free to fork if you'd like to continue developing TsMonad.\n\n## Description\nThis library provides implementations of the most useful monads outside of Haskell (subjectively, this is Maybe and Either). It also provides a strongly-typed emulation of pattern matching to help enforce program correctness.\n\nI won't presume to attempt a monad tutorial here. There are several online - I recommend Douglas Crockford's Monads \u0026 Gonads talk.\n\n## License\nMIT\n\n## Usage\nThis library will work with vanilla ES3 JavaScript with node or in the browser. However, it is far better with [TypeScript](http://www.typescriptlang.org).\n\nNode:\n\n    var TsMonad = require('tsmonad');\n\nBrowser:\n\n    \u003cscript src=\"node_modules/tsmonad/dist/tsmonad.js\"\u003e\u003c/script\u003e\n\nTypeScript definitions:\n\n    /// \u003creference path=\"node_modules/tsmonad/dist/tsmonad.d.ts\" /\u003e\n\n## Examples (in TypeScript)\nYou can see the unit tests for the examples below [online here](https://cbowdon.github.io/tests/TsMonad) and view the source in test/examples.ts.\n\n### Pattern matching emulation\n\n```ts\nvar turns_out_to_be_100 = Maybe.just(10)\n    .caseOf({\n        just: n =\u003e n * n,\n        nothing: () =\u003e -1\n    });\n\nvar turns_out_to_be_a_piano = Maybe.nothing\u003cnumber\u003e()\n    .caseOf({\n        just: n =\u003e n * n,\n        nothing: () =\u003e -1 // joke, it's negative one not a piano\n    });\n\nvar turns_out_to_throw_a_compiler_error = Maybe.just(321)\n    .caseOf({\n        just: n =\u003e 999,\n        // tsc will tell you that this \"does not implement 'nothing'\"\n        // helping to enforce correct handling of all possible paths\n    });\n```\n\n### General Maybe usage\n\nThe Maybe monad can simplify processing of values that may not exist:\n\n```ts\nvar canRideForFree = user.getAge()  // user might not have provided age, this is a Maybe\u003cnumber\u003e\n    .bind(age =\u003e getBusPass(age))   // not all ages have a bus pass, this is a Maybe\u003cBusPass\u003e\n    .caseOf({\n        just: busPass =\u003e busPass.isValidForRoute('Weston'),\n        nothing: () =\u003e false\n    });\n ```\n\nWithout Maybe, this would be something like:\n\n```ts\nvar canRideForFree,\n    age = user.getAge(); // might be null or undefined\n\nif (age) {\n    var busPass = getBusPass(age); // might be null or undefined\n    if (busPass) {\n        canRideForFree = busPass.isValidForRoute('Weston');\n    }\n}\ncanRideForFree = false;\n```\n\nPlease excuse the messy var scoping and implicit any types in the above. Again, the neat thing about the caseOf method is that it forces you to consider the failure case - it's not always obvious if you're missing a branch of your if-else statement, until it blows up at runtime.\n\nThere are some convenience methods in Maybe:\n\n```ts\nuser.getLikesCookies().defaulting(false); // Maybe\u003cfalse\u003e\nuser.getLikesCookies().valueOr(false); // false\nuser.getLikesCookies().valueOrCompute(() =\u003e expensiveCalculation());\nuser.getLikesCookies().valueOrThrow(new Error());\n\n// Maybe.just({ three: 3, hi: 'hi'})\nMaybe.sequence\u003cnumber|string\u003e({ three: Maybe.just(3), hi: Maybe.just('hi') });\n\n// Maybe.nothing\nMaybe.sequence\u003cnumber\u003e({ three: Maybe.just(3), hi: Maybe.nothing() });\n```\n\n### General Either usage\n\n```ts\nvar canRideForFree = user.getAge()  // either 42 or 'Information withheld' - type of Either\u003cstring,number\u003e\n    .bind(age =\u003e getBusPass(age))   // either busPass or 'Too young for a bus pass' - type of Either\u003cstring,BusPass\u003e\n    .caseOf({\n        right: busPass =\u003e busPass.isValidForRoute('Weston'),\n        left: errorMessage =\u003e { console.log(errorMessage); return false; }\n    });\n```\n\n### General Writer usage\n\nSomewhat contrived example of recording arithmetic operations:\n\n```ts\nvar is_true = Writer.writer(['Started with 0'], 0)\n    .bind(x =\u003e Writer.writer(['+ 8'], x + 8))\n    .bind(x =\u003e Writer.writer(['- 6', '* 8'], 8 * (x - 6)))\n    .caseOf({\n        writer: (s, v) =\u003e v === 16 \u0026\u0026 s.join(', ') === 'Started with 0, + 8, - 6, * 8'\n    }));\n```\n\n### The lift method (fmap)\n\nThe lift method takes a lambda, applies it to the wrapped value and calls the unit function of the monad on the result (e.g. for Maybe it calls just). Useful when you want to bind to a function that doesn't return a monad.\n\n```ts\nvar turns_out_to_be_true = Maybe.just(123)\n    .lift(n =\u003e n * 2)\n    .caseOf({\n        just: n =\u003e n === 246,\n        nothing: () =\u003e false\n    });\n```\n\nNote that for Maybe, if the lifted function returns null or undefined then it returns Nothing rather than wrapping a null in a Just, which is perverse.\n\n## FAQ and apologies\n#### Why only Maybe, Either and Writer (so far)?\n\nThese monads are the most obviously useful in JavaScript's world of unrestricted mutable state and side effects. I'm currently evaluating which other common monads offer enough benefit to be worth implementing in TypeScript.\n\n#### Where's monad transformers?\n\nSorry. One day. But for the moment it's not practicable to do this without support for higher-kinded types.\n\n#### Is it [Fantasy Land](https://github.com/fantasyland/fantasy-land) conformant?\n\nYes - there are aliases for Fantasy Land interfaces of Functor and Monad.\n\n![\"Fantasy land logo\"](https://raw.github.com/fantasyland/fantasy-land/master/logo.png)\n","funding_links":[],"categories":["TypeScript","Libraries"],"sub_categories":["[Javascript](https://developer.mozilla.org/en-US/docs/Web/JavaScript)"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcbowdon%2FTsMonad","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcbowdon%2FTsMonad","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcbowdon%2FTsMonad/lists"}