{"id":28181574,"url":"https://github.com/bioball/mandolin","last_synced_at":"2025-05-16T03:13:16.884Z","repository":{"id":58234867,"uuid":"43732514","full_name":"bioball/mandolin","owner":"bioball","description":"Painlessly enhance your JavaScript with monadic types","archived":false,"fork":false,"pushed_at":"2015-10-22T06:39:53.000Z","size":272,"stargazers_count":14,"open_issues_count":7,"forks_count":1,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-20T02:11:38.365Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","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/bioball.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":"2015-10-06T05:55:04.000Z","updated_at":"2024-11-14T03:13:13.000Z","dependencies_parsed_at":"2022-08-31T09:42:21.776Z","dependency_job_id":null,"html_url":"https://github.com/bioball/mandolin","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/bioball%2Fmandolin","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bioball%2Fmandolin/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bioball%2Fmandolin/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bioball%2Fmandolin/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bioball","download_url":"https://codeload.github.com/bioball/mandolin/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254459049,"owners_count":22074606,"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":[],"created_at":"2025-05-16T03:13:16.412Z","updated_at":"2025-05-16T03:13:16.879Z","avatar_url":"https://github.com/bioball.png","language":"JavaScript","readme":"# Mandolin\n\n[![Build Status](https://travis-ci.org/bioball/mandolin.svg)](https://travis-ci.org/bioball/mandolin)\n\nPainlessly enhance your JavaScript with monadic types.\n\n```js\nconst definition = m.define({\n  firstName: m.string,\n  lastName: Option.as(m.string),\n  email: Option.as(m.string),\n  phoneNumber: Either.as(m.string, m.number)\n});\n\ndefinition.parse({\n  firstName: \"Bob\",\n  lastName: \"McAdoo\",\n  email: null,\n  phoneNumber: \"444-444-4444\"\n})\n// Right({ \n//   firstName: \"Bob\",\n//   lastName: Some(\"McAdoo\"),\n//   email: None(),\n//   phoneNumber: Left(\"444-444-4444\")\n// })\n```\n\n## What is a monad?\n\nIn brief, a Monad is a wrapper around a value that allows you to make safe, composable operations. It eliminates the need to throw errors, as well as the need for things like `null` values. A JavaScript `Array` is a monad-like data type, but doesn't fully satisfy the rules of being a monad, given that it doesn't have a `bind` (or `flatMap`) function.\n\nA monad is a type that satisfies the following methods:\n* A composition function, called `\u003e\u003e=`, `bind`, or `flatMap`, that has type `(A) =\u003e M\u003cB\u003e`, and returns type `M\u003cB\u003e`.\n* A `return` (or `unit`), that has type `(A) =\u003e M\u003cA\u003e`.\n\nMonads are common in functional programming languages.\n\nHere are some great resources that discuss monads:\n\n* [Brian Beckman: Don't Fear The Monad](https://www.youtube.com/watch?v=ZhuHCtR3xq8)\n* [James Coglan: Promises are the monad of asynchronous programming](https://blog.jcoglan.com/2011/03/11/promises-are-the-monad-of-asynchronous-programming/)\n* [Douglas Crockford: Monads and Gonads](https://www.youtube.com/watch?v=b0EF0VTs9Dc)\n\nIn this library, the monadic `bind` is called `flatMap`, in order to not conflict with `Function.prototype.bind`. The monadic `return` is called `unit`, to not cause any confusion with the `return` keyword.\n\n## Installation\n\n```bash\nnpm install mandolin\nbower install mandolin\n```\n\nCurrently, you must use a CommonJS loader to `require` this module.\n\n```js\nconst m = require('mandolin');\nconst { Option, Some, None } = m;\n```\n\n## Available monads\n\n* [Option](#option)\n* [Either](#either)\n\n### Option\n\nAn `Option` is a type comprising of `Some` and `None`. A value of type `Option` can either be a `Some`, in which it holds a value, or a `None`, in which it holds no value. This is used in lieu of `null` and `undefined`.\n\nExample:\n\n```js\nconst bobsEmail = new Some(\"bob@mcadoo.com\");\n// We have Bob's email\nconst sandrasEmail = new None();\n// we do not have Sandra's email\n```\n\nThe way to get a reference to the actual value is through either `map`, `flatMap` or `match`.\n\n```js\nbobsEmail.map((email) =\u003e doThingWithEmail(email));\n```\n\nThis is similar to writing a null check:\n\n```js\nif (email !== null || email !== undefined) {\n  doThingWithEmail(email)\n}\n```\n\n### Either\n\nA disjoint union of `Left` and `Right`, and is right-biased. `map` and `flatMap` are only called if it is a `Right`. This is similar to an `Option`, in that `Left : None :: Right : Some`. The difference is that a `Left` can also hold values.\n\n## Reads\n\nMandolin uses `Reads` objects, which are not monads, but whose purpose are to serialize values into algebraic types (`Option`, `Either`).\n\nA `Reads` is created with this signature:\n\n```js\nconst r = new Reads(reader);\n```\n\nWhere `reader` is a function that accepts a value, and returns an `Either`; a `Right` with a successful value, or a `Left` with an unsuccessful value. For example, a `reader` of even numbers would look like this:\n\n```js\nconst reader = (v) =\u003e v % 2 ? new Left(\"number is not even\") : new Right(v);\n```\n\nYou can think of a `Reads` as a rule for serializing something. `Reads` can be freely chained together, to further define a rule.\n\n```js\nconst greaterThan10 = new Reads((v) =\u003e v \u003c 10 ? new Left(\"number is less than 10\") : new Right(v));\nconst evenAndGreaterThan10 = r.with(greaterThan10);\n\nevenAndGreaterThan10.getValue(4) // Left(\"number is not event\")\nevenAndGreaterThan10.getValue(14) // Right(14)\n```\n\nReads can be chained via `with`, `map` and `flatMap`.\n\n\n## Parser\n\nAlong with `Reads`, Mandolin has `Parser`s that accept arbitrary objects of key --\u003e `Reads` pairs, and returns an either with a successful serialized object, or an error.\n\nThis provides a generic way to validate objects, and coerce values into algebraic data types.\n\nYou may create a definition with this signature:\n\n```js\nconst definition = m.define({\n  age: m.number,\n  name: m.string\n});\n```\n\nIn this example, `m.number` and `m.string` are pre-baked `Reads` for primitives. The following are all available:\n\n* `m.number`\n* `m.string`\n* `m.boolean`\n* `m.array`\n* `m.object`\n* `m.undefined`\n* `m.null`\n* `m.any`\n\nThe return value of `m.define` is a `Parser`, which is a special type of `Reads`. Thus, nested objects are simply notated as such:\n\n```js\nconst definition = m.define({\n  age: m.number,\n  name: m.string,\n  address: m.define({\n    street1: m.string\n  })\n});\n```\n\n`Option` and `Either` both have a generic way to create `Reads`.\n\n```js\nconst definition = m.define({\n  meta: Option.reads,\n  email: Option.as(m.string)\n});\n```\n\n`Option.reads` will return either a `None()`, or a `Some(\u003cAny\u003e)`. `Option.as()` accepts a `Reads` argument, to perform further validation after casting away `null` or `undefined`.\n\nOnce a definition has been instantiated, calling `parse` will return an `Either`, with the `Right` holding the successfully parsed value, and the `Left` holding the first error encountered while parsing.\n\n```js\ndefinition.parse({\n  meta: \"foo\",\n  email: null\n})\n// returns Right({\n//   meta: Some(\"foo\"),\n//   email: None()\n// });\n```\n\n## Types\n\nThis library makes no assumptions about type safety. The approach, rather, is to use Reads combinators to serialize values that follow certain sets of rules. For example, in Scala, an `Option` of a `String` is notated as such:\n\n```scala\nOption[String]\n```\n\nThe seemingly equivalent example in our library is this:\n\n```js\nOption.as(m.string)\n```\n\nThe difference is, `Option.as(m.String)` is not a type, but a rule for reading in values. I can just as easily write another rule for `Option.as(evenNumber)`. Essentially, this is the same thing as types, but I think makes more sense in a world that doesn't perform any compile-time type checking.\n\n## Pattern Matching\n\nJavaScript doesn't have pattern matching built into the language. However, each algebraic data type in this library comes with a `match` function that behaves like pattern matching.\n\n```js\nnew Left(\"foo\").match({\n  Left (str) { ... },\n  Right (str) { ... }\n});\n```\n\nThe return value of `match` is the return value of whichever function ends up being called. This is analagous to Scala's `match`.\n\n```scala\nfoo match {\n  case Left(_) =\u003e ???\n  case Right(_) =\u003e ???\n}\n```\n\n\n## Fantasy-land\n\nThis module is [fantasy-land](https://github.com/fantasyland/fantasy-land) compliant. `chain` is an alias of `flatMap`, and `of` is an alias of `unit`.","funding_links":[],"categories":["Libraries"],"sub_categories":["[Javascript](https://developer.mozilla.org/en-US/docs/Web/JavaScript)"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbioball%2Fmandolin","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbioball%2Fmandolin","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbioball%2Fmandolin/lists"}