{"id":20676561,"url":"https://github.com/calebowens/monads","last_synced_at":"2026-02-12T15:02:52.856Z","repository":{"id":57714324,"uuid":"501774018","full_name":"calebowens/monads","owner":"calebowens","description":null,"archived":false,"fork":false,"pushed_at":"2022-06-11T08:49:55.000Z","size":143,"stargazers_count":3,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-12-25T04:48:50.973Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"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/calebowens.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":"2022-06-09T18:53:55.000Z","updated_at":"2022-08-04T08:36:43.000Z","dependencies_parsed_at":"2022-08-25T18:01:22.234Z","dependency_job_id":null,"html_url":"https://github.com/calebowens/monads","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/calebowens/monads","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calebowens%2Fmonads","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calebowens%2Fmonads/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calebowens%2Fmonads/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calebowens%2Fmonads/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/calebowens","download_url":"https://codeload.github.com/calebowens/monads/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calebowens%2Fmonads/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29369379,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-12T08:51:36.827Z","status":"ssl_error","status_checked_at":"2026-02-12T08:51:26.849Z","response_time":55,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"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":[],"created_at":"2024-11-16T21:12:59.507Z","updated_at":"2026-02-12T15:02:52.819Z","avatar_url":"https://github.com/calebowens.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Caleb's Monads\nError handling monadic patterns and abstract classes for monads\n\nAPI Docs: [HERE!](https://calebowens.github.io/monads/)\n\nGit Repo: [HERE!](https://github.com/calebowens/monads)\n\nNPM Listing: [HERE!](https://www.npmjs.com/package/monads-co)\n\n## Uhh, what is a monad? \n\nClearly, a monad is a monoid in the category of endofunctors...\n\nFor those of us who don't have a PhD in category theory, it will suffice to say that it's something that implements our `Monad\u003cT\u003e` class. In the real world, they provide a powerful interface for mapping and manipulating data.\n\nYou probably already work with a monad in your day-to-day life with JavaScript's Promises, they encapsulate a value and let you manipulate it with a \"mapping\" function, namely `.then`.\n\nThe JS Promise could be considered not a \"true\" monad, but the analogy will suffice for basing your understanding off of.\n\nI think examples are a very powerful tool for coming to understand the behaviour of an abstraction, so I hope the following examples can shed some light for you.\n\n## Examples\n\nThe most simple example is the `Optional\u003cT\u003e` type in this library which enables a function to either return a value, or not. This is not a particularly useful abstraction in JS/TS when you can return `T | undefined` as the type, but is fantastic for illustrative purposes.\n\n### Wrapping a function\n\nFor my example function I'm going to use document.querySelector() as it will return `Element | null` and we would like to deal only with the `Element` it returns.\n\n```ts\nimport { Optional } from './Optional'\n\nconst safeQuerySelect = Optional.wrap(document.querySelector)\n```\n\nThis sets the `safeQuerySelect` constant, to a function that takes in the same arguments as `document.querySelector()`, but instead returns `Optional\u003cElement\u003e`.\n\n### Doing something with the function's return\n\nI think it's not too unlikely a scenario that you would want to get an input element and want to get it's held value. So this is what the current snippet does, while also providing a default value.\n\n```ts\nconst content = safeQuerySelect('#my-input')\n  .map((element: HTMLInputElement) =\u003e element.value)\n  .or('default input')\n```\n\n- `safeQuerySelect('#my-input')` This returns Optional\\\u003cElement\\\u003e.\n- `.map((element: HTMLInputElement) =\u003e element.value)` If there is in fact an element that has been found, it preforms the function `(element: HTMLInputElement) =\u003e element.value` on it. This returns now `Optional\u003cstring\u003e`.\n- `.or('default input')` This method lets you provide a default value, if it happens to be that no element was found.\n\nSo what is the final value of `content`? Well, if an element is found, it will return its value, otherwise it will set `content` to the string `\"default value\"`.\n\n### Making the example safer\n\nSome of you might have noticed that I cast `element` to `HTMLInputElement` which is an \"unsafe\" operation as in reality, if someone made a html div element, and gave it the tag `#my-input`, it would not have a .value property.\n\nWhat we would really like to do is to have our map return `Optional\u003cstring\u003e` so our default could be used if there is no `.value` property. We've actually got the `.then()` function which lets us return an Optional\u003cstring\u003e and it flattens out so we don't end up with an `Optional\u003cOptional\u003cstring\u003e\u003e` like you would expect with `.map()`, rather you're left with just `Optional\u003cstring\u003e`.\n\n```ts\nconst content = safeQuerySelect('#my-input')\n  .then((element) =\u003e Maybe\u003cstring\u003e(element.getAttribute('value')))\n  .or('default input')\n```\n\n- `safeQuerySelect('#my-input')` Returns `Optional\u003cElement\u003e`\n- `.then((element) =\u003e Maybe\u003cstring\u003e(element.getAttribute(value)))`\n  - `element.getAttribute(\"value\")` Fetches value attribute returning `string | null`.\n  - `Maybe\u003cstring\u003e(...)` Converts the `string | null` to `Optional\u003cstring\u003e`.\n  - `.then((element) =\u003e ...)` performs the mapping, but returns only `Optional\u003cstring\u003e` similar to a flatMap.\n- `.or('default input')` Then will return either our element's value, or the default string.\n\n### Error handling\n\nIts basically the same as Rust's... TODO: Write docs form Result\\\u003cValueT, ErrorT\\\u003e","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcalebowens%2Fmonads","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcalebowens%2Fmonads","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcalebowens%2Fmonads/lists"}