{"id":20819565,"url":"https://github.com/calmm-js/partial.lenses.validation","last_synced_at":"2025-05-07T15:23:11.912Z","repository":{"id":57319730,"uuid":"107013422","full_name":"calmm-js/partial.lenses.validation","owner":"calmm-js","description":"Partial Lenses Validation is a JavaScript library for validating and transforming data","archived":false,"fork":false,"pushed_at":"2019-01-05T09:15:23.000Z","size":475,"stargazers_count":41,"open_issues_count":0,"forks_count":4,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-04-14T02:11:27.451Z","etag":null,"topics":["asynchronous","contracts","json","structural","transform","validation"],"latest_commit_sha":null,"homepage":"","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/calmm-js.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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":"2017-10-15T13:13:50.000Z","updated_at":"2024-12-06T07:41:42.000Z","dependencies_parsed_at":"2022-08-25T22:42:33.415Z","dependency_job_id":null,"html_url":"https://github.com/calmm-js/partial.lenses.validation","commit_stats":null,"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calmm-js%2Fpartial.lenses.validation","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calmm-js%2Fpartial.lenses.validation/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calmm-js%2Fpartial.lenses.validation/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/calmm-js%2Fpartial.lenses.validation/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/calmm-js","download_url":"https://codeload.github.com/calmm-js/partial.lenses.validation/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252903156,"owners_count":21822381,"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":["asynchronous","contracts","json","structural","transform","validation"],"created_at":"2024-11-17T22:06:48.270Z","updated_at":"2025-05-07T15:23:11.889Z","avatar_url":"https://github.com/calmm-js.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# \u003ca id=\"partial-lenses-validation\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#) [Partial Lenses Validation](#partial-lenses-validation) \u0026middot; [![Gitter](https://img.shields.io/gitter/room/calmm-js/chat.js.svg)](https://gitter.im/calmm-js/chat) [![GitHub stars](https://img.shields.io/github/stars/calmm-js/partial.lenses.validation.svg?style=social)](https://github.com/calmm-js/partial.lenses.validation) [![npm](https://img.shields.io/npm/dm/partial.lenses.validation.svg)](https://www.npmjs.com/package/partial.lenses.validation)\n\nThis is a library of validation\n[transform](https://github.com/calmm-js/partial.lenses/#transforms)\n[combinators](https://wiki.haskell.org/Combinator).  The main idea is to produce\nvalidation errors in the same shape as the data structure being validated.  This\nway validation errors can be accessed at the same path as the data and can be\nmechanically associated with the corresponding elements of the validated data\nstructure.\n\nNote that the [▶\nlinks](https://calmm-js.github.io/partial.lenses.validation/index.html#) take\nyou to a live version of this page and that there is a\n[playground](https://calmm-js.github.io/partial.lenses.validation/playground.html#GoOghgxhCmAOAuBnAFAKAEogJYoMrwCcsA7AcwEoAaVAcgHUj5oACCAewBMWALaA6AIQ1U5IA)\nfor sharing examples.\n\n[![npm version](https://badge.fury.io/js/partial.lenses.validation.svg)](http://badge.fury.io/js/partial.lenses.validation)\n[![Bower version](https://badge.fury.io/bo/partial.lenses.validation.svg)](https://badge.fury.io/bo/partial.lenses.validation)\n[![Build Status](https://travis-ci.org/calmm-js/partial.lenses.validation.svg?branch=master)](https://travis-ci.org/calmm-js/partial.lenses.validation)\n[![Code Coverage](https://img.shields.io/codecov/c/github/calmm-js/partial.lenses.validation/master.svg)](https://codecov.io/github/calmm-js/partial.lenses.validation?branch=master)\n[![](https://david-dm.org/calmm-js/partial.lenses.validation.svg)](https://david-dm.org/calmm-js/partial.lenses.validation)\n[![](https://david-dm.org/calmm-js/partial.lenses.validation/dev-status.svg)](https://david-dm.org/calmm-js/partial.lenses.validation?type=dev)\n\n## \u003ca id=\"contents\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#contents) [Contents](#contents)\n\n* [Examples](#examples)\n  * [Event table UI](#event-table-ui)\n  * [Library contracts](#library-contracts)\n* [Reference](#reference)\n  * [Elimination](#elimination)\n    * [Synchronous](#synchronous)\n      * [`V.accepts(rule, data) ~\u003e boolean`](#V-accepts) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n      * [`V.errors(rule, data) ~\u003e errors | undefined`](#V-errors) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n      * [`V.validate(rule, data) ~{throws}~\u003e data`](#V-validate) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * [Asynchronous](#asynchronous)\n      * [`V.acceptsAsync(rule, data) ~\u003e promise(boolean)`](#V-acceptsAsync) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n      * [`V.errorsAsync(rule, data) ~\u003e promise(errors | undefined)`](#V-errorsAsync) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n      * [`V.tryValidateAsyncNow(rule, data) ~{throws}~\u003e data | promise(data)`](#V-tryValidateAsyncNow) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n      * [`V.validateAsync(rule, data) ~\u003e promise(data)`](#V-validateAsync) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * [General](#general)\n      * [`V.run({Monad, onAccept: data =\u003e any, onReject: error =\u003e any}, rule, data) ~\u003e any`](#V-run) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n  * [Primitive](#primitive)\n    * [`V.accept ~\u003e rule`](#V-accept) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * [`V.acceptAs(value) ~\u003e rule`](#V-acceptAs) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * [`V.acceptWith(async (value, index) =\u003e value) ~\u003e rule`](#V-acceptWith) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * [`V.reject ~\u003e rule`](#V-reject) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * [`V.rejectAs(error) ~\u003e rule`](#V-rejectAs) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * [`V.rejectWith(async (value, index) =\u003e error) ~\u003e rule`](#V-rejectWith) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * [`V.remove ~\u003e rule`](#V-remove) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n  * [Predicates](#predicates)\n    * [`V.where(async (value, index) =\u003e testable) ~\u003e rule`](#V-where) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n  * [Elaboration](#elaboration)\n    * [`V.modifyError(async (value, error, index) =\u003e error, rule) ~\u003e rule`](#V-modifyError) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * [`V.setError(error, rule) ~\u003e rule`](#V-setError) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n  * [Logical](#logical)\n    * [`V.and(...rules) ~\u003e rule`](#V-and) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * [`V.both(rule, rule) ~\u003e rule`](#V-both) \u003csmall\u003e\u003csup\u003ev0.3.3\u003c/sup\u003e\u003c/small\u003e\n    * [`V.either(rule, rule) ~\u003e rule`](#V-either) \u003csmall\u003e\u003csup\u003ev0.3.3\u003c/sup\u003e\u003c/small\u003e\n    * [`V.not(rule) ~\u003e rule`](#V-not) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * [`V.or(...rules) ~\u003e rule`](#V-or) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n  * [Arrays](#arrays)\n    * [Uniform](#uniform)\n      * [`V.arrayId(rule) ~\u003e rule`](#V-arrayId) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n      * [`V.arrayIx(rule) ~\u003e rule`](#V-arrayIx) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * [Varying](#varying)\n      * [`V.args(...rules) ~\u003e rule`](#V-args) \u003csmall\u003e\u003csup\u003ev0.3.1\u003c/sup\u003e\u003c/small\u003e\n      * [`V.tuple(...rules) ~\u003e rule`](#V-tuple) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n  * [Functions](#functions)\n    * [`V.dependentFn(rule, (...args) =\u003e rule) ~\u003e rule`](#V-dependentFn) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * [`V.freeFn(rule, rule) ~\u003e rule`](#V-freeFn) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n  * [Objects](#objects)\n    * [`V.keep('prop', rule) ~\u003e rule`](#V-keep) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * [`V.optional(rule) ~\u003e rule`](#V-optional) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * [`V.props({...prop: rule}) ~\u003e rule`](#V-props) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * [`V.propsOr(rule, {...prop: rule}) ~\u003e rule`](#V-propsOr) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n  * [Conditional](#conditional)\n    * \u003ca href=\"#V-cases\"\u003e\u003ccode\u003eV.cases(...[(value, index) =\u0026gt; testable, rule][, [rule]]) ~\u0026gt; rule\u003c/code\u003e\u003c/a\u003e \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n    * \u003ca href=\"#V-casesOf\"\u003e\u003ccode\u003eV.casesOf(traversal, ...[(value, index) =\u0026gt; testable, rule][, [rule]]) ~\u0026gt; rule\u003c/code\u003e\u003c/a\u003e \u003csmall\u003e\u003csup\u003ev0.3.4\u003c/sup\u003e\u003c/small\u003e\n    * [`V.ifElse((value, index) =\u003e testable, rule, rule) ~\u003e rule`](#V-ifElse) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n  * [Dependent](#dependent)\n    * [`V.choose((value, index) =\u003e rule) ~\u003e rule`](#V-choose) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n  * [Recursive](#recursive)\n    * [`V.lazy(rule =\u003e rule) ~\u003e rule`](#V-lazy) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n  * [Transformation](#transformation)\n    * [Ad-hoc](#ad-hoc)\n      * [`V.modifyAfter(rule, async (value, index) =\u003e value) ~\u003e rule`](#V-modifyAfter) \u003csmall\u003e\u003csup\u003ev0.3.3\u003c/sup\u003e\u003c/small\u003e\n      * [`V.setAfter(rule, value) ~\u003e rule`](#V-setAfter) \u003csmall\u003e\u003csup\u003ev0.3.3\u003c/sup\u003e\u003c/small\u003e\n      * [`V.removeAfter(rule) ~\u003e rule`](#V-removeAfter) \u003csmall\u003e\u003csup\u003ev0.3.3\u003c/sup\u003e\u003c/small\u003e\n    * [Promotion](#promotion)\n      * \u003ca href=\"#V-promote\"\u003e\u003ccode\u003eV.promote(...[rule[, (value, index) =\u0026gt; value]]) ~\u0026gt; rule\u003c/code\u003e\u003c/a\u003e \u003csmall\u003e\u003csup\u003ev0.3.6\u003c/sup\u003e\u003c/small\u003e\n      * \u003ca href=\"#V-upgrades\"\u003e\u003ccode\u003eV.upgrades(...[(value, index) =\u0026gt; testable, rule[, async (value, index) =\u0026gt; value]]) ~\u0026gt; rule\u003c/code\u003e\u003c/a\u003e \u003csmall\u003e\u003csup\u003ev0.3.6\u003c/sup\u003e\u003c/small\u003e\n      * \u003ca href=\"#V-upgradesOf\"\u003e\u003ccode\u003eV.upgradesOf(traversal, ...[(value, index) =\u0026gt; testable, rule[, async (value, index) =\u003e value]]) ~\u0026gt; rule\u003c/code\u003e\u003c/a\u003e \u003csmall\u003e\u003csup\u003ev0.3.6\u003c/sup\u003e\u003c/small\u003e\n* [Tips](#tips)\n  * [Prefer case analysis to logical OR](#prefer-case-analysis-to-logical-or)\n  * [Prefer rule templates to logical AND](#prefer-rule-templates-to-logical-and)\n* [Known caveats](#known-caveats)\n* [Related work](#related-work)\n\n## \u003ca id=\"examples\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#examples) [Examples](#examples)\n\nThe following sections briefly describe some examples based on actual use cases\nof this library.\n\n### \u003ca id=\"event-table-ui\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#event-table-ui) [Event table UI](#event-table-ui)\n\nImagine a UI \u0026mdash; or take a look at this [live\nexample](https://codesandbox.io/s/x20w218owo) \u0026mdash; for editing a data\nstructure that is an array (table) of objects (records) that have a `date` field\nand an `event` field:\n\n```json\n[\n  {\"date\": \"2017-09-11\", \"event\": \"EFSA-H\"},\n  {\"date\": \"2017-09-20\", \"event\": \"EFSA-T\"},\n  {\"date\": \"\",           \"event\": \"EFSA-T\"}\n]\n```\n\nWe need to validate that each object has a valid date and an event and that\ndates and events are unique.  Furthermore, we wish to give feedback on all\nelements with errors so as to guide the user.\n\nHere is a sample set of rules\n\n```js\nconst rules = V.choose(events =\u003e V.arrayIx(V.props({\n  date: V.and(\n    [isNonEmpty,                  'required'],\n    [isValidDate,                 'yyyy-mm-dd'],\n    [isUniqueBy('date', events),  'duplicate']),\n  event: V.and(\n    [isNonEmpty,                  'required'],\n    [isUniqueBy('event', events), 'duplicate'])\n})))\n```\n\nwhere\n\n```js\nconst isNonEmpty = R.identity\n\nfunction isUniqueBy(p, xs) {\n  const counts = L.counts([L.elems, p], xs)\n  return x =\u003e counts.get(x) \u003c= 1\n}\n\nconst isValidDate = R.test(/^\\d{4}-\\d{2}-\\d{2}$/)\n```\n\nto give such validation feedback.  The rules basically just follow the structure\nof the data.\n\nValidating with those rules we get a data structure with the potential error\nfeedback at the same location as the offending element:\n\n```js\nV.errors(rules, [\n  {\"date\": \"2017-09-11\", \"event\": \"EFSA-H\"},\n  {\"date\": \"2017-09-20\", \"event\": \"EFSA-T\"},\n  {\"date\": \"\",           \"event\": \"EFSA-T\"}\n])\n// [ null,\n//   { event: 'duplicate' },\n//   { date: 'required', event: 'duplicate' } ]\n```\n\nThe result tells us that the first object is valid (i.e. there are no validation\nerrors in it).  The `event` in the second object is a duplicate.  The third\nobject is missing a date and the event is a duplicate.\n\n### \u003ca id=\"library-contracts\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#library-contracts) [Library contracts](#library-contracts)\n\nThe interface file of this library,\n[partial.lenses.validation.js](./src/partial.lenses.validation.js), uses the\nlibrary itself to specify the contracts for the exports.\n\nAssuming `process.env.NODE_ENV` is not `\"production\"` and you pass invalid\narguments to a function of this library, you will likely get an error message.\nFor example,\n\n```js\nV.validate(\n  V.casesOf(\n    'type',\n    [\n      R.identical('number'),\n      V.props({\n        type: R.is(String),\n        value: R.isNumber\n      })\n    ],\n    [\n      R.identical('boolean'),\n      V.props({\n        type: R.is(String),\n        value: R.is(Boolean)\n      })\n    ],\n  ),\n  {\n    type: 'boolean',\n    value: false\n  }\n)\n// Error: {\n//   \"errors\": [\n//     \"partial.lenses.validation: `props` given invalid arguments\",\n//     [\n//       {\n//         \"value\": null\n//       }\n//     ]\n//   ]\n// }\n```\n\nthrows an error, because `R.isNumber` is not defined.  The error is thrown as\nsoon as the call to [`V.props`](#V-props) is made.\n\nExamples of other libraries using Partial Lenses Validation for contract\nchecking:\n\n* [Prettier Printer](https://github.com/polytypic/prettier-printer/blob/master/src/prettier-printer.js)\n* [Partial Lenses History](https://github.com/calmm-js/partial.lenses.history/blob/master/src/partial.lenses.history.js)\n\n## \u003ca id=\"reference\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#reference) [Reference](#reference)\n\nThe [combinators](https://wiki.haskell.org/Combinator) provided by this library\nare available as named imports.  Typically one just imports the library as:\n\n```jsx\nimport * as V from 'partial.lenses.validation'\n```\n\nThis library is actually built on top of [Partial\nLenses](https://github.com/calmm-js/partial.lenses/)\n[transforms](https://github.com/calmm-js/partial.lenses#transforms).  It is also\ntypical to use e.g. [Ramda](http://ramdajs.com/), bound as `R` in examples, to\nimplement predicates.\n\n### \u003ca id=\"elimination\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#elimination) [Elimination](#elimination)\n\nTo use a validation rule one runs it using one of the elimination functions.\n\n#### \u003ca id=\"synchronous\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#synchronous) [Synchronous](#synchronous)\n\nIn case a validation rule is fully synchronous, it is better to use a\nsynchronous elimination function, because synchronous validation is faster than\nasynchronous validation.\n\n##### \u003ca id=\"V-accepts\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-accepts) [`V.accepts(rule, data) ~\u003e boolean`](#V-accepts) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.accepts(rule, data)` runs the given validation rule on the given data and\nsimply returns `true` in case the data is accepted and `false` if not.\n\nFor example:\n\n```js\nV.accepts(V.arrayIx(R.is(String)), ['Yes', 'No'])\n// true\n```\n\n##### \u003ca id=\"V-errors\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-errors) [`V.errors(rule, data) ~\u003e errors | undefined`](#V-errors) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.errors(rule, data)` runs the given validation rule on the given data.  In\ncase the data is accepted by the rule, the result is `undefined`.  Otherwise the\nresult is an object structure in the shape of the data structure containing the\nvalidation errors.\n\nFor example:\n\n```js\nV.errors(\n  V.props({\n    no: R.is(Number),\n    yes: R.is(String)\n  }),\n  {\n    yes: 101,\n  }\n)\n// { no: null, yes: 101 }\n```\n\nNote that in case a validation error would be `undefined`, a `null` is reported\ninstead.\n\n##### \u003ca id=\"V-validate\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-validate) [`V.validate(rule, data) ~{throws}~\u003e data`](#V-validate) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.validate(rule, data)` runs the given validation rule on the given input data.\nIn case the data is accepted, the validated output data is returned.  In case\nthe data is rejected, an [`Error`\nobject](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error)\nis thrown whose message is the stringified validation error and that has an\nextra `errors` property that has the (non-stringified) validation errors.\n\nFor example:\n\n```js\nV.validate(\n  V.props({\n    missing: R.is(String)\n  }),\n  {\n    unexpected: 'field'\n  }\n)\n// Error: {\n//   \"missing\": null\n//   \"unexpected\": \"field\"\n// }\n```\n\n#### \u003ca id=\"asynchronous\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#asynchronous) [Asynchronous](#asynchronous)\n\nIn case a validation rule contains asynchronous parts, it is necessary to use\none of the asynchronous elimination functions.  Functions that are *allowed, but\nnot required*, to be asynchronous are indicated in the documentation using the\n`async` keyword.\n\nThe below `ghInfoOfAsync` function is a simple asynchronous function that tries\nto use the [public GitHub search\nAPI](https://developer.github.com/v3/search/#search-repositories) to search for\ninformation on a GitHub project of specified name:\n\n```js\nasync function ghInfoOfAsync(name) {\n  const q = encodeURIComponent(name)\n  const res = await fetch(`https://api.github.com/search/repositories?q=${q}`)\n  const body = await res.json()\n  return L.get(['items', L.find(R.whereEq({name}))], body)\n}\n```\n\n##### \u003ca id=\"V-acceptsAsync\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-acceptsAsync) [`V.acceptsAsync(rule, data) ~\u003e promise(boolean)`](#V-acceptsAsync) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.acceptsAsync(rule, data)` runs the given validation rule on the given data\nlike [`V.accepts`](#V-accepts) except that the validation rule is allowed to\ncontain asynchronous validation [predicates](#V-where) and\n[transformations](#V-acceptWith).  The result will always be returned as a\n[promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).\n\n##### \u003ca id=\"V-errorsAsync\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-errorsAsync) [`V.errorsAsync(rule, data) ~\u003e promise(errors | undefined)`](#V-errorsAsync) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.errorsAsync(rule, data)` runs the given validation rule on the given data\nlike [`V.errors`](#V-errors) except that the validation rule is allowed to\ncontain asynchronous validation [predicates](#V-where) and\n[transformations](#V-acceptWith).  The result will always be returned as a\n[promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).\n\nFor example:\n\n```js\nV.errorsAsync(\n  V.arrayId(\n    R.pipeP(ghInfoOfAsync, L.get('stargazers_count'), R.lte(100))\n  ),\n  [\n    'partial.lenses',\n    'partial.lenses.validation'\n  ]\n).catch(R.identity).then(console.log)\n// [ 'partial.lenses.validation' ]\n```\n\n##### \u003ca id=\"V-tryValidateAsyncNow\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-tryValidateAsyncNow) [`V.tryValidateAsyncNow(rule, data) ~{throws}~\u003e data | promise(data)`](#V-tryValidateAsyncNow) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.tryValidateAsyncNow(rule, data)` runs the given validation rule on the given\ndata like [`V.validateAsync`](#V-validateAsync) except that in case the\nvalidation result is synchronously available it is returned or thrown\nimmediately as is without wrapping it inside a\n[promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).\nIn case the result is not available synchronously, a promise is returned.\n\n`V.tryValidateAsyncNow` can be used for wrapping asynchronous functions, for\nexample, because the first stage of validating a function is always synchronous.\n\nFor example:\n\n```js\nconst ghInfoOfAsyncChecked = V.tryValidateAsyncNow(\n  V.dependentFn(\n    V.args(R.and(R.is(String), V.not(R.isEmpty))),\n    name =\u003e V.optional(\n      V.propsOr(V.accept, {\n        name: R.identical(name),\n        stargazers_count: R.is(Number)\n        // ...\n      })\n    )\n  ),\n  ghInfoOfAsync\n)\n```\n\n##### \u003ca id=\"V-validateAsync\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-validateAsync) [`V.validateAsync(rule, data) ~\u003e promise(data)`](#V-validateAsync) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.validateAsync(rule, data)` runs the given validation rule on the given data\nlike [`V.validate`](#V-validate) except that the validation rule is allowed to\ncontain asynchronous validation [predicates](#V-where) and\n[transformations](#V-acceptWith).  The result, whether accepted or rejected, is\nreturned as a\n[promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).\n\nFor example:\n\n```js\nV.validateAsync(\n  V.arrayId(\n    V.and(\n      R.is(String),\n      V.acceptWith(ghInfoOfAsyncChecked),\n      V.keep(\n        'name',\n        V.propsOr(V.remove, {\n          name: R.is(String),\n          stargazers_count: V.and(\n            R.is(Number),\n            [R.lte(1000), n =\u003e `Only ${n} stars. You know how to fix it!`]\n          )\n        })\n      )\n    )\n  ),\n  [\n    'partial.lenses',\n    'partial.lenses.validation'\n  ]\n).catch(R.identity).then(console.log)\n// Error: [\n//   {\n//     \"stargazers_count\": \"Only 448 stars. You know how to fix it!\",\n//     \"name\": \"partial.lenses\"\n//   },\n//   {\n//     \"stargazers_count\": \"Only 5 stars. You know how to fix it!\"\n//     \"name\": \"partial.lenses.validation\",\n//   }\n// ]\n```\n\n#### \u003ca id=\"general\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#general) [General](#general)\n\nIt is also possible to run validation rules with an arbitrary computational\nmonad such as a monad based on observables.\n\n##### \u003ca id=\"V-run\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-run) [`V.run({Monad, onAccept: data =\u003e any, onReject: error =\u003e any}, rule, data) ~\u003e any`](#V-run) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.run({Monad, onAccept, onReject}, rule, data)` runs the given validation rule\non the given data using the specified computational monad and either calls the\naccept callback with the validated data or the reject callback with the\nvalidation errors.\n\nThe parameters `Monad`, `onAccept`, and `onReject` are optional and default to\nwhat [`V.validate`](#V-validate) uses.  The `Monad` parameter needs to be a\n[Static Land](https://github.com/rpominov/static-land) compatible\n[Monad](https://github.com/rpominov/static-land) with all the four functions.\nIf you specify the `Monad`, you will likely want to specify both `onAccept` and\n`onReject` as well.\n\n### \u003ca id=\"primitive\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#primitive) [Primitive](#primitive)\n\nAt the most basic level a rule either accepts or rejects the value in focus.\n\n#### \u003ca id=\"V-accept\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-accept) [`V.accept ~\u003e rule`](#V-accept) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.accept` accepts the current focus as is.  `V.accept` should be rarely used as\nit performs no validation whatsoever.\n\n#### \u003ca id=\"V-acceptAs\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-acceptAs) [`V.acceptAs(value) ~\u003e rule`](#V-acceptAs) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.acceptAs(value)` accepts the current focus and replaces it with the given\nvalue.  `V.acceptAs` is rarely used alone, because it performs no validation as\nsuch, and is usually combined with e.g. [`V.and`](#V-and).\n\nFor example:\n\n```js\nV.validate(V.and(R.identical(1), V.acceptAs('one')), 1)\n// 'one'\n```\n\n#### \u003ca id=\"V-acceptWith\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-acceptWith) [`V.acceptWith(async (value, index) =\u003e value) ~\u003e rule`](#V-acceptWith) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.acceptWith(fn)` accepts the current focus and replaces it with the value\nreturned by the given [possibly async](#asynchronous) function.  `V.acceptWith`\nis rarely used alone, because it performs no validation as such, and is usually\ncombined with e.g. [`V.and`](#V-and).\n\nIn a logical [`V.or`](#V-or) each rule gets the same value as input and the\nresult of the first accepting rule becomes the result.  In a logical\n[`V.and`](#V-and) the output of a previous rule becomes the input of the next\nrule.\n\nFor example:\n\n```js\nV.validate(\n  V.and(\n    V.or(\n      V.and(\n        R.is(Number),\n        V.acceptWith(n =\u003e `number ${n}`)\n      ),\n      R.is(String)\n    ),\n    V.acceptWith(R.toUpper)\n  ),\n  10\n)\n// 'NUMBER 10'\n```\n\n#### \u003ca id=\"V-reject\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-reject) [`V.reject ~\u003e rule`](#V-reject) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.reject` rejects the current focus as is.  In case the focus is `undefined`,\nthe error will be `null` instead.\n\nThe idea is that the validation error data structure simply contains the parts\nof the validated data structure that weren't accepted.  This usually allows a\nprogrammer who is familiar with the system to quickly diagnose the problem.\n\nFor example:\n\n```js\nV.errors(\n  V.propsOr(V.reject, {}),\n  {\n    thisField: 'is not allowed',\n  }\n)\n// { thisField: 'is not allowed' }\n```\n\n#### \u003ca id=\"V-rejectAs\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-rejectAs) [`V.rejectAs(error) ~\u003e rule`](#V-rejectAs) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.rejectAs(error)` rejects the current focus as the given error value.  In case\nthe given error value is `undefined`, it is replaced with `null` instead.\n\nUsing `V.rejectAs` one can specify what the error should be.  This way an error\ndata structure can be constructed that can, for example, contain error messages\nto be displayed in a form that an end user can understand.\n\nFor example:\n\n```js\nV.errors(\n  V.propsOr(V.rejectAs('Unexpected field'), {}),\n  {\n    thisField: 'is not allowed',\n  }\n)\n// { thisField: 'Unexpected field' }\n```\n\n#### \u003ca id=\"V-rejectWith\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-rejectWith) [`V.rejectWith(async (value, index) =\u003e error) ~\u003e rule`](#V-rejectWith) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.rejectWith(fn)` rejects the current focus with the error value returned by\nthe given [possibly async](#asynchronous) function from the value in focus.  In\ncase the return value is `undefined`, the error will be `null` instead.\n\nUsing `V.rejectWith` one can specify what the error should be depending on the\nvalue in focus.  This allows detailed error messages to be constructed.\n\nFor example:\n\n```js\nV.errors(\n  V.propsOr(\n    V.rejectWith(value =\u003e `Unexpected field: ${JSON.stringify(value)}`),\n    {}\n  ),\n  {\n    thisField: 'is not allowed',\n  }\n)\n// { thisField: 'Unexpected field: \"is not allowed\"' }\n```\n\n#### \u003ca id=\"V-remove\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-remove) [`V.remove ~\u003e rule`](#V-remove) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.remove` replaces the not yet rejected value in focus with `undefined`,\nwhich means that it is removed from the surrounding array or object.  Beware\nthat `V.remove` by itself performs no validation.  You usually combine\n`V.remove` with e.g. [`V.and`](#V-and) or [`V.propsOr`](#V-propsOr).\n\nFor example:\n\n```js\nV.validate(\n  V.propsOr(V.remove, {\n    required: R.is(String)\n  }),\n  {\n    required: 'field',\n    unexpected: 'and removed'\n  }\n)\n// { required: 'field' }\n```\n\n### \u003ca id=\"predicates\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#predicates) [Predicates](#predicates)\n\nUnary (and binary) functions are implicitly treated as predicates and lifted to\nvalidation rules using [`V.where`](#V-where).\n\n#### \u003ca id=\"V-where\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-where) [`V.where(async (value, index) =\u003e testable) ~\u003e rule`](#V-where) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.where(predicate)`, or using the shorthand notation `predicate`, lifts the\ngiven [possibly async](#asynchronous) predicate to a validation rule.  In case\nthe focus does not satisfy the predicate, it is rejected with\n[`V.reject`](#V-reject).\n\nNote that explicitly calling `V.where` is typically unnecessary, because unary\n(and binary) functions are implicitly treated as predicates and lifted with\n`V.where` to rules in this library.\n\nFor example:\n\n```js\nV.validate(\n  V.props({\n    isNumber: V.where(R.is(Number)),\n    alsoNumber: R.is(Number) // \u003c-- implicit `V.where`\n  }),\n  {\n    isNumber: 101,\n    alsoNumber: 42\n  }\n)\n// { isNumber: 101, alsoNumber: 42 }\n```\n\nIn case the predicate throws an exception, the focus is rejected with the\nexception as the error value.\n\n### \u003ca id=\"elaboration\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#elaboration) [Elaboration](#elaboration)\n\nIt is also possible to modify the error after a rule has rejected the focus.\n\n#### \u003ca id=\"V-modifyError\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-modifyError) [`V.modifyError(async (value, error, index) =\u003e error, rule) ~\u003e rule`](#V-modifyError) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.modifyError(fn, rule)`, or using the shorthand notation `[rule, fn]`, acts\nlike `rule` except that in case the rule rejects the focus, the error is\ncomputed using the given [possibly async](#asynchronous) function that is given\nthe value in focus, the error from the rule and the index of the focus.  In case\nthe given function returns `undefined`, the error will be `null` instead.\n\nNote that the shorthand notation `[rule, fn]` can be used instead of a more\nverbose function call.  This shorthand is provided to make it more convenient to\nattach detailed error messages to rules.\n\nFor example:\n\n```js\nV.errors(\n  V.choose(data =\u003e {\n    const expectedSum = L.sum(['numbers', L.elems], data)\n    return V.props({\n      numbers: V.arrayIx(R.is(Number)),\n      sum: [ // \u003c-- Implicit `V.modifyError`\n        R.identical(expectedSum),\n        actualSum =\u003e `Expected ${expectedSum} instead of ${actualSum}`\n      ]\n    })\n  }),\n  {\n    numbers: [3, 1, 4],\n    sum: 9\n  }\n)\n// { sum: 'Expected 8 instead of 9' }\n```\n\n#### \u003ca id=\"V-setError\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-setError) [`V.setError(error, rule) ~\u003e rule`](#V-setError) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.setError(error, rule)`, or using the shorthand notation `[rule, error]` when\n`error` is not a function, acts like `rule` except that in case the rule rejects\nthe focus, the given error is used instead.  In case the given error is\n`undefined`, it is replaced with `null` instead.\n\nNote that the shorthand notation `[rule, error]` can be used instead of a more\nverbose function call when the `error` is not a function.  In case `error` is a\nfunction, it is called like with [`V.modifyError`](#V-modifyError).  This\nshorthand is provided to make it more convenient to attach detailed error\nmessages to rules.\n\nFor example:\n\n```js\nV.errors(\n  V.choose(data =\u003e {\n    const expectedSum = L.sum(['numbers', L.elems], data)\n    return V.props({\n      numbers: V.arrayIx(R.is(Number)),\n      sum: [ // \u003c-- Implicit `V.setError`\n        R.identical(expectedSum),\n        `Expected ${expectedSum}`\n      ]\n    })\n  }),\n  {\n    numbers: [3, 1, 4],\n    sum: 9\n  }\n)\n// { sum: 'Expected 8' }\n```\n\n### \u003ca id=\"logical\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#logical) [Logical](#logical)\n\nLogical connectives provide a simple means to combine rules to form more complex\nrules.\n\n#### \u003ca id=\"V-and\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-and) [`V.and(...rules) ~\u003e rule`](#V-and) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.and(rule1, ..., ruleN)` validates the value in focus with all of the given\nrules one-by-one starting from the first given rule.  In case some rule rejects\nthe focus, that becomes the result of `V.and`.  Otherwise the result of `V.and`\nis the accepted result produced by passing the original focus through all of the\ngiven rules.  Note that `V.and` is not curried like [`V.both`](#V-both).\n\n#### \u003ca id=\"V-both\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-both) [`V.both(rule, rule) ~\u003e rule`](#V-both) \u003csmall\u003e\u003csup\u003ev0.3.3\u003c/sup\u003e\u003c/small\u003e\n\n`V.both(rule1, rule2)` validates the value in focus with both of the given rules\nstarting with the first of the given rules.  `V.both(rule1, rule2)` is\nequivalent to [`V.and(rule1, rule2)`](#V-and).\n\n#### \u003ca id=\"V-either\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-either) [`V.either(rule, rule) ~\u003e rule`](#V-either) \u003csmall\u003e\u003csup\u003ev0.3.3\u003c/sup\u003e\u003c/small\u003e\n\n`V.either(rule1, rule2)` validates the value in focus with either of the given\nrules starting with the first of the given rules.  `V.either(rule1, rule2)` is\nequivalent to [`V.or(rule1, rule2)`](#V-or).\n\n#### \u003ca id=\"V-not\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-not) [`V.not(rule) ~\u003e rule`](#V-not) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.not(rule)` validates the value in focus with the given rule.  In case the\nrule accepts the focus, `V.not` rejects it instead.  In case the rule rejects\nthe focus, `V.not` accepts it instead.\n\n#### \u003ca id=\"V-or\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-or) [`V.or(...rules) ~\u003e rule`](#V-or) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.or(rule1, ..., ruleN)` tries to validate the value in focus with one of the\ngiven rules starting from the first given rule.  In case some rule accepts the\nfocus, that becomes the result of `V.or`.  Otherwise the error produced by the\nlast of the given rules becomes the result of `V.or`.  Note that `V.or` is not\ncurried like [`V.either`](#V-either).\n\n### \u003ca id=\"arrays\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#arrays) [Arrays](#arrays)\n\nRules for validating elements can be lifted to rules for validating arrays of\nelements.\n\n#### \u003ca id=\"uniform\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#uniform) [Uniform](#uniform)\n\nAll elements in a uniform array have the same form.\n\n##### \u003ca id=\"V-arrayId\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-arrayId) [`V.arrayId(rule) ~\u003e rule`](#V-arrayId) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.arrayId(rule)` validates the elements of an array with the given rule.  In\ncase one or more elements are rejected, the error is an array containing only\nthe rejected elements.\n\nThe idea is that the elements of the validated array are addressed by some\nunique identities intrinsic to the elements.  Filtering out the accepted\nelements keeps the error result readable.\n\n##### \u003ca id=\"V-arrayIx\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-arrayIx) [`V.arrayIx(rule) ~\u003e rule`](#V-arrayIx) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.arrayIx(rule)` validates the elements of an array with the given rule.  In\ncase one or more elements are rejected, the error is an array containing the\nrejected elements and `null` values for the accepted elements.\n\nThe idea is that elements of the validated array are addressed only by their\nindex and it is necessary to keep the rejected elements at their original\nindices.  The accepted elements are replaced with `null` to make the output less\nnoisy.\n\n#### \u003ca id=\"varying\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#varying) [Varying](#varying)\n\nElements at different positions in a varying array may have different forms.\n\n##### \u003ca id=\"V-args\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-args) [`V.args(...rules) ~\u003e rule`](#V-args) \u003csmall\u003e\u003csup\u003ev0.3.1\u003c/sup\u003e\u003c/small\u003e\n\n`V.args(rule1, ..., ruleN)` validates an array by validating each element of the\narray with a specific rule.  If the array is shorter than the number of rules,\nthe missing elements are treated as being `undefined` and validated with the\ncorresponding rules.  This means that rules for optional elements need to be\nexplicitly specified as such.  If the array is longer than the number of rules,\nthe extra elements are simply accepted.  This is roughly how JavaScript treats\nfunction arguments.  See also [`V.tuple`](#V-tuple).\n\n##### \u003ca id=\"V-tuple\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-tuple) [`V.tuple(...rules) ~\u003e rule`](#V-tuple) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.tuple(rule1, ..., ruleN)` validates a fixed length array by validating each\nelement of the array with a specific rule.  See also [`V.args`](#V-args).\n\nFor example:\n\n```js\nV.accepts(\n  V.tuple(R.is(String), R.is(Number)),\n  ['one', 2]\n)\n// true\n```\n\nNote that elements cannot be removed from a tuple using [`V.remove`](#V-remove).\n\n### \u003ca id=\"functions\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#functions) [Functions](#functions)\n\nIt is also possible to validate functions.  Of course, validating a function is\ndifferent from validating data, because it is not possible to validate the\nactual arguments to a function before the function is called and likewise it is\nonly possible to validate the return value of a function after the function\nreturns.  Therefore validating a function means that the function is wrapped\nwith a function that performs validation of arguments and the return value as\nthe function is called.\n\n#### \u003ca id=\"V-dependentFn\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-dependentFn) [`V.dependentFn(rule, (...args) =\u003e rule) ~\u003e rule`](#V-dependentFn) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.dependentFn(argumentsRule, argumentsToResultRule)` [wraps](#V-acceptWith) the\nfunction at focus with a validating wrapper that validates the arguments to and\nreturn value from the function as it is called.  The rule for validating the\nreturn value is constructed by calling the given function with the validated\narguments.  In case there is no need for the return value rule to depend on the\nactual arguments, one can use the simpler [`V.freeFn`](#V-freeFn) combinator\ninstead.\n\nFor example:\n\n```js\nconst sqrt = V.validate(\n  V.dependentFn(\n    V.args(R.both(R.is(Number), R.lte(0))),\n    x =\u003e y =\u003e Math.abs(y*y - x) \u003c 0.001\n  ),\n  Math.sqrt\n)\n\nsqrt(4)\n// 2\n```\n\nNote that the wrapped function produced by `V.dependentFn` is not curried and\nhas zero arity.  If necessary, you can wrap the produced function with\ne.g. [`R.curryN`](http://ramdajs.com/docs/#curryN) or\n[`R.nAry`](http://ramdajs.com/docs/#nAry) to change the arity of the function.\n\n#### \u003ca id=\"V-freeFn\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-freeFn) [`V.freeFn(rule, rule) ~\u003e rule`](#V-freeFn) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.freeFn(argumentsRule, resultRule)` [wraps](#V-acceptWith) the function at\nfocus with a validating wrapper that validates the arguments to and return value\nfrom the function as it is called.  `V.freeFn` does not allow the rule for the\nreturn value to depend on the arguments.  If you wish to validate the return\nvalue depending on the arguments you need to use the\n[`V.dependentFn`](#V-dependentFn) combinator.\n\nFor example:\n\n```js\nconst random = V.validate(\n  V.freeFn(\n    V.tuple(),\n    R.both(R.lte(0), R.gt(1))\n  ),\n  Math.random\n)\n\nrandom('Does not take arguments!')\n// Error: [\n//   \"Does not take arguments!\"\n// ]\n```\n\nNote that the wrapped function produced by `V.freeFn` is not curried and has\nzero arity.  If necessary, you can wrap the produced function with\ne.g. [`R.curryN`](http://ramdajs.com/docs/#curryN) or\n[`R.nAry`](http://ramdajs.com/docs/#nAry) to change the arity of the function.\n\n### \u003ca id=\"objects\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#objects) [Objects](#objects)\n\nRules for validating objects can be formed by composing rules for validating\nindividual properties of objects.\n\n#### \u003ca id=\"V-keep\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-keep) [`V.keep('prop', rule) ~\u003e rule`](#V-keep) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.keep('prop', rule)` acts like the given rule except that in case the rule\nrejects the focus, the specified property is copied from the original object to\nthe error object.  This is useful when e.g. validating arrays of objects with an\nidentifying property.  Keeping the identifying property allows the rejected\nobject to be identified.\n\n#### \u003ca id=\"V-optional\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-optional) [`V.optional(rule) ~\u003e rule`](#V-optional) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.optional(rule)` acts like the given rule except that in case the focus is\n`undefined` it is accepted without invoking the given rule.  This is\nparticularly designed for specifying that an object property is optional.\n\nFor example:\n\n```js\nV.validate(\n  V.arrayIx(\n    V.props({\n      field: V.optional([R.is(Number), 'Expected a number'])\n    })\n  ),\n  [\n    {notTheField: []},\n    {field: 'Not a number'},\n    {field: 76}\n  ]\n)\n// Error: [\n//   {\n//     \"notTheField\": []\n//   },\n//   {\n//     \"field\": \"Expected a number\"\n//   },\n//   null\n// ]\n```\n\n#### \u003ca id=\"V-props\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-props) [`V.props({...prop: rule}) ~\u003e rule`](#V-props) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.props({prop: rule, ...})` is for validating an object and is given a template\nobject of rules with which to validate the corresponding fields.  Unexpected\nfields are rejected.  Note that `V.props` is equivalent to\n[`V.propsOr(V.reject)`](#V-propsOr).\n\n#### \u003ca id=\"V-propsOr\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-propsOr) [`V.propsOr(rule, {...prop: rule}) ~\u003e rule`](#V-propsOr) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.propsOr(otherwise, {prop: rule, ...})` is for validating an object and is\ngiven a rule to apply to fields not otherwise specified and a template object of\nrules with which to validate the corresponding fields.  Note that\n[`V.props`](#V-props) is equivalent to `V.propsOr(V.reject)`.\n\n### \u003ca id=\"conditional\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#conditional) [Conditional](#conditional)\n\nRules can be chosen conditionally on the data being validated.\n\n#### \u003ca id=\"V-cases\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-cases) \u003ca href=\"#V-cases\"\u003e\u003ccode\u003eV.cases(...[(value, index) =\u0026gt; testable, rule][, [rule]]) ~\u0026gt; rule\u003c/code\u003e\u003c/a\u003e \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.cases([p1, r1], ..., [pN, rN], [r])` is given `[predicate, rule]` -pairs as\narguments.  The predicates are called from first to last with the focus.  In\ncase a predicate passes, the corresponding rule is used on the focus and the\nremaining predicates are skipped and rules ignored.  The last argument to\n`V.cases` can be a default rule that omits the predicate, `[rule]`, in which\ncase the rule is always applied in case no predicate passes.  In case all\npredicates fail and there is no default rule, the focus is rejected.\n\nFor example:\n\n```js\nV.validate(\n  V.cases(\n    [\n      R.whereEq({type: 'a'}),\n      V.propsOr(V.accept, {\n        foo: [R.lt(0), 'Must be positive']\n      })\n    ],\n    [\n      V.propsOr(V.accept, {\n        foo: [R.gt(0), 'Must be negative']\n      })\n    ]\n  ),\n  {\n    type: 'b',\n    foo: 10\n  }\n)\n// Error: {\n//   \"foo\": \"Must be negative\"\n// }\n```\n\nNote that, like with [`V.ifElse`](#V-ifElse), `V.cases([p1, r1], ..., [rN])` can\nbe expressed in terms of the logical operators, but `V.cases` has a simpler\ninternal implementation and is likely to be faster.\n\n#### \u003ca id=\"V-casesOf\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-casesOf) \u003ca href=\"#V-casesOf\"\u003e\u003ccode\u003eV.casesOf(traversal, ...[(value, index) =\u0026gt; testable, rule][, [rule]]) ~\u0026gt; rule\u003c/code\u003e\u003c/a\u003e \u003csmall\u003e\u003csup\u003ev0.3.4\u003c/sup\u003e\u003c/small\u003e\n\n`V.casesOf(traversal, [p1, r1], ..., [pN, rN], [r])` is like\n[`V.cases`](#V-cases) except that subfocuses for the predicates are produced by\nthe given traversal from the current focus and a case is taken if the predicate\naccepts any one of the subfocuses.\n\nFor example:\n\n```js\nV.validate(\n  V.casesOf(\n    'type',\n    [R.identical('number'), V.props({type: R.is(String), value: R.is(Number)})],\n    [R.identical('string'), V.props({type: R.is(String), value: R.is(String)})]\n  ),\n  {\n    type: 'string',\n    value: 'foo'\n  }\n)\n// { type: 'string', value: 'foo' }\n```\n\n#### \u003ca id=\"V-ifElse\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-ifElse) [`V.ifElse((value, index) =\u003e testable, rule, rule) ~\u003e rule`](#V-ifElse) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.ifElse(predicate, consequent, alternative)` acts like the given consequent\nrule in case the predicate is satisfied by the focus and otherwise like the\ngiven alternative rule.\n\nFor example:\n\n```js\nV.validate(\n  V.ifElse(R.is(Number), R.lte(0), R.is(String)),\n  -1\n)\n// Error: -1\n```\n\nNote that `V.ifElse(p, c, a)` can be expressed as `V.or(V.and(p, c),\nV.and(V.not(p), a))`, but `V.ifElse` has a simpler internal implementation and\nis likely to be faster.\n\n### \u003ca id=\"dependent\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#dependent) [Dependent](#dependent)\n\nRules can depend on the data being validated.\n\n#### \u003ca id=\"V-choose\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-choose) [`V.choose((value, index) =\u003e rule) ~\u003e rule`](#V-choose) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.choose(fn)` is given a function that gets the current focus and then must\nreturn rules to be used on the focus.  This allows rules to depend on the data\nand allows rules that examine multiple parts of the data.\n\nFor example:\n\n```js\nV.validate(\n  V.choose(({a, b}) =\u003e V.props({\n    a: [R.equals(b), \"Must equal 'b'\"],\n    b: [R.equals(a), \"Must equal 'a'\"]\n  })),\n  {\n    a: 1,\n    b: 2\n  }\n)\n// Error: {\n//   \"a\": \"Must equal 'b'\",\n//   \"b\": \"Must equal 'a'\"\n// }\n```\n\nNote that `V.choose` can be used to implement conditionals like\n[`V.cases`](#V-cases) and [`V.ifElse`](#V-ifElse).  Also note that code inside\n`V.choose`, including code that constructs rules, is always run when the\n`V.choose` rule itself is used.  For performance reasons it can be advantageous\nto move invariant expressions outside of the body of the function given to\n`V.choose`.  Also, when simpler conditional combinators like\n[`V.cases`](#V-cases) or [`V.ifElse`](#V-ifElse) are sufficient, they can be\npreferable for performance reasons, because they are given previously\nconstructed rules.\n\n### \u003ca id=\"recursive\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#recursive) [Recursive](#recursive)\n\nRules for recursive data structures can be constructed with the help of\n[`V.choose`](#V-choose) and [`V.lazy`](#V-lazy), which both allow one to refer\nback to the rule itself or to delay the invocation of a rule computing function.\n\n#### \u003ca id=\"V-lazy\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-lazy) [`V.lazy(rule =\u003e rule) ~\u003e rule`](#V-lazy) \u003csmall\u003e\u003csup\u003ev0.3.0\u003c/sup\u003e\u003c/small\u003e\n\n`V.lazy(fn)` constructs a rule lazily.  The given function is passed a\nforwarding proxy to its own return value.  This allows the rule to use itself as\na subrule and construct a recursive rule.\n\nFor example:\n\n```js\nV.accepts(\n  V.lazy(tree =\u003e V.arrayId(\n    V.props({\n      name: R.is(String),\n      children: tree\n    })\n  )),\n  [\n    {\n      name: 'root',\n      children: [\n        {name: '1st child', children: []},\n        {\n          name: '2nd child',\n          children: [{name: 'You got the point', children: []}]\n        },\n      ]\n    }\n  ]\n)\n// true\n```\n\n### \u003ca id=\"transformation\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#transformation) [Transformation](#transformation)\n\nRules can modify the value after a rule has accepted the focus.\n\n#### \u003ca id=\"ad-hoc\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#ad-hoc) [Ad-hoc](#ad-hoc)\n\nRules can include simple ad-hoc post-validation transformations.\n\n##### \u003ca id=\"V-modifyAfter\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-modifyValue) [`V.modifyAfter(rule, async (value, index) =\u003e value) ~\u003e rule`](#V-modifyAfter) \u003csmall\u003e\u003csup\u003ev0.3.3\u003c/sup\u003e\u003c/small\u003e\n\n`V.modifyAfter(rule, fn)` replaces the focus after the given rule has accepted\nit with the value returned by the given [possibly async](#asynchronous)\nfunction.  `V.modifyAfter(rule, fn)` is equivalent to [`V.both(rule,\nV.acceptWith(fn))`](#V-both).\n\n##### \u003ca id=\"V-setAfter\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-setValue) [`V.setAfter(rule, value) ~\u003e rule`](#V-setAfter) \u003csmall\u003e\u003csup\u003ev0.3.3\u003c/sup\u003e\u003c/small\u003e\n\n`V.setAfter(rule, value)` replaces the focus after the given rule has accepted\nit with the given value.  `V.setAfter(rule, value)` is equivalent to\n[`V.both(rule, V.acceptAs(value))`](#V-both).\n\n##### \u003ca id=\"V-removeAfter\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-removeValue) [`V.removeAfter(rule) ~\u003e rule`](#V-removeAfter) \u003csmall\u003e\u003csup\u003ev0.3.3\u003c/sup\u003e\u003c/small\u003e\n\n`V.removeAfter(rule)` removes the focus after the given rule has accepted it.\n`V.removeAfter(rule)` is equivalent to [`V.both(rule, V.remove)`](#V-both).\n\n#### \u003ca id=\"promotion\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#promotion) [Promotion](#promotion)\n\nRules can validate versioned data and transform it to another version in the\nprocess.  The combinators [`V.promote`](#V-promote),\n[`V.upgrades`](#V-upgrades), [`V.upgradesOf`](#V-upgradesOf) are designed for\ncases where there are multiple versions of data or schema.  Using them one can\nvalidate any one of the versions and also convert the data to desired version\n\u0026mdash; usually to the latest version \u0026mdash; so that rest of the program does\nnot need to deal with different versions.\n\n##### \u003ca id=\"V-promote\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-promote) \u003ca href=\"#V-promote\"\u003e\u003ccode\u003eV.promote(...[rule[, (value, index) =\u0026gt; value]]) ~\u0026gt; rule\u003c/code\u003e\u003c/a\u003e \u003csmall\u003e\u003csup\u003ev0.3.6\u003c/sup\u003e\u003c/small\u003e\n\n`V.promote` is like [`V.or`](#V-or), but the rules given to `V.promote` need to\nbe wrapped inside an array `[rule]` and may optionally include a transformation\nfunction, `[rule, fn]`.  `V.promote` tries, like [`V.or`](#V-or), to find a rule\nthat accepts the focus.  If no such rule is found, the focus is rejected.\nOtherwise if the accepting rule has an associated function, then the function is\nused to transform the focus and the same validation process is rerun.  This way\nany sequence of transformations is also validated.\n\nFor example:\n\n```js\nV.validate(\n  V.promote(\n    [\n      V.props({\n        type: R.identical('v2'),\n        value: R.is(Number)\n      })\n    ],\n    [\n      V.props({\n        type: R.identical('v1'),\n        constant: R.is(Number)\n      }),\n      ({constant}) =\u003e ({type: 'v2', value: constant})\n    ]\n  ),\n  {type: 'v1', constant: 42}\n)\n// { type: 'v2', value: 42 }\n```\n\nNote that [`V.or(r1, ..., rN)`](#V-or) is equivalent to `V.promote([r1], ...,\n[rN])`.\n\n##### \u003ca id=\"V-upgrades\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-upgrades) \u003ca href=\"#V-upgrades\"\u003e\u003ccode\u003eV.upgrades(...[(value, index) =\u0026gt; testable, rule[, async (value, index) =\u0026gt; value]]) ~\u0026gt; rule\u003c/code\u003e\u003c/a\u003e \u003csmall\u003e\u003csup\u003ev0.3.6\u003c/sup\u003e\u003c/small\u003e\n\n`V.upgrades` is like [`V.cases`](#V-cases), but each case may optionally include\na transformation function, `[predicate, rule, fn]`.  `V.upgrades` tries, like\n[`V.cases`](#V-cases), to find the first passing predicate.  When no such\npredicate is found, the focus is rejected.  Otherwise the focus is validated\nwith the associated rule.  If the case also includes a [possibly\nasync](#asynchronous) transformation function, the function is used to transform\nthe value in focus and the same validation process is rerun.  This way any\nsequence of transformations is also validated.\n\nFor example:\n\n```js\nV.validate(\n  V.upgrades(\n    [\n      L.get(['type', R.identical('v1')]),\n      V.props({\n        type: R.is(String),\n        constant: R.is(Number)\n      }),\n      ({constant}) =\u003e ({type: 'v2', value: constant})\n    ],\n    [\n      L.get(['type', R.identical('v2')]),\n      V.props({\n        type: R.is(String),\n        value: R.is(Number)\n      })\n    ]\n  ),\n  {type: 'v1', constant: 42}\n)\n// { type: 'v2', value: 42 }\n```\n\nNote that `V.cases([p1, r1], ..., [[pN, ]rN])` is equivalent to `V.upgrades([p1,\nr1], ..., [[pN, ]rN])`.\n\n##### \u003ca id=\"V-upgradesOf\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#V-upgradesOf) \u003ca href=\"#V-upgradesOf\"\u003e\u003ccode\u003eV.upgradesOf(traversal, ...[(value, index) =\u0026gt; testable, rule[, async (value, index) =\u003e value]]) ~\u0026gt; rule\u003c/code\u003e\u003c/a\u003e \u003csmall\u003e\u003csup\u003ev0.3.6\u003c/sup\u003e\u003c/small\u003e\n\n`V.upgradesOf` is like [`V.casesOf`](#V-casesOf), but each case may optionally\ninclude a transformation function, `[predicate, rule, fn]`.  `V.upgradesOf`\ntries, like [`V.casesOf`](#V-casesOf), to find the first predicate that accepts\nany one of the the traversed subfocuses.  When no such predicate is found, the\nfocus is rejected.  Otherwise the focus is validated with the associated rule.\nIf the case also includes a [possibly async](#asynchronous) transformation\nfunction, the function is used to transform the value in focus and the same\nvalidation process is rerun.  This way any sequence of transformations is also\nvalidated.\n\nFor example:\n\n```js\nV.validate(\n  V.upgradesOf(\n    'type',\n    [\n      R.identical('v1'),\n      V.props({\n        type: R.is(String),\n        constant: R.is(Number)\n      }),\n      ({constant}) =\u003e ({type: 'v2', value: constant})\n    ],\n    [\n      R.identical('v2'),\n      V.props({\n        type: R.is(String),\n        value: R.is(Number)\n      })\n    ]\n  ),\n  {type: 'v1', constant: 42}\n)\n// { type: 'v2', value: 42 }\n```\n\nNote that `V.casesOf(t, [p1, r1], ..., [[pN, ]rN])` is equivalent to\n`V.upgradesOf(t, [p1, r1], ..., [[pN, ]rN])`.\n\n## \u003ca id=\"tips\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#tips) [Tips](#tips)\n\nThe following subsections give some tips on effective use of this library.\n\n### \u003ca id=\"prefer-case-analysis-to-logical-or\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#prefer-case-analysis-to-logical-or) [Prefer case analysis to logical OR](#prefer-case-analysis-to-logical-or)\n\nThe logical combinators [`V.or`](#V-or), [`V.either`](#V-either), and also\n[`V.promote`](#V-promote), can be convenient, but it is often preferable to use\nconditional combinators like [`V.cases`](#V-cases) and\n[`V.upgrades`](#V-upgrades), because, once a case predicate has been satisfied,\nno other cases are attempted in case the corresponding rule fails and the\nresulting error is likely to be of higher quality.\n\n### \u003ca id=\"prefer-rule-templates-to-logical-and\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#prefer-rule-templates-to-logical-and) [Prefer rule templates to logical AND](#prefer-rule-templates-to-logical-and)\n\nIt might be tempting to use [`V.and`](#V-and) to combine\n[`V.propsOr(V.accept)`](#V-propsOr) rules\n\n```jsx\nV.and(\n  V.propsOr(V.accept, rules1),\n  V.propsOr(V.accept, rules2),\n  // ...\n)\n```\n\nbut this has a couple of disadvantages:\n\n* The resulting rule will accept additional properties.\n* If one of the `V.propsOr` rules rejects, then errors from later rules are not\n  reported.\n\nIt is usually better to combine rule templates inside [`V.props`](#V-props) instead:\n\n```jsx\nV.props({\n  ...rules1,\n  ...rules2,\n  // ...\n})\n```\n\nThis way additional properties are not accepted and errors from all rules are\nreported in case of rejection.\n\n## \u003ca id=\"known-caveats\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#known-caveats) [Known caveats](#known-caveats)\n\nProbably the main weakness in the design of this library is that this library\nspecifically tries to avoid having to implement everything.  In particular, one\nof the ideas is to simply allow arbitrary predicates from a library like\n[Ramda](http://ramdajs.com/) to be used as rules.  This means that rules do not\ncontain extra information such as a corresponding [random value\ngenerator](https://en.wikipedia.org/wiki/QuickCheck) of values matching the rule\nor a traversable specification of the rule for exporting the specification for\n[external tools](https://en.wikipedia.org/wiki/OpenAPI_Specification).  One way\nto provide such features is to pair validation rules with the necessary extra\ninformation.  It should be possible to do that outside of this library.\n\nThe current implementation does not operate\n[incrementally](https://en.wikipedia.org/wiki/Incremental_computing).  Every\ntime e.g. [`V.validate`](#V-validate) is called, everything is recomputed.  This\ncan become a performance issue particularly in an interactive setting where\nsmall incremental changes to a data structure are being validated in response to\nuser actions.  It should be possible to implement caching so that on repeated\ncalls only changes would be recomputed.  This is left for future work.\n\n## \u003ca id=\"related-work\"\u003e\u003c/a\u003e [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses.validation/index.html#related-work) [Related work](#related-work)\n\nThis library primarily exists as a result of Stefan Rimaila's work on\n[validation](https://github.com/stuf/validation) using lenses.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcalmm-js%2Fpartial.lenses.validation","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcalmm-js%2Fpartial.lenses.validation","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcalmm-js%2Fpartial.lenses.validation/lists"}