diff --git a/CHANGELOG.md b/CHANGELOG.md
index cbda0297..358e7ace 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,89 @@
# Partial Lenses Changelog
+## 14.0.0
+
+*The current plan is to change Partial Lenses to support so called naked or
+prototypeless objects with `null` prototype (i.e. `Object.create(null)`) in a
+following major version. This is a breaking change although it is likely that
+it will not affect most users. Usefully warning for this change of behaviour by
+adding diagnostics to optics seems somewhat difficult.*
+
+Previously obsoleted `L.iftes` was removed.
+
+The `L.Constant` functor was removed. It can be replaced as follows:
+
+```diff
+-L.constant
++{map: (_, x) => x}
+```
+
+`L.get` has been changed to use the now exported `L.Select` applicative instead
+of the removed `L.Constant` functor. The reason for this change is that it both
+generalizes `L.get` and simplifies things overall.
+
+`L.get` now works exactly like `L.select`. `L.select` and `L.selectAs` have
+been consequently obsoleted. Change usages as follows:
+
+```diff
+-L.select(...)
++L.get(...)
+```
+
+```diff
+-L.selectAs(...)
++L.getAs(...)
+```
+
+In the cases where `L.get` previously returned a valid result, the only
+differences are in the cases where `L.zero` is involved. `L.zero` is used by
+several other combinators that could be used as lenses including the
+conditionals,
+
+* `L.cond`, and
+* `L.condOf`,
+
+and the querying combinators,
+
+* `L.chain`,
+* `L.choice`,
+* `L.optional`,
+* `L.unless`, and
+* `L.when`,
+
+and the transform ops,
+
+* `L.assignOp`,
+* `L.modifyOp`,
+* `L.removeOp`, and
+* `L.setOp`.
+
+Previously `L.zero` was implemented so that it did something reasonable even
+with a plain functor. Since no operation in this library now uses a plain
+functor, the special case behaviour of `L.zero` has been removed. When
+previously used with `L.get`, `L.zero` passed `undefined` to the inner optics
+and ignored what the inner optics returned. For example, previously:
+
+```js
+L.get(['x', L.when(x => x > 0), L.valueOr(0)], {x: -1})
+// 0
+```
+
+but now `L.zero` exits early:
+
+```js
+L.get(['x', L.when(x => x > 0), L.valueOr(0)], {x: -1})
+// undefined
+```
+
+This is clearly a breaking change. However, this is unlikely to affect a large
+number of use cases. To get the old behavior, use of `L.zero` need to be
+avoided. In the example case, one could write:
+
+```js
+L.get(['x', L.ifElse(x => x > 0, [], R.always(0))], {x: -1})
+// 0
+```
+
## 13.10.0
There is no longer guarantee that optic operations return newly allocated data
@@ -69,13 +153,6 @@ with `L.valueOr`.
Removed previously obsoleted `L.findHint`.
-The current plan is to change Partial Lenses to support so called naked or
-prototypeless objects with `null` prototype (i.e. `Object.create(null)`) in the
-next major version. This is a breaking change although it is likely that it
-will not affect most users. Usefully warning for this change of behaviour by
-adding diagnostics to optics seems somewhat difficult. *This note was moved
-from 12.0.0.*
-
## 12.0.0
As documented in 11.21.0:
diff --git a/README.md b/README.md
index 47deb50b..dc7c4526 100644
--- a/README.md
+++ b/README.md
@@ -63,17 +63,9 @@ parts. [Try Lenses!](https://calmm-js.github.io/partial.lenses/playground.html)
* [`L.choices(optic, ...optics) ~> optic`](#L-choices "L.choices: (POptic s a, ...POptic s a) -> POptic s a") v11.10.0
* [`L.choose((maybeValue, index) => optic) ~> optic`](#L-choose "L.choose: ((Maybe s, Index) -> POptic s a) -> POptic s a") v1.0.0
* L.cond(...[(maybeValue, index) => testable, consequentOptic][, [alternativeOptic]]) ~> optic
v13.1.0
- * L.condOf(lens, ...[(maybeValue, index) => testable, consequentOptic][, [alternativeOptic]]) ~> optic
v13.5.0
+ * L.condOf(traversal, ...[(maybeValue, index) => testable, consequentOptic][, [alternativeOptic]]) ~> optic
v13.5.0
* [`L.ifElse((maybeValue, index) => testable, optic, optic) ~> optic`](#L-ifElse "L.ifElse: ((Maybe s, Index) -> Boolean) -> POptic s a -> POptic s a -> POptic s a") v13.1.0
- * ~~[`L.iftes((maybeValue, index) => testable, consequentOptic, ...[, alternativeOptic]) ~> optic`](#L-iftes "L.iftes: ((Maybe s, Index) -> Boolean) -> PLens s a -> PLens s a -> PLens s a") v11.14.0~~
* [`L.orElse(backupOptic, primaryOptic) ~> optic`](#L-orElse "L.orElse: (POptic s a, POptic s a) -> POptic s a") v2.1.0
- * [Querying](#querying)
- * [`L.chain((value, index) => optic, optic) ~> optic`](#L-chain "L.chain: ((a, Index) -> POptic s b) -> POptic s a -> POptic s b") v3.1.0
- * [`L.choice(...optics) ~> optic`](#L-choice "L.choice: (...POptic s a) -> POptic s a") v2.1.0
- * [`L.optional ~> optic`](#L-optional "L.optional: POptic a a") v3.7.0
- * [`L.unless((maybeValue, index) => testable) ~> optic`](#L-unless "L.unless: ((Maybe a, Index) -> Boolean) -> POptic a a") v12.1.0
- * [`L.when((maybeValue, index) => testable) ~> optic`](#L-when "L.when: ((Maybe a, Index) -> Boolean) -> POptic a a") v5.2.0
- * [`L.zero ~> optic`](#L-zero "L.zero: POptic s a") v6.0.0
* [Indices](#indices)
* [`L.joinIx(optic) ~> optic`](#L-joinIx "L.joinIx: POptic s a -> POptic s a") v13.15.0
* [`L.mapIx((index, maybeValue) => index) ~> optic`](#L-mapIx "L.mapIx: ((Index, Maybe a) -> Index) -> POptic a a") v13.15.0
@@ -84,9 +76,9 @@ parts. [Try Lenses!](https://calmm-js.github.io/partial.lenses/playground.html)
* [`L.getLog(lens, maybeData) ~> maybeValue`](#L-getLog "L.getLog: PLens s a -> Maybe s -> Maybe a") v13.14.0
* [`L.log(...labels) ~> optic`](#L-log "L.log: (...Any) -> POptic s s") v3.2.0
* [Internals](#internals)
- * [`L.Constant ~> Functor`](#L-Constant "L.Constant: Functor") v13.7.0
* [`L.Identity ~> Monad`](#L-Identity "L.Identity: Monad") v13.7.0
* [`L.IdentityAsync ~> Monadish`](#L-IdentityAsync "L.IdentityAsync: Monadish") v13.12.0
+ * [`L.Select ~> Applicative`](#L-Select "L.Select: Applicative") v14.0.0
* [`L.toFunction(optic) ~> optic`](#L-toFunction "L.toFunction: POptic s t a b -> (Maybe s, Index, (Functor|Applicative|Monad) c, (Maybe a, Index) -> c b) -> c t") v7.0.0
* [Transforms](#transforms)
* [Operations on transforms](#operations-on-transforms)
@@ -95,10 +87,10 @@ parts. [Try Lenses!](https://calmm-js.github.io/partial.lenses/playground.html)
* [Sequencing](#sequencing)
* [`L.seq(...transforms) ~> transform`](#L-seq "L.seq: (...PTransform s a) -> PTransform s a") v9.4.0
* [Transforming](#transforming)
- * [`L.assignOp(object) ~> optic`](#L-assignOp "L.assignOp: {p1: a1, ...ps} -> POptic {p1: a1, ...ps, ...o} {p1: a1, ...ps}") v11.13.0
- * [`L.modifyOp((maybeValue, index) => maybeValue) ~> optic`](#L-modifyOp "L.modifyOp: ((Maybe a, Index) -> Maybe a) -> POptic a a") v11.7.0
- * [`L.removeOp ~> optic`](#L-removeOp "L.removeOp: POptic a a") v11.7.0
- * [`L.setOp(maybeValue) ~> optic`](#L-setOp "L.setOp: Maybe a -> POptic a a") v11.7.0
+ * [`L.assignOp(object) ~> traversal`](#L-assignOp "L.assignOp: {p1: a1, ...ps} -> PTraversal {p1: a1, ...ps, ...o} {p1: a1, ...ps}") v11.13.0
+ * [`L.modifyOp((maybeValue, index) => maybeValue) ~> traversal`](#L-modifyOp "L.modifyOp: ((Maybe a, Index) -> Maybe a) -> PTraversal a a") v11.7.0
+ * [`L.removeOp ~> traversal`](#L-removeOp "L.removeOp: PTraversal a a") v11.7.0
+ * [`L.setOp(maybeValue) ~> traversal`](#L-setOp "L.setOp: Maybe a -> PTraversal a a") v11.7.0
* [Traversals](#traversals)
* [Creating new traversals](#creating-new-traversals)
* [`L.branch({prop: traversal, ...props}) ~> traversal`](#L-branch "L.branch: {p1: PTraversal p1 a, ...pts} -> PTraversal {p1: p1, ...ps} a") v5.1.0
@@ -116,6 +108,13 @@ parts. [Try Lenses!](https://calmm-js.github.io/partial.lenses/playground.html)
* [`L.query(...traversals) ~> traversal`](#L-query "L.query: (PTraversal s1 s2, ...PTraversal sN a) ~> PTraversal JSON a") v13.6.0
* [`L.satisfying((maybeValue, index) => testable) ~> traversal`](#L-satisfying "L.satisfying: ((Maybe s, Index) -> Boolean) -> PTraversal JSON a") v13.3.0
* [`L.values ~> traversal`](#L-values "L.values: PTraversal {p: a, ...ps} a") v7.3.0
+ * [Querying](#querying)
+ * [`L.chain((value, index) => optic, optic) ~> traversal`](#L-chain "L.chain: ((a, Index) -> POptic s b) -> POptic s a -> PTraversal s b") v3.1.0
+ * [`L.choice(...optics) ~> traversal`](#L-choice "L.choice: (...POptic s a) -> PTraversal s a") v2.1.0
+ * [`L.optional ~> traversal`](#L-optional "L.optional: PTraversal a a") v3.7.0
+ * [`L.unless((maybeValue, index) => testable) ~> traversal`](#L-unless "L.unless: ((Maybe a, Index) -> Boolean) -> PTraversal a a") v12.1.0
+ * [`L.when((maybeValue, index) => testable) ~> traversal`](#L-when "L.when: ((Maybe a, Index) -> Boolean) -> PTraversal a a") v5.2.0
+ * [`L.zero ~> traversal`](#L-zero "L.zero: PTraversal s a") v6.0.0
* [Folds over traversals](#folds-over-traversals)
* [`L.all((maybeValue, index) => testable, traversal, maybeData) ~> boolean`](#L-all "L.all: ((Maybe a, Index) -> Boolean) -> PTraversal s a -> Boolean") v9.6.0
* [`L.and(traversal, maybeData) ~> boolean`](#L-and "L.and: PTraversal s Boolean -> Boolean") v9.6.0
@@ -132,6 +131,8 @@ parts. [Try Lenses!](https://calmm-js.github.io/partial.lenses/playground.html)
* [`L.foldr((value, maybeValue, index) => value, value, traversal, maybeData) ~> value`](#L-foldr "L.foldr: ((r, Maybe a, Index) -> r) -> r -> PTraversal s a -> Maybe s -> r") v7.2.0
* [`L.forEach((maybeValue, index) => undefined, traversal, maybeData) ~> undefined`](#L-forEach "L.forEach: ((Maybe a, Index) -> Undefined) -> PTraversal s a -> Maybe s -> Undefined") v11.20.0
* [`L.forEachWith(() => context, (context, maybeValue, index) => undefined, traversal, maybeData) ~> context`](#L-forEachWith "L.forEachWith: (() -> c) -> ((c, Maybe a, Index) -> Undefined) -> PTraversal s a -> Maybe s -> c") v13.4.0
+ * [`L.get(traversal, maybeData) ~> maybeValue`](#L-get "L.get: PTraversal s a -> Maybe s -> Maybe a") v2.2.0
+ * [`L.getAs((maybeValue, index) => maybeValue, traversal, maybeData) ~> maybeValue`](#L-getAs "L.getAs: ((Maybe a, Index) -> Maybe b) -> PTraversal s a -> Maybe s -> Maybe b") v14.0.0
* [`L.isDefined(traversal, maybeData) ~> boolean`](#L-isDefined "L.isDefined: PTraversal s a -> Maybe s -> Boolean") v11.8.0
* [`L.isEmpty(traversal, maybeData) ~> boolean`](#L-isEmpty "L.isEmpty: PTraversal s a -> Maybe s -> Boolean") v11.5.0
* [`L.join(string, traversal, maybeData) ~> string`](#L-join "L.join: String -> PTraversal s a -> Maybe s -> String") v11.2.0
@@ -146,13 +147,11 @@ parts. [Try Lenses!](https://calmm-js.github.io/partial.lenses/playground.html)
* [`L.or(traversal, maybeData) ~> boolean`](#L-or "L.or: PTraversal s Boolean -> Boolean") v9.6.0
* [`L.product(traversal, maybeData) ~> number`](#L-product "L.product: PTraversal s Number -> Maybe s -> Number") v7.2.0
* [`L.productAs((maybeValue, index) => number, traversal, maybeData) ~> number`](#L-productAs "L.productAs: ((Maybe a, Index) -> Number) -> PTraversal s a -> Maybe s -> Number") v11.2.0
- * [`L.select(traversal, maybeData) ~> maybeValue`](#L-select "L.select: PTraversal s a -> Maybe s -> Maybe a") v9.8.0
- * [`L.selectAs((maybeValue, index) => maybeValue, traversal, maybeData) ~> maybeValue`](#L-selectAs "L.selectAs: ((Maybe a, Index) -> Maybe b) -> PTraversal s a -> Maybe s -> Maybe b") v9.8.0
+ * ~~[`L.select(traversal, maybeData) ~> maybeValue`](#L-select "L.select: PTraversal s a -> Maybe s -> Maybe a") v9.8.0~~
+ * ~~[`L.selectAs((maybeValue, index) => maybeValue, traversal, maybeData) ~> maybeValue`](#L-selectAs "L.selectAs: ((Maybe a, Index) -> Maybe b) -> PTraversal s a -> Maybe s -> Maybe b") v9.8.0~~
* [`L.sum(traversal, maybeData) ~> number`](#L-sum "L.sum: PTraversal s Number -> Maybe s -> Number") v7.2.0
* [`L.sumAs((maybeValue, index) => number, traversal, maybeData) ~> number`](#L-sumAs "L.sumAs: ((Maybe a, Index) -> Number) -> PTraversal s a -> Maybe s -> Number") v11.2.0
* [Lenses](#lenses)
- * [Operations on lenses](#operations-on-lenses)
- * [`L.get(lens, maybeData) ~> maybeValue`](#L-get "L.get: PLens s a -> Maybe s -> Maybe a") v2.2.0
* [Creating new lenses](#creating-new-lenses)
* [`L.lens((maybeData, index) => maybeValue, (maybeValue, maybeData, index) => maybeData) ~> lens`](#L-lens "L.lens: ((Maybe s, Index) -> Maybe a) -> ((Maybe a, Maybe s, Index) -> Maybe s) -> PLens s a") v1.0.0
* [`L.getter((maybeData, index) => maybeValue) ~> lens`](#L-getter "L.getter: ((Maybe s, Index) -> Maybe a) -> PLens s a") v13.16.0
@@ -238,7 +237,7 @@ parts. [Try Lenses!](https://calmm-js.github.io/partial.lenses/playground.html)
* [`List` indexing](#list-indexing)
* [Interfacing traversals](#interfacing-traversals)
* [Deepening topics](#deepening-topics)
- * [Understanding `L.filter`, `L.find`, `L.select`, and `L.when`](#understanding-filter-find-select-and-when)
+ * [Understanding `L.filter`, `L.find`, `L.get`, and `L.when`](#understanding-filter-find-get-and-when)
* [Advanced topics](#advanced-topics)
* [Performance tips](#performance-tips)
* [Nesting traversals does not create intermediate aggregates](#nesting-traversals-does-not-create-intermediate-aggregates)
@@ -446,7 +445,8 @@ L.get(textIn('fi'), undefined)
// ''
```
-With partial lenses, `undefined` is the equivalent of non-existent.
+With partial lenses, [`undefined` is the equivalent of
+non-existent](#use-of-undefined).
#### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#updating-data) [Updating data](#updating-data)
@@ -804,12 +804,12 @@ no longer the same operation—the special value is not the first element of
the array.
Now, in partial lenses, the idea is that in case the input does not match the
-expectation of an optic, then the input is treated as being `undefined`, which
-is the equivalent of non-existent: reading through the optic gives `undefined`
-and writing through the optic replaces the focus with the written value. This
-makes the optics in this library partial and allows specific partial optics,
-such as the simple [`L.prop`](#L-prop) lens, to be used in a wider range of
-situations than corresponding total optics.
+expectation of an optic, then the [input is treated as being `undefined`, which
+is the equivalent of non-existent](#use-of-undefined): reading through the optic
+gives `undefined` and writing through the optic replaces the focus with the
+written value. This makes the optics in this library partial and allows
+specific partial optics, such as the simple [`L.prop`](#L-prop) lens, to be used
+in a wider range of situations than corresponding total optics.
Making all optics partial has a number of consequences. For one thing, it can
potentially hide bugs: an incorrectly specified optic treats the input as
@@ -931,7 +931,7 @@ behave. Here is a table of the means of composition supported by this library:
| [Nesting](#nesting) | [`L.compose(...optics)`](#L-compose) or `[...optics]` | [Monoid](https://en.wikipedia.org/wiki/Monoid) over [unityped](http://cs.stackexchange.com/questions/18847/if-dynamically-typed-languages-are-truly-statically-typed-unityped-languages-w) [optics](#optics)
| [Recursing](#recursing) | [`L.lazy(optic => optic)`](#L-lazy) | [Fixed point](https://en.wikipedia.org/wiki/Fixed-point_combinator)
| [Adapting](#adapting) | [`L.choices(optic, ...optics)`](#L-choices) | [Semigroup](https://en.wikipedia.org/wiki/Semigroup) over [optics](#optics)
-| [Querying](#querying) | [`L.choice(...optics)`](#L-choice) and [`L.chain(value => optic, optic)`](#L-chain) | [MonadPlus](https://en.wikibooks.org/wiki/Haskell/Alternative_and_MonadPlus) over [optics](#optics)
+| [Querying](#querying) | [`L.choice(...optics)`](#L-choice) and [`L.chain(value => optic, optic)`](#L-chain) | [MonadPlus](https://en.wikibooks.org/wiki/Haskell/Alternative_and_MonadPlus) over [traversals](#traversals)
| Picking | [`L.pick({...prop:lens})`](#L-pick) | Product of [lenses](#lenses)
| Branching | [`L.branch({...prop:traversal})`](#L-branch) | [Coproduct](https://en.wikipedia.org/wiki/Coproduct) of [traversals](#traversals)
| [Sequencing](#sequencing) | [`L.seq(...transforms)`](#L-seq) | Monad over [transforms](#transforms)
@@ -1394,15 +1394,33 @@ L.cond(..., [R.T, alternative])
because in the latter case `L.cond` cannot determine that a user defined
predicate will always be true and has to construct a more expensive optic.
+Note that when no `[alternative]` is specified, `L.cond` returns a
+[traversal](#traversals), because the default [`L.zero`](#L-zero) is a
+traversal.
+
Note that `L.cond` can be implemented using [`L.choose`](#L-choose), but not
vice versa. [`L.choose`](#L-choose) not only allows the optic to be chosen
dynamically, but also allows the optic to be constructed dynamically and using
the data at the focus.
-##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-condOf) L.condOf(lens, ...[(maybeValue, index) => testable, consequentOptic][, [alternativeOptic]]) ~> optic
v13.5.0
+##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-condOf) L.condOf(traversal, ...[(maybeValue, index) => testable, consequentOptic][, [alternativeOptic]]) ~> optic
v13.5.0
`L.condOf` is like [`L.cond`](#L-cond) except the first argument to `L.condOf`
-is a lens to get the parameters for the predicates from the underlying view.
+is a traversal whose focuses are tested with the predicates.
+
+```jsx
+L.condOf(traversal,
+ [ predicate, consequent ]
+ , ...
+ [ , [ alternative ] ] )
+```
+
+`L.condOf` acts like the *consequent* optic of first `[predicate, consequent]`
+pair whose *predicate* accepts [any](#L-any) focus produced by the traversal.
+The last argument to `L.condOf` can be an `[alternative]` singleton, where the
+*alternative* is an optic to be used in case none of the predicates accepts
+[any](#L-any) focus produced by the traversal. If there is no `[alternative]`
+[`L.zero`](#L-zero) is used.
For example:
@@ -1418,6 +1436,14 @@ L.get(
// 'Try writing this with `L.cond`.'
```
+Note that `L.condOf(t, [p1, o1], ..., [pN, oN], [o])` is roughly equivalent to a
+combination of [`L.any`](#L-any) and [`L.cond`](#L-cond): `L.cond([L.any(p1, t),
+o1], ..., [L.any(pN, t), oN], [o])`.
+
+Note that when no `[alternative]` is specified, `L.condOf` returns a
+[traversal](#traversals), because the default [`L.zero`](#L-zero) is a
+traversal.
+
##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-ifElse) [`L.ifElse((maybeValue, index) => testable, optic, optic) ~> optic`](#L-ifElse "L.ifElse: ((Maybe s, Index) -> Boolean) -> POptic s a -> POptic s a -> POptic s a") v13.1.0
`L.ifElse` creates an optic whose operation is selected based on the given
@@ -1437,45 +1463,6 @@ L.modify(L.ifElse(Array.isArray, L.elems, L.values), R.inc, {x: 1, y: 2, z: 3})
// { x: 2, y: 3, z: 4 }
```
-##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-iftes) ~~[`L.iftes((maybeValue, index) => testable, consequentOptic, ...[, alternativeOptic]) ~> optic`](#L-iftes "L.iftes: ((Maybe s, Index) -> Boolean) -> PLens s a -> PLens s a -> PLens s a") v11.14.0~~
-
-**WARNING: `L.iftes` has been obsoleted. Use [`L.ifElse`](#L-ifElse) or
-[`L.cond`](#L-cond) instead. See [CHANGELOG](./CHANGELOG.md#1310) for
-details.**
-
-`L.iftes` creates an optic whose operation is selected from the given optics and
-predicates on the underlying view.
-
-```jsx
-L.iftes( predicate, consequent
- [ , ... ]
- [ , alternative ] )
-```
-
-`L.iftes` is not curried unlike most functions in this library. `L.iftes`
-requires at least two arguments and successive arguments form *predicate* -
-*consequent* pairs. The predicates are functions on the underlying view and are
-tested sequentially. The consequents are optics and `L.iftes` acts like the
-consequent corresponding to the first predicate that returns true. If `L.iftes`
-is given an odd number of arguments, the last argument is the *alternative*
-taken in case none of the predicates returns true. If all predicates return
-false and there is no alternative, `L.iftes` acts like [`L.zero`](#L-zero).
-
-For example:
-
-```js
-const minorAxis = L.iftes(({x, y} = {}) => Math.abs(y) < Math.abs(x), 'y', 'x')
-
-L.get(minorAxis, {x: -3, y: 1})
-// 1
-```
-```js
-L.modify(minorAxis, R.negate, {x: -3, y: 1})
-// { x: -3, y: -1 }
-```
-
-Note that `L.iftes` can be implemented using [`L.choose`](#L-choose).
-
##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-orElse) [`L.orElse(backupOptic, primaryOptic) ~> optic`](#L-orElse "L.orElse: (POptic s a, POptic s a) -> POptic s a") v2.1.0
`L.orElse(backupOptic, primaryOptic)` acts like `primaryOptic` when its view is
@@ -1485,115 +1472,6 @@ Note that [`L.choice(...optics)`](#L-choice) is equivalent to
`optics.reduceRight(L.orElse, L.zero)` and [`L.choices(...optics)`](#L-choices)
is equivalent to `optics.reduce(L.orElse)`.
-#### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#querying) [Querying](#querying)
-
-Querying combinators allow one to use optics to query data structures. Querying
-is distinguished from [adapting](#adapting) in that querying defaults to an
-empty or read-only [zero](#L-zero).
-
-##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-chain) [`L.chain((value, index) => optic, optic) ~> optic`](#L-chain "L.chain: ((a, Index) -> POptic s b) -> POptic s a -> POptic s b") v3.1.0
-
-`L.chain` provides a monadic
-[chain](https://github.com/rpominov/static-land/blob/master/docs/spec.md#chain)
-combinator for querying with optics. `L.chain(toOptic, optic)` is equivalent to
-
-```jsx
-L.compose(
- optic,
- L.choose(
- (value, index) => value === undefined ? L.zero : toOptic(value, index)
- )
-)
-```
-
-Note that with the [`R.always`](http://ramdajs.com/docs/#always), `L.chain`,
-[`L.choice`](#L-choice) and [`L.zero`](#L-zero) combinators, one can consider
-optics as subsuming the maybe monad.
-
-##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-choice) [`L.choice(...optics) ~> optic`](#L-choice "L.choice: (...POptic s a) -> POptic s a") v2.1.0
-
-`L.choice` returns a partial optic that acts like the first of the given optics
-whose view is not `undefined` on the given data structure. When the views of
-all of the given optics are `undefined`, the returned optic acts like
-[`L.zero`](#L-zero), which is the identity element of `L.choice`. See also
-[`L.choices`](#L-choices).
-
-For example:
-
-```js
-L.modify([L.elems, L.choice('a', 'd')], R.inc, [{R: 1}, {a: 1}, {d: 2}])
-// [ { R: 1 }, { a: 2 }, { d: 3 } ]
-```
-
-##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-optional) [`L.optional ~> optic`](#L-optional "L.optional: POptic a a") v3.7.0
-
-`L.optional` is an optic over an optional element. When used as a traversal,
-and the focus is `undefined`, the traversal is empty. When used as a lens, and
-the focus is `undefined`, the lens will be read-only.
-
-As an example, consider the difference between:
-
-```js
-L.set([L.elems, 'x'], 3, [{x: 1}, {y: 2}])
-// [ { x: 3 }, { y: 2, x: 3 } ]
-```
-
-and:
-
-```js
-L.set([L.elems, 'x', L.optional], 3, [{x: 1}, {y: 2}])
-// [ { x: 3 }, { y: 2 } ]
-```
-
-Note that `L.optional` is equivalent to [`L.when(x => x !==
-undefined)`](#L-when).
-
-##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-unless) [`L.unless((maybeValue, index) => testable) ~> optic`](#L-unless "L.unless: ((Maybe a, Index) -> Boolean) -> POptic a a") v12.1.0
-
-`L.unless` allows one to selectively skip elements within a traversal or to
-selectively turn a lens into a read-only lens whose view is `undefined`. See
-also [`L.when`](#L-when).
-
-For example:
-
-```js
-L.modify([L.elems, L.unless(x => x < 0)], R.negate, [0, -1, 2, -3, 4])
-// [ -0, -1, -2, -3, -4 ]
-```
-
-##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-when) [`L.when((maybeValue, index) => testable) ~> optic`](#L-when "L.when: ((Maybe a, Index) -> Boolean) -> POptic a a") v5.2.0
-
-`L.when` allows one to selectively skip elements within a traversal or to
-selectively turn a lens into a read-only lens whose view is `undefined`. See
-also [`L.unless`](#L-unless).
-
-For example:
-
-```js
-L.modify([L.elems, L.when(x => x > 0)], R.negate, [0, -1, 2, -3, 4])
-// [ 0, -1, -2, -3, -4 ]
-```
-
-Note that `L.when(p)` is equivalent to [`L.choose((x, i) => p(x, i) ?
-L.identity : L.zero)`](#L-choose).
-
-##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-zero) [`L.zero ~> optic`](#L-zero "L.zero: POptic s a") v6.0.0
-
-`L.zero` is the identity element of [`L.choice`](#L-choice) and
-[`L.chain`](#L-chain). As a traversal, `L.zero` is a traversal of no elements
-and as a lens, i.e. when used with [`L.get`](#L-get), `L.zero` is a read-only
-lens whose view is always `undefined`.
-
-For example:
-
-```js
-L.collect(
- [L.elems, L.cond([R.is(Array), L.elems], [R.is(Object), 'x'], [L.zero])],
- [1, {x: 2}, [3, 4]]
-)
-// [ 2, 3, 4 ]
-```
-
#### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#indices) [Indices](#indices)
The indexing combinators allow one to manipulate the indices passed down by
@@ -1709,9 +1587,9 @@ returned. `L.getLog`, like [`L.log`](#L-log), is intended for debugging.
For example:
```js
-L.getLog(['x', 0, 'y'], {x: [{y: 101}]})
-// { x: [ { y: 101 } ] } <= [ { y: 101 } ] <= { y: 101 } <= 101
-// 101
+L.getLog(['data', L.elems, 'y'], {data: [{x: 1}, {y: 2}]})
+// { data: [ { x: 1 }, { y: 2 } ] } <= [ { x: 1 }, { y: 2 } ] <= { y: 2 } <= 2
+// 2
```
(If you are looking at the above snippet in the interactive version of this
@@ -1747,14 +1625,6 @@ L.set(['x', L.log('%s x: %j')], '11', {x: 10})
#### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#internals) [Internals](#internals)
-##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-Constant) [`L.Constant ~> Functor`](#L-Constant "L.Constant: Functor") v13.7.0
-
-`L.Constant` is the [Static
-Land](https://github.com/rpominov/static-land/blob/master/docs/spec.md)
-compatible constant
-[`Functor`](https://github.com/rpominov/static-land/blob/master/docs/spec.md#functor)
-definition used by Partial Lenses.
-
##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-Identity) [`L.Identity ~> Monad`](#L-Identity "L.Identity: Monad") v13.7.0
`L.Identity` is the [Static
@@ -1772,6 +1642,42 @@ monad](https://buzzdecafe.github.io/2018/04/10/no-promises-are-not-monads),
which explains the "monadish". Fortunately one usually does not want nested
promises in which case the approximation can be close enough.
+##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-Constant) [`L.Select ~> Applicative`](#L-Select "L.Select: Applicative") v14.0.0
+
+`L.Select` is the [Static
+Land](https://github.com/rpominov/static-land/blob/master/docs/spec.md)
+compatible
+[`Applicative`](https://github.com/rpominov/static-land/blob/master/docs/spec.md#applicative)
+definition that extends the constant functor to select the first non-`undefined`
+element.
+
+The basis for `Select` is the following
+[monoid](https://github.com/rpominov/static-land/blob/master/docs/spec.md#monoid)
+over JavaScript values:
+
+```js
+const Defined = {
+ empty: _ => undefined,
+ concat: (l, r) => l !== undefined ? l : r
+}
+```
+
+It is a monoid, because it satisfies the Monoid laws:
+
+```js
+const MonoidLaws = (M, x, y, z) => ({
+ associativity: test(M.concat(M.concat(x, y), z), M.concat(x, M.concat(y, z))),
+ leftIdentity: test(M.concat(M.empty(), x), x) ,
+ rightIdentity: test(M.concat(x, M.empty()), x)
+})
+
+MonoidLaws(Defined, {Try: 'any'}, 'JavaScript', ['values'])
+// {associativity: true, leftIdentity: true, rightIdentity: true}
+```
+
+In Partial Lenses [`undefined` is used to representing
+nothingness](#use-of-undefined).
+
##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-toFunction) [`L.toFunction(optic) ~> optic`](#L-toFunction "L.toFunction: POptic s t a b -> (Maybe s, Index, (Functor|Applicative|Monad) c, (Maybe a, Index) -> c b) -> c t") v7.0.0
`L.toFunction` converts a given optic, which can be a [string](#L-prop), an
@@ -1953,10 +1859,12 @@ which is not the same as the [querying monad](#L-chain).
#### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#transforming) [Transforming](#transforming)
-##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-assignOp) [`L.assignOp(object) ~> optic`](#L-assignOp "L.assignOp: {p1: a1, ...ps} -> POptic {p1: a1, ...ps, ...o} {p1: a1, ...ps}") v11.13.0
+##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-assignOp) [`L.assignOp(object) ~> traversal`](#L-assignOp "L.assignOp: {p1: a1, ...ps} -> PTraversal {p1: a1, ...ps, ...o} {p1: a1, ...ps}") v11.13.0
-`L.assignOp` creates an optic that merges the given object into the object in
-focus.
+`L.assignOp` creates a transform that merges the given object into the object in
+focus. When used as a traversal, `L.assignOp` acts as a traversal of no
+elements. Usually, however, `L.assignOp` is used within
+[transforms](#transforms).
For example:
@@ -1965,11 +1873,10 @@ L.transform([L.elems, L.assignOp({y: 1})], [{x: 3}, {x: 4, y: 5}])
// [ { x: 3, y: 1 }, { x: 4, y: 1 } ]
```
-##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-modifyOp) [`L.modifyOp((maybeValue, index) => maybeValue) ~> optic`](#L-modifyOp "L.modifyOp: ((Maybe a, Index) -> Maybe a) -> POptic a a") v11.7.0
+##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-modifyOp) [`L.modifyOp((maybeValue, index) => maybeValue) ~> traversal`](#L-modifyOp "L.modifyOp: ((Maybe a, Index) -> Maybe a) -> PTraversal a a") v11.7.0
-`L.modifyOp` creates an optic that maps the focus with the given function. When
-used as a traversal, `L.modifyOp` acts as a traversal of no elements. When used
-as a lens, `L.modifyOp` acts as a read-only lens whose view is the mapped focus.
+`L.modifyOp` creates a transform that maps the focus with the given function.
+When used as a traversal, `L.modifyOp` acts as a traversal of no elements.
Usually, however, `L.modifyOp` is used within [transforms](#transforms).
For example:
@@ -1987,7 +1894,7 @@ L.transform(
// ys: [ 0, 1, 2 ] }
```
-##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-removeOp) [`L.removeOp ~> optic`](#L-removeOp "L.removeOp: POptic a a") v11.7.0
+##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-removeOp) [`L.removeOp ~> traversal`](#L-removeOp "L.removeOp: PTraversal a a") v11.7.0
`L.removeOp` is shorthand for [`L.setOp(undefined)`](#L-setOp).
@@ -2020,7 +1927,7 @@ L.transform(
The idea is to filter the data both by `time` and by `subelements`.
-##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-setOp) [`L.setOp(maybeValue) ~> optic`](#L-setOp "L.setOp: Maybe a -> POptic a a") v11.7.0
+##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-setOp) [`L.setOp(maybeValue) ~> traversal`](#L-setOp "L.setOp: Maybe a -> PTraversal a a") v11.7.0
`L.setOp(x)` is shorthand for [`L.modifyOp(R.always(x))`](#L-modifyOp).
@@ -2270,10 +2177,10 @@ L.modify(
// { language: 'sv', text: 'Rubrik' } ] }
```
-And one can also view the text of a specific language using [select](#L-select):
+And one can also view the text of a specific language:
```js
-L.select(L.query(L.when(R.propEq('language', 'sv')), 'text'), sampleTitles)
+L.get(L.query(L.when(R.propEq('language', 'sv')), 'text'), sampleTitles)
// 'Rubrik'
```
@@ -2332,6 +2239,111 @@ L.modify([L.rewrite(objectTo(XYZ)), L.values], R.negate, new XYZ(1, 2, 3))
Note that `L.values` is equivalent to [`L.branchOr([], {})`](#L-branchOr).
+#### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#querying) [Querying](#querying)
+
+Querying combinators allow one to use optics to query data structures. Querying
+is distinguished from [adapting](#adapting) in that querying defaults to an
+empty or read-only [zero](#L-zero).
+
+##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-chain) [`L.chain((value, index) => optic, optic) ~> traversal`](#L-chain "L.chain: ((a, Index) -> POptic s b) -> POptic s a -> PTraversal s b") v3.1.0
+
+`L.chain` provides a monadic
+[chain](https://github.com/rpominov/static-land/blob/master/docs/spec.md#chain)
+combinator for querying with optics. `L.chain(toOptic, optic)` is equivalent to
+
+```jsx
+L.compose(
+ optic,
+ L.choose(
+ (value, index) => value === undefined ? L.zero : toOptic(value, index)
+ )
+)
+```
+
+Note that with the [`R.always`](http://ramdajs.com/docs/#always), `L.chain`,
+[`L.choice`](#L-choice) and [`L.zero`](#L-zero) combinators, one can consider
+optics as subsuming the maybe monad.
+
+##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-choice) [`L.choice(...optics) ~> traversal`](#L-choice "L.choice: (...POptic s a) -> PTraversal s a") v2.1.0
+
+`L.choice` returns a partial optic that acts like the first of the given optics
+whose view is not `undefined` on the given data structure. When the views of
+all of the given optics are `undefined`, the returned optic acts like
+[`L.zero`](#L-zero), which is the identity element of `L.choice`. See also
+[`L.choices`](#L-choices).
+
+For example:
+
+```js
+L.modify([L.elems, L.choice('a', 'd')], R.inc, [{R: 1}, {a: 1}, {d: 2}])
+// [ { R: 1 }, { a: 2 }, { d: 3 } ]
+```
+
+##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-optional) [`L.optional ~> traversal`](#L-optional "L.optional: PTraversal a a") v3.7.0
+
+`L.optional` is an optic over an optional element. When used as a traversal,
+and the focus is `undefined`, the traversal is empty. When used as a lens, and
+the focus is `undefined`, the lens will be read-only.
+
+As an example, consider the difference between:
+
+```js
+L.set([L.elems, 'x'], 3, [{x: 1}, {y: 2}])
+// [ { x: 3 }, { y: 2, x: 3 } ]
+```
+
+and:
+
+```js
+L.set([L.elems, 'x', L.optional], 3, [{x: 1}, {y: 2}])
+// [ { x: 3 }, { y: 2 } ]
+```
+
+Note that `L.optional` is equivalent to [`L.when(x => x !==
+undefined)`](#L-when).
+
+##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-unless) [`L.unless((maybeValue, index) => testable) ~> traversal`](#L-unless "L.unless: ((Maybe a, Index) -> Boolean) -> PTraversal a a") v12.1.0
+
+`L.unless` allows one to selectively skip elements within a traversal. See also
+[`L.when`](#L-when).
+
+For example:
+
+```js
+L.modify([L.elems, L.unless(x => x < 0)], R.negate, [0, -1, 2, -3, 4])
+// [ -0, -1, -2, -3, -4 ]
+```
+
+##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-when) [`L.when((maybeValue, index) => testable) ~> traversal`](#L-when "L.when: ((Maybe a, Index) -> Boolean) -> PTraversal a a") v5.2.0
+
+`L.when` allows one to selectively skip elements within a traversal. See also
+[`L.unless`](#L-unless).
+
+For example:
+
+```js
+L.modify([L.elems, L.when(x => x > 0)], R.negate, [0, -1, 2, -3, 4])
+// [ 0, -1, -2, -3, -4 ]
+```
+
+Note that `L.when(p)` is equivalent to [`L.choose((x, i) => p(x, i) ?
+L.identity : L.zero)`](#L-choose).
+
+##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-zero) [`L.zero ~> traversal`](#L-zero "L.zero: PTraversal s a") v6.0.0
+
+`L.zero` is a traversal of no elements and is the identity element of
+[`L.choice`](#L-choice) and [`L.chain`](#L-chain).
+
+For example:
+
+```js
+L.collect(
+ [L.elems, L.cond([R.is(Array), L.elems], [R.is(Object), 'x'], [L.zero])],
+ [1, {x: 2}, [3, 4]]
+)
+// [ 2, 3, 4 ]
+```
+
#### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#folds-over-traversals) [Folds over traversals](#folds-over-traversals)
##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-all) [`L.all((maybeValue, index) => testable, traversal, maybeData) ~> boolean`](#L-all "L.all: ((Maybe a, Index) -> Boolean) -> PTraversal s a -> Boolean") v9.6.0
@@ -2351,7 +2363,7 @@ L.all(
```
See also: [`L.any`](#L-any), [`L.none`](#L-none), and
-[`L.selectAs`](#L-selectAs).
+[`L.getAs`](#L-getAs).
##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-and) [`L.and(traversal, maybeData) ~> boolean`](#L-and "L.and: PTraversal s Boolean -> Boolean") v9.6.0
@@ -2381,7 +2393,7 @@ L.any(x => x > 5, primitives, [[[1], 2], {y: 3}, [{l: 4, r: [5]}, {x: 6}]])
```
See also: [`L.all`](#L-all), [`L.none`](#L-none), and
-[`L.selectAs`](#L-selectAs).
+[`L.getAs`](#L-getAs).
##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-collect) [`L.collect(traversal, maybeData) ~> [...values]`](#L-collect "L.collect: PTraversal s a -> Maybe s -> [a]") v3.6.0
@@ -2588,6 +2600,58 @@ L.forEachWith(() => new Map(), (m, v, k) => m.set(k, v), L.values, {x: 2, y: 1})
Note that a new `Map` is returned each time the above expression is evaluated.
+##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-get) [`L.get(traversal, maybeData) ~> maybeValue`](#L-get "L.get: PTraversal s a -> Maybe s -> Maybe a") v9.8.0
+
+`L.get` returns the element focused on by a [lens](#lenses) from a data
+structure or goes lazily over the elements focused on by the given
+[traversal](#traversals) and returns the first non-`undefined` element. See
+also [`L.getLog`](#L-getLog).
+
+For example:
+
+```js
+L.get('y', {x: 112, y: 101})
+// 101
+```
+
+```js
+L.get([L.elems, 'y'], [{x:1}, {y:2}, {z:3}])
+// 2
+```
+
+Note that `L.get` is equivalent to [`L.getAs(x => x)`](#L-getAs).
+
+##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-getAs) [`L.getAs((maybeValue, index) => maybeValue, traversal, maybeData) ~> maybeValue`](#L-getAs "L.getAs: ((Maybe a, Index) -> Maybe b) -> PTraversal s a -> Maybe s -> Maybe b") v14.0.0
+
+`L.getAs` goes lazily over the elements focused on by the given traversal,
+applying the given function to each element, and returns the first
+non-`undefined` value returned by the function.
+
+```js
+L.getAs(x => x > 3 ? -x : undefined, L.elems, [3, 1, 4, 1, 5])
+// -4
+```
+
+`L.getAs` operates lazily. The user specified function is only applied to
+elements until the first non-`undefined` value is returned and after that
+`L.getAs` returns without examining more elements.
+
+Note that `L.getAs` can be used to implement many other operations over
+traversals such as finding an element matching a predicate and checking whether
+all/any elements match a predicate. For example, here is how you could
+implement a for all predicate over traversals:
+
+```js
+const all = (p, t, s) => !L.getAs(x => p(x) ? undefined : true, t, s)
+```
+
+Now:
+
+```js
+all(x => x < 9, primitives, [[[1], 2], {y: 3}, [{l: 4, r: [5]}, {x: 6}]])
+// true
+```
+
##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-isDefined) [`L.isDefined(traversal, maybeData) ~> boolean`](#L-isDefined "L.isDefined: PTraversal s a -> Maybe s -> Boolean") v11.8.0
`L.isDefined` determines whether or not the given traversal focuses on any
@@ -2736,7 +2800,7 @@ L.none(x => x > 5, primitives, [[[1], 2], {y: 3}, [{l: 4, r: [5]}, {x: 6}]])
// false
```
-See also: [`L.all`](#L-all), [`L.any`](#L-any), and [`L.selectAs`](#L-selectAs).
+See also: [`L.all`](#L-all), [`L.any`](#L-any), and [`L.getAs`](#L-getAs).
##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-or) [`L.or(traversal, maybeData) ~> boolean`](#L-or "L.or: PTraversal s Boolean -> Boolean") v9.6.0
@@ -2781,7 +2845,10 @@ Note that unlike many other folds, `L.productAs` expects the function to only
return numbers and `undefined` is not treated in a special way. If you need to
skip elements, you can return the number `1`.
-##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-select) [`L.select(traversal, maybeData) ~> maybeValue`](#L-select "L.select: PTraversal s a -> Maybe s -> Maybe a") v9.8.0
+##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-select) ~~[`L.select(traversal, maybeData) ~> maybeValue`](#L-select "L.select: PTraversal s a -> Maybe s -> Maybe a") v9.8.0~~
+
+**WARNING: `L.select` has been obsoleted. Just use [`L.get`](#L-get). See
+[CHANGELOG](./CHANGELOG.md#1400) for details.**
`L.select` goes lazily over the elements focused on by the given traversal and
returns the first non-`undefined` element.
@@ -2793,7 +2860,10 @@ L.select([L.elems, 'y'], [{x:1}, {y:2}, {z:3}])
Note that `L.select` is equivalent to [`L.selectAs(x => x)`](#L-selectAs).
-##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-selectAs) [`L.selectAs((maybeValue, index) => maybeValue, traversal, maybeData) ~> maybeValue`](#L-selectAs "L.selectAs: ((Maybe a, Index) -> Maybe b) -> PTraversal s a -> Maybe s -> Maybe b") v9.8.0
+##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-selectAs) ~~[`L.selectAs((maybeValue, index) => maybeValue, traversal, maybeData) ~> maybeValue`](#L-selectAs "L.selectAs: ((Maybe a, Index) -> Maybe b) -> PTraversal s a -> Maybe s -> Maybe b") v9.8.0~~
+
+**WARNING: `L.selectAs` has been obsoleted. Just use [`L.getAs`](#L-getAs).
+See [CHANGELOG](./CHANGELOG.md#1400) for details.**
`L.selectAs` goes lazily over the elements focused on by the given traversal,
applying the given function to each element, and returns the first
@@ -2856,22 +2926,6 @@ elements, you can return the number `0`.
Lenses always have a single focus which can be [viewed](#L-get) directly. Put
in another way, a lens specifies a path to a single element in a data structure.
-#### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#operations-on-lenses) [Operations on lenses](#operations-on-lenses)
-
-##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-get) [`L.get(lens, maybeData) ~> maybeValue`](#L-get "L.get: PLens s a -> Maybe s -> Maybe a") v2.2.0
-
-`L.get` returns the element focused on by a [lens](#lenses) from a data
-structure.
-
-For example:
-
-```js
-L.get('y', {x: 112, y: 101})
-// 101
-```
-
-Note that `L.get` does not work on [traversals](#traversals).
-
#### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#creating-new-lenses) [Creating new lenses](#creating-new-lenses)
##### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-lens) [`L.lens((maybeData, index) => maybeValue, (maybeValue, maybeData, index) => maybeData) ~> lens`](#L-lens "L.lens: ((Maybe s, Index) -> Maybe a) -> ((Maybe a, Maybe s, Index) -> Maybe s) -> PLens s a") v1.0.0
@@ -4515,9 +4569,9 @@ L.joinAs(
## [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#deepening-topics) [Deepening topics](#deepening-topics)
-### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#understanding-filter-find-select-and-when) [Understanding `L.filter`, `L.find`, `L.select`, and `L.when`](#understanding-filter-find-select-and-when)
+### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#understanding-filter-find-select-and-when) [Understanding `L.filter`, `L.find`, `L.get`, and `L.when`](#understanding-filter-find-get-and-when)
-The [`L.filter`](#L-filter), [`L.find`](#L-find), [`L.select`](#L-select), and
+The [`L.filter`](#L-filter), [`L.find`](#L-find), [`L.get`](#L-get), and
[`L.when`](#L-when) serve related, but different, purposes and it is important
to understand their differences in order to make best use of them.
@@ -4527,24 +4581,23 @@ Here is a table of their call patterns and type signatures:
| ------------------------------------------ | ----------------------------------------------------------
| `L.filter((value, index) => bool) ~> lens` | `L.filter: ((Maybe a, Index) -> Boolean) -> PLens [a] [a]`
| `L.find((value, index) => bool) ~> lens` | `L.find: ((Maybe a, Index) -> Boolean) -> PLens [a] a`
-| `L.select(traversal, data) ~> value` | `L.select: PTraversal s a -> Maybe s -> Maybe a`
+| `L.get(traversal, data) ~> value` | `L.get: PTraversal s a -> Maybe s -> Maybe a`
| `L.when((value, index) => bool) ~> optic` | `L.when: ((Maybe a, Index) -> Boolean) -> POptic a a`
As can be read from above, both [`L.filter`](#L-filter) and [`L.find`](#L-find)
-introduce lenses, [`L.select`](#L-select) eliminates a traversal, and
+introduce lenses, [`L.get`](#L-get) eliminates a traversal, and
[`L.when`](#L-when) introduces an optic, which will always be a traversal in
this section. We can also read that [`L.filter`](#L-filter) and
-[`L.find`](#L-find) operate on arrays, while [`L.select`](#L-select) and
+[`L.find`](#L-find) operate on arrays, while [`L.get`](#L-get) and
[`L.when`](#L-when) operate on arbitrary traversals. Yet another thing to make
-note of is that both [`L.find`](#L-find) and [`L.select`](#L-select) are
-many-to-one while both [`L.filter`](#L-filter) and [`L.when`](#L-when) retain
-cardinality.
+note of is that both [`L.find`](#L-find) and [`L.get`](#L-get) are many-to-one
+while both [`L.filter`](#L-filter) and [`L.when`](#L-when) retain cardinality.
The following equations relate the operations in the read direction:
```jsx
L.get([L.filter(p), 0]) = L.get(L.find(p))
- L.select([L.elems, L.when(p)]) = L.get(L.find(p))
+ L.get([L.elems, L.when(p)]) = L.get(L.find(p))
L.collect([L.elems, L.when(p)]) = L.get(L.filter(p))
```
@@ -4555,7 +4608,7 @@ an array identified by a given predicate. Despite the name, [`L.find`](#L-find)
is probably not what one should use to generally search for something in a data
structure.
-[`L.select`](#L-select) (and [`L.selectAs`](#L-selectAs)) can be used to search
+[`L.get`](#L-get) (and [`L.getAs`](#L-getAs)) can be used to search
for an element in a data structure following an arbitrary traversal. That
traversal can, of course, also make use of [`L.when`](#L-when) to filter elements
or to limit the traversal.
@@ -4752,16 +4805,38 @@ collections and back).
#### [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#use-of-undefined) [Use of `undefined`](#use-of-undefined)
-`undefined` is a natural choice in JavaScript, especially when dealing with
-JSON, to represent nothingness. Some libraries use `null`, but that is arguably
-a poor choice, because `null` is a valid JSON value. Some libraries implement
-special `Maybe` types, but the benefits do not seem worth the trouble. First of
-all, `undefined` already exists in JavaScript and is not a valid JSON value.
-Inventing a new value to represent nothingness doesn't seem to add much. OTOH,
-wrapping values with `Just` objects introduces a significant performance
-overhead due to extra allocations. Operations with optics do not otherwise
-necessarily require large numbers of allocations and can be made highly
-efficient.
+`undefined` is arguably a natural choice in JavaScript to represent nothingness:
+
+* `undefined` is the result of an attempt to access non-existent properties of
+ objects.
+* `undefined` is the result of functions that do not explicitly return another
+ value.
+* `undefined` is not a valid JSON value and does not get mixed up with valid
+ JSON values.
+* We can form a [monoid over JavaScript values by treating `undefined` as
+ zero](#L-Select).
+
+Some libraries use `null`, but that is arguably a poor choice, because `null` is
+a valid JSON value, which means that when accessing JSON data a result of `null`
+is ambiguous.
+
+One downside of using `undefined` is that it can sometimes be a valid value.
+Fortunately this is fairly rarely the case so inventing a new value to represent
+nothingness doesn't seem to add much.
+
+Some libraries implement special `Maybe` types, but the benefits do not seem
+worth the trouble or the disadvantages in this context. The main disadvantage
+is that wrapping values with `Just` objects introduces a significant performance
+overhead due to extra allocations, because operations with optics do not
+otherwise necessarily require large numbers of allocations and can be made
+highly efficient. Also, a `Maybe`
+[monad](https://github.com/rpominov/static-land/blob/master/docs/spec.md#monad)
+is not necessary for optics. A
+[monoid](https://github.com/rpominov/static-land/blob/master/docs/spec.md#monoid)
+is sufficient for optics based on
+[applicatives](https://github.com/rpominov/static-land/blob/master/docs/spec.md#applicative),
+because applicatives do not have a join operation and are not nested like
+monads.
Not having an explicit `Just` object means that dealing with values such as
`Just Nothing` requires special consideration.
@@ -5024,10 +5099,10 @@ pinch of salt and preferably try and measure your actual use cases!
200,548/s 19.27 O.Setter.set(o_x_y_z, 0, xyzn)
1,280,471/s 1.00 R.find(x => x > 3, xs100)
- 1,066,129/s 1.20 L.selectAs(x => x > 3 ? x : undefined, L.elems, xs100)
+ 1,066,129/s 1.20 L.getAs(x => x > 3 ? x : undefined, L.elems, xs100)
2,529/s 506.25 O.Fold.findOf(O.Traversal.traversed, x => x > 3, xs100)
- 9,325,674/s 1.00 L.selectAs(x => x < 3 ? x : undefined, L.elems, xs100)
+ 9,325,674/s 1.00 L.getAs(x => x < 3 ? x : undefined, L.elems, xs100)
4,411,876/s 2.11 R.find(x => x < 3, xs100)
2,473/s 3770.86 O.Fold.findOf(O.Traversal.traversed, x => x < 3, xs100) -- NO SHORTCUT EVALUATION
@@ -5129,7 +5204,7 @@ pinch of salt and preferably try and measure your actual use cases!
49,965/s 1.00 L.modify(L.flatten, inc, xsss100)
- 7,833,540/s 1.00 L.selectAs(x => x > 3 ? x : undefined, L.elems, pi)
+ 7,833,540/s 1.00 L.getAs(x => x > 3 ? x : undefined, L.elems, pi)
4,448,086/s 1.76 R.find(x => x > 3, pi)
32,770/s 239.05 O.Fold.findOf(O.Traversal.traversed, x => x > 3, pi)
diff --git a/bench/bench.js b/bench/bench.js
index 20fe5959..9cbee888 100644
--- a/bench/bench.js
+++ b/bench/bench.js
@@ -442,12 +442,12 @@ R.forEach(
`U.set(['x', 'y', 'z'], 0, xyzn)`
],
[
- `L.selectAs(x => x > 3 ? x : undefined, L.elems, xs100)`,
+ `L.getAs(x => x > 3 ? x : undefined, L.elems, xs100)`,
`R.find(x => x > 3, xs100)`,
`O.Fold.findOf(O.Traversal.traversed, x => x > 3, xs100)`
],
[
- `L.selectAs(x => x < 3 ? x : undefined, L.elems, xs100)`,
+ `L.getAs(x => x < 3 ? x : undefined, L.elems, xs100)`,
`R.find(x => x < 3, xs100)`,
[
`O.Fold.findOf(O.Traversal.traversed, x => x < 3, xs100)`,
@@ -577,7 +577,7 @@ R.forEach(
`L.traverse(Ident, inc, L.leafs, xsss100)`
],
[
- `L.selectAs(x => x > 3 ? x : undefined, L.elems, pi)`,
+ `L.getAs(x => x > 3 ? x : undefined, L.elems, pi)`,
`R.find(x => x > 3, pi)`,
`O.Fold.findOf(O.Traversal.traversed, x => x > 3, pi)`
],
diff --git a/src/partial.lenses.js b/src/partial.lenses.js
index 6bef162a..20fba0e3 100644
--- a/src/partial.lenses.js
+++ b/src/partial.lenses.js
@@ -50,6 +50,8 @@ const singletonPartial = x => (void 0 !== x ? [x] : x)
const expect = (p, f) => copyName(x => (p(x) ? f(x) : void 0), f)
+const freezeInDev = process.env.NODE_ENV === 'production' ? id : I.freeze
+
function deepFreeze(x) {
if (I.isArray(x)) {
x.forEach(deepFreeze)
@@ -156,15 +158,26 @@ function selectInArrayLike(xi2v, xs) {
//
-const Select = {
- map: I.sndU,
- of: () => {},
- ap: (l, r) => (void 0 !== l ? l : r)
+function Applicative(map, of, ap) {
+ if (!this) return freezeInDev(new Applicative(map, of, ap))
+ this.map = map
+ this.of = of
+ this.ap = ap
}
-const ConcatOf = (ap, empty) => ({map: I.sndU, ap, of: I.always(empty)})
+const Monad = I.inherit(function Monad(map, of, ap, chain) {
+ if (!this) return freezeInDev(new Monad(map, of, ap, chain))
+ Applicative.call(this, map, of, ap)
+ this.chain = chain
+}, Applicative)
+
+//
+
+const ConstantWith = (ap, empty) => Applicative(I.sndU, I.always(empty), ap)
+
+const ConstantOf = ({concat, empty}) => ConstantWith(concat, empty())
-const Sum = ConcatOf(I.addU, 0)
+const Sum = ConstantWith(I.addU, 0)
const mumBy = ord =>
I.curry(function mumBy(xi2y, t, s) {
@@ -264,15 +277,6 @@ const reqMaybeArray = msg => zs => {
//
-const reqApplicative = (name, arg) => C => {
- if (!C.of)
- errorGiven(
- `\`${name}${arg ? `(${arg})` : ''}\` requires an applicative`,
- C,
- 'Note that you cannot `get` a traversal. Perhaps you wanted to `collect` it?'
- )
-}
-
const reqMonad = name => C => {
if (!C.chain)
errorGiven(
@@ -325,10 +329,18 @@ function traversePartialIndex(A, xi2yA, xs, skip) {
//
-const ConstantLog = {
- map: (f, {m, p, c}) => ({m: `%O <= ${m}`, p: [f(p[0]), p], c})
-}
-const getLogFn = x => ({m: '%O', p: [x, consExcept], c: x})
+const SelectLog = Applicative(
+ (f, {p, x, c}) => {
+ x = f(x)
+ if (!I.isFunction(x)) p = [x, p]
+ return {p, x, c}
+ },
+ x => ({p: [], x, c: undefined}),
+ (l, r) => {
+ const v = undefined !== l.c ? l : r
+ return {p: v.p, x: l.x(r.x), c: v.c}
+ }
+)
//
@@ -428,40 +440,17 @@ const modifyU = (process.env.NODE_ENV === 'production'
const modifyAsyncU = (o, f, s) =>
returnAsync(toFunction(o)(s, void 0, IdentityAsync, f))
-function makeIx(i) {
- const ix = (s, j) => ((ix.v = j), s)
- ix.v = i
- return ix
-}
-
-function getNestedU(l, s, j, ix) {
- for (let n = l.length, o; j < n; ++j)
- switch (typeof (o = l[j])) {
- case 'string':
- s = getProp((ix.v = o), s)
- break
- case 'number':
- s = getIndex((ix.v = o), s)
- break
- case 'object':
- s = getNestedU(o, s, 0, ix)
- break
- default:
- s = o(s, ix.v, Constant, ix)
- }
- return s
-}
-
-const getU = (process.env.NODE_ENV === 'production'
+const getAsU = (process.env.NODE_ENV === 'production'
? id
- : C.par(0, C.ef(reqOptic)))((l, s) => {
+ : C.par(1, C.ef(reqOptic)))(function getAs(xi2y, l, s) {
switch (typeof l) {
case 'string':
- return getProp(l, s)
+ return xi2y(getProp(l, s), l)
case 'number':
- return getIndex(l, s)
- case 'object':
- for (let i = 0, n = l.length, o; i < n; ++i)
+ return xi2y(getIndex(l, s), l)
+ case 'object': {
+ const n = l.length
+ for (let i = 0, o; i < n; ++i)
switch (typeof (o = l[i])) {
case 'string':
s = getProp(o, s)
@@ -470,11 +459,14 @@ const getU = (process.env.NODE_ENV === 'production'
s = getIndex(o, s)
break
default:
- return getNestedU(l, s, i, makeIx(l[i - 1]))
+ return composed(i, l)(s, l[i - 1], Select, xi2y)
}
- return s
+ return xi2y(s, l[n - 1])
+ }
default:
- return l(s, void 0, Constant, id)
+ return xi2y !== id && l.length !== 4
+ ? xi2y(l(s, void 0), void 0)
+ : l(s, void 0, Select, xi2y)
}
})
@@ -528,7 +520,7 @@ const getPick = (process.env.NODE_ENV === 'production' ? id : C.res(I.freeze))(
let r
for (const k in template) {
const t = template[k]
- const v = I.isObject(t) ? getPick(t, x) : getU(t, x)
+ const v = I.isObject(t) ? getPick(t, x) : getAsU(id, t, x)
if (void 0 !== v) {
if (!r) r = {}
r[k] = v
@@ -858,10 +850,7 @@ const orElseU = function orElse(back, prim) {
}
}
-function zeroOp(y, i, C, xi2yC, x) {
- const of = C.of
- return of ? of(y) : C.map(I.always(y), xi2yC(x, i))
-}
+const zero = (x, _i, C, _xi2yC) => C.of(x)
//
@@ -883,9 +872,6 @@ const pickInAux = (t, k) => [k, pickIn(t)]
//
-const condOfDefault = I.always(zeroOp)
-const condOfCase = (p, o, r) => (y, j) => (p(y, j) ? o : r(y, j))
-
// Auxiliary
export const seemsArrayLike = x =>
@@ -894,30 +880,17 @@ export const seemsArrayLike = x =>
// Internals
-export const Identity = (process.env.NODE_ENV === 'production' ? id : I.freeze)(
- {
- map: I.applyU,
- of: id,
- ap: I.applyU,
- chain: I.applyU
- }
-)
+export const Identity = Monad(I.applyU, id, I.applyU, I.applyU)
-export const IdentityAsync = (process.env.NODE_ENV === 'production'
- ? id
- : I.freeze)({
- map: chainAsync,
- ap: (xyP, xP) => chainAsync(xP => chainAsync(xyP => xyP(xP), xyP), xP),
- of: id,
- chain: chainAsync
-})
-
-export const Constant = (process.env.NODE_ENV === 'production' ? id : I.freeze)(
- {
- map: I.sndU
- }
+export const IdentityAsync = Monad(
+ chainAsync,
+ id,
+ (xyP, xP) => chainAsync(xP => chainAsync(xyP => xyP(xP), xyP), xP),
+ chainAsync
)
+export const Select = ConstantWith((l, r) => (void 0 !== l ? l : r))
+
export const toFunction = (process.env.NODE_ENV === 'production'
? id
: C.par(0, C.ef(reqOptic)))(function toFunction(o) {
@@ -1017,17 +990,39 @@ export const condOf = (process.env.NODE_ENV === 'production'
return fn(of, ...cs)
})(function condOf(of) {
of = toFunction(of)
- let op = condOfDefault
- let n = arguments.length
- while (--n) {
- const c = arguments[n]
- op =
- c.length === 1
- ? I.always(toFunction(c[0]))
- : condOfCase(c[0], toFunction(c[1]), op)
+
+ let n = arguments.length - 1
+ if (!n) return zero
+
+ let def = arguments[n]
+ if (def.length === 1) {
+ --n
+ def = toFunction(def[0])
+ } else {
+ def = zero
}
- return function condOf(x, i, C, xi2yC) {
- return of(x, i, Constant, op)(x, i, C, xi2yC)
+
+ const ps = Array(n)
+ const os = Array(n + 1)
+ for (let i = 0; i < n; ++i) {
+ const c = arguments[i + 1]
+ ps[i] = c[0]
+ os[i] = toFunction(c[1])
+ }
+ os[n] = def
+
+ return function condOf(x, i, F, xi2yF) {
+ let min = n
+ of(x, i, Select, (y, j) => {
+ for (let i = 0; i < min; ++i) {
+ if (ps[i](y, j)) {
+ min = i
+ if (i === 0) return 0
+ else break
+ }
+ }
+ })
+ return os[min](x, i, F, xi2yF)
}
})
@@ -1035,23 +1030,6 @@ export const ifElse = I.curry(function ifElse(c, t, e) {
return eitherU(toFunction(t), toFunction(e))(c)
})
-export const iftes = (process.env.NODE_ENV === 'production'
- ? id
- : fn =>
- function iftes(_c, _t) {
- warn(
- iftes,
- '`iftes` has been obsoleted. Use `ifElse` or `cond` instead. See CHANGELOG for details.'
- )
- return fn.apply(null, arguments)
- })(function iftes(_c, _t) {
- let n = arguments.length
- let r = n & 1 ? toFunction(arguments[--n]) : zero
- while (0 <= (n -= 2))
- r = eitherU(toFunction(arguments[n + 1]), r)(arguments[n])
- return r
-})
-
export const orElse = I.curry(orElseU)
// Querying
@@ -1062,13 +1040,13 @@ export const chain = I.curry(function chain(xi2yO, xO) {
export const choice = (...os) => os.reduceRight(orElseU, zero)
-export const unless = eitherU(zeroOp, identity)
+export const unless = eitherU(zero, identity)
-export const when = eitherU(identity, zeroOp)
+export const when = eitherU(identity, zero)
export const optional = when(I.isDefined)
-export const zero = (x, i, C, xi2yC) => zeroOp(x, i, C, xi2yC)
+export {zero}
// Indices
@@ -1099,11 +1077,13 @@ export const skipIx = setName(tieIx(I.sndU), 'skipIx')
// Debugging
-export const getLog = I.curry(function getLog(l, s) {
- const {m, p, c} = traverseU(ConstantLog, getLogFn, l, s)
- console.log.apply(console, pushTo(p, [m]))
+export function getLog(l, s) {
+ let {p, c} = traverseU(SelectLog, x => ({p: [x, consExcept], x, c: x}), l, s)
+ p = pushTo(p, ['%O'])
+ for (let i = 2; i < p.length; ++i) p[0] += ' <= %O'
+ console.log.apply(console, p)
return c
-})
+}
export function log() {
const show = I.curry(function log(dir, x) {
@@ -1148,13 +1128,13 @@ export const seq = (process.env.NODE_ENV === 'production'
export const assignOp = x => [propsOf(x), setOp(x)]
export const modifyOp = xi2y =>
- function modifyOp(x, i, C, xi2yC) {
- return zeroOp((x = xi2y(x, i)), i, C, xi2yC, x)
+ function modifyOp(x, i, C, _xi2yC) {
+ return C.of(xi2y(x, i))
}
export const setOp = y =>
- function setOp(_x, i, C, xi2yC) {
- return zeroOp(y, i, C, xi2yC, y)
+ function setOp(_x, _i, C, _xi2yC) {
+ return C.of(y)
}
export const removeOp = setOp()
@@ -1183,11 +1163,9 @@ export function branches() {
// Traversals and combinators
-export const elems = (process.env.NODE_ENV === 'production'
- ? id
- : C.par(2, C.ef(reqApplicative('elems'))))(function elems(xs, i, A, xi2yA) {
+export function elems(xs, i, A, xi2yA) {
return seemsArrayLike(xs) ? elemsI(xs, i, A, xi2yA) : A.of(xs)
-})
+}
export const elemsTotal = (xs, i, A, xi2yA) =>
seemsArrayLike(xs)
@@ -1202,12 +1180,7 @@ export const entries = setName(toFunction([keyed, elems]), 'entries')
export const keys = setName(toFunction([keyed, elems, 0]), 'keys')
-export const matches = (process.env.NODE_ENV === 'production'
- ? id
- : C.dep(
- re =>
- re.global ? C.res(C.par(2, C.ef(reqApplicative('matches', re)))) : id
- ))(function matches(re) {
+export function matches(re) {
return function matches(x, _i, C, xi2yC) {
if (I.isString(x)) {
const {map} = C
@@ -1230,43 +1203,25 @@ export const matches = (process.env.NODE_ENV === 'production'
)
}
}
- return zeroOp(x, void 0, C, xi2yC)
+ return C.of(x)
}
-})
+}
-export const values = (process.env.NODE_ENV === 'production'
- ? id
- : C.par(2, C.ef(reqApplicative('values'))))(
- setName(branchOr1Level(identity, I.protoless0), 'values')
-)
+export const values = setName(branchOr1Level(identity, I.protoless0), 'values')
-export const children = (process.env.NODE_ENV === 'production'
- ? id
- : C.par(2, C.ef(reqApplicative('children'))))(function children(
- x,
- i,
- C,
- xi2yC
-) {
+export function children(x, i, C, xi2yC) {
return I.isArray(x)
? elemsI(x, i, C, xi2yC)
: I.isObject(x)
? values(x, i, C, xi2yC)
: C.of(x)
-})
+}
-export const flatten = (process.env.NODE_ENV === 'production'
- ? id
- : C.par(2, C.ef(reqApplicative('flatten'))))(function flatten(
- x,
- i,
- C,
- xi2yC
-) {
+export function flatten(x, i, C, xi2yC) {
const rec = (x, i) =>
I.isArray(x) ? elemsI(x, i, C, rec) : void 0 !== x ? xi2yC(x, i) : C.of(x)
return rec(x, i)
-})
+}
export function query() {
const r = []
@@ -1290,8 +1245,7 @@ export const leafs = satisfying(
// Folds over traversals
export const all = I.curry(function all(xi2b, t, s) {
- return !traverseU(
- Select,
+ return !getAsU(
(x, i) => {
if (!xi2b(x, i)) return true
},
@@ -1303,8 +1257,7 @@ export const all = I.curry(function all(xi2b, t, s) {
export const and = all(id)
export const any = I.curry(function any(xi2b, t, s) {
- return !!traverseU(
- Select,
+ return !!getAsU(
(x, i) => {
if (xi2b(x, i)) return true
},
@@ -1331,9 +1284,7 @@ export const collectAs = (process.env.NODE_ENV === 'production'
export const collect = collectAs(id)
-export const concatAs = mkTraverse(id, function concatAs(m) {
- return ConcatOf(m.concat, m.empty())
-})
+export const concatAs = mkTraverse(id, ConstantOf)
export const concat = concatAs(id)
@@ -1412,12 +1363,18 @@ export const forEachWith = I.curry(function forEachWith(newC, ef, t, s) {
return c
})
+export function get(l, s) {
+ return 1 < arguments.length ? getAsU(id, l, s) : s => getAsU(id, l, s)
+}
+
+export const getAs = I.curry(getAsU)
+
export const isDefined = I.curry(function isDefined(t, s) {
- return void 0 !== traverseU(Select, id, t, s)
+ return void 0 !== getAsU(id, t, s)
})
export const isEmpty = I.curry(function isEmpty(t, s) {
- return !traverseU(Select, toTrue, t, s)
+ return !getAsU(toTrue, t, s)
})
export const joinAs = mkTraverse(
@@ -1428,7 +1385,7 @@ export const joinAs = mkTraverse(
0,
C.ef(reqString('`join` and `joinAs` expect a string delimiter'))
))(function joinAs(d) {
- return ConcatOf(
+ return ConstantWith(
(x, y) => (void 0 !== x ? (void 0 !== y ? x + d + y : x) : y)
)
})
@@ -1465,8 +1422,7 @@ export const minimumBy = mumBy(I.ltU)
export const minimum = minimumBy(id)
export const none = I.curry(function none(xi2b, t, s) {
- return !traverseU(
- Select,
+ return !getAsU(
(x, i) => {
if (xi2b(x, i)) return true
},
@@ -1477,24 +1433,36 @@ export const none = I.curry(function none(xi2b, t, s) {
export const or = any(id)
-export const productAs = traverse(ConcatOf(I.multiplyU, 1))
+export const productAs = traverse(ConstantWith(I.multiplyU, 1))
export const product = productAs(unto(1))
-export const selectAs = traverse(Select)
+export const select =
+ process.env.NODE_ENV === 'production'
+ ? get
+ : I.curry(function select(l, s) {
+ warn(
+ select,
+ '`select` has been obsoleted. Just use `get`. See CHANGELOG for details.'
+ )
+ return get(l, s)
+ })
-export const select = selectAs(id)
+export const selectAs =
+ process.env.NODE_ENV === 'production'
+ ? getAs
+ : I.curry(function selectAs(f, l, s) {
+ warn(
+ selectAs,
+ '`selectAs` has been obsoleted. Just use `getAs`. See CHANGELOG for details.'
+ )
+ return getAs(f, l, s)
+ })
export const sumAs = traverse(Sum)
export const sum = sumAs(unto0)
-// Operations on lenses
-
-export function get(l, s) {
- return 1 < arguments.length ? getU(l, s) : s => getU(l, s)
-}
-
// Creating new lenses
export const lens = I.curry(lensU)
@@ -1736,7 +1704,7 @@ export const array = elem => {
}
export const inverse = iso => (x, i, F, xi2yF) =>
- F.map(x => getU(iso, x), xi2yF(setU(iso, x, void 0), i))
+ F.map(x => getAsU(id, iso, x), xi2yF(setU(iso, x, void 0), i))
// Basic isomorphisms
@@ -1907,7 +1875,7 @@ export const subtract = c => numberIsoU(I.add(-c), I.add(c))
// Interop
export const pointer = s => {
- if (s[0] === '#') s = getU(uriComponent, s)
+ if (s[0] === '#') s = getAsU(id, uriComponent, s)
const ts = s.split('/')
const n = ts.length
for (let i = 1; i < n; ++i) {
diff --git a/test/tests.js b/test/tests.js
index d232e716..3594dcd5 100644
--- a/test/tests.js
+++ b/test/tests.js
@@ -194,6 +194,10 @@ describe('L.log', () => {
describe('L.getLog', () => {
testEq(() => L.getLog(['x', 0, 'y'], {x: [{y: 101}]}), 101)
+ testEq(
+ () => L.getLog(['data', L.elems, 'y'], {data: [{x: 1}, {y: 2}, {y: 3}]}),
+ 2
+ )
})
describe('L.compose', () => {
@@ -219,9 +223,9 @@ describe('L.identity', () => {
describe('arities', () => {
const arities = {
- Constant: undefined,
Identity: undefined,
IdentityAsync: undefined,
+ Select: undefined,
add: 1,
all: 3,
and: 2,
@@ -271,12 +275,12 @@ describe('arities', () => {
forEach: 3,
forEachWith: 4,
get: 2,
+ getAs: 3,
getInverse: 2,
getLog: 2,
getter: 1,
identity: 4,
ifElse: 3,
- iftes: 2,
index: 1,
indexed: 4,
inverse: 1,
@@ -570,7 +574,7 @@ describe('L.setter', () => {
describe('L.zero', () => {
testEq(() => L.get(L.zero, 'anything'), undefined)
- testEq(() => L.get([L.zero, L.valueOr('whatever')], 'anything'), 'whatever')
+ testEq(() => L.get([L.zero, L.valueOr('whatever')], 'anything'), undefined)
testEq(() => L.set(L.zero, 'anything', 'original'), 'original')
testEq(() => L.collect([L.elems, L.zero], [1, 3]), [])
testEq(() => L.remove([L.elems, L.zero], [1, 2]), [1, 2])
@@ -908,7 +912,7 @@ describe('L.optional', () => {
describe('L.when', () => {
testEq(() => L.get(L.when(x => x > 2), 1), undefined)
- testEq(() => L.get([L.when(x => x > 2), I.always(2)], 1), 2)
+ testEq(() => L.get([L.when(x => x > 2), I.always(2)], 1), undefined)
testEq(() => L.get(L.when(x => x > 2), 3), 3)
testEq(() => L.collect([L.elems, L.when(x => x > 2)], [1, 3, 2, 4]), [3, 4])
testEq(
@@ -919,7 +923,7 @@ describe('L.when', () => {
describe('L.unless', () => {
testEq(() => L.get(L.unless(x => x <= 2), 1), undefined)
- testEq(() => L.get([L.unless(x => x <= 2), I.always(2)], 1), 2)
+ testEq(() => L.get([L.unless(x => x <= 2), I.always(2)], 1), undefined)
testEq(() => L.get(L.unless(x => x <= 2), 3), 3)
testEq(() => L.collect([L.elems, L.unless(x => x <= 2)], [1, 3, 2, 4]), [
3,
@@ -1408,12 +1412,13 @@ describe('L.seq', () => {
})
describe('lazy folds', () => {
- testEq(() => L.select(flatten, [[[[[[[[[[101]]]]]]]]]]), 101)
- testEq(() => L.select(L.elems, []), undefined)
- testEq(() => L.select(L.values, {}), undefined)
+ testEq(() => L.get([L.elems, 'y'], [{x: 1}, {y: 2}, {z: 3}]), 2)
+ testEq(() => L.get(flatten, [[[[[[[[[[101]]]]]]]]]]), 101)
+ testEq(() => L.get(L.elems, []), undefined)
+ testEq(() => L.get(L.values, {}), undefined)
testEq(
() =>
- L.selectAs((x, i) => (x > 3 ? [x + 2, i] : undefined), L.elems, [
+ L.getAs((x, i) => (x > 3 ? [x + 2, i] : undefined), L.elems, [
3,
1,
4,
@@ -1424,7 +1429,7 @@ describe('lazy folds', () => {
)
testEq(
() =>
- L.selectAs((x, i) => (x > 3 ? [x + 2, i] : undefined), L.values, {
+ L.getAs((x, i) => (x > 3 ? [x + 2, i] : undefined), L.values, {
a: 3,
b: 1,
c: 4,
@@ -1433,10 +1438,10 @@ describe('lazy folds', () => {
}),
[6, 'c']
)
- testEq(() => L.selectAs(_ => {}, L.values, {x: 1}), undefined)
+ testEq(() => L.getAs(_ => {}, L.values, {x: 1}), undefined)
testEq(
() =>
- L.selectAs(x => (x < 9 ? undefined : [x]), flatten, [
+ L.getAs(x => (x < 9 ? undefined : [x]), flatten, [
[[1], 2],
{y: 3},
[{l: 41, r: [5]}, {x: 6}]
@@ -1446,7 +1451,7 @@ describe('lazy folds', () => {
testEq(
() => {
let n = 0
- const v = X.selectAs(
+ const v = X.getAs(
x => {
n += 1
return x === 42 ? x : undefined
@@ -1461,7 +1466,7 @@ describe('lazy folds', () => {
testEq(
() => {
let n = 0
- const v = X.selectAs(
+ const v = X.getAs(
x => {
n += 1
return x === 42 ? x : undefined
@@ -1476,7 +1481,7 @@ describe('lazy folds', () => {
testEq(
() => {
let n = 0
- const v = X.selectAs(
+ const v = X.getAs(
x => {
n += 1
return x === 42 ? x : undefined
@@ -1491,7 +1496,7 @@ describe('lazy folds', () => {
testEq(
() => {
let n = 0
- const v = X.selectAs(
+ const v = X.getAs(
x => {
n += 1
return x === 'ba' ? x : undefined
@@ -1630,7 +1635,7 @@ describe('transforming', () => {
L.transform([L.elems, L.when(x => x > 3), L.removeOp], [3, 1, 4, 1, 5]),
[3, 1, 1]
)
- testEq(() => L.get(L.setOp(42), 101), 42)
+ testEq(() => L.get(L.setOp(42), 101), undefined)
testEq(() => L.set(L.setOp(42), 96, 101), 42)
})
@@ -1706,38 +1711,47 @@ describe('L.condOf', () => {
[42, 101]
)
testEq(() => L.get(L.condOf([]), 'anything'), undefined)
-})
-
-describe('L.ifElse', () => {
- testEq(() => L.set(L.ifElse(R.not, L.setOp(1), L.zero), 3, 2), 2)
- testEq(() => L.set(L.ifElse(R.not, L.setOp(1), L.zero), 3, 0), 1)
- testEq(() => L.transform(L.ifElse(R.not, L.setOp(1), L.setOp(0)), null), 1)
- testEq(() => L.transform(L.ifElse(R.not, L.setOp(1), L.setOp(0)), 2), 0)
-})
-
-describe('L.iftes', () => {
- testEq(() => L.set(L.iftes(R.not, L.setOp(1)), 3, 2), 2)
- testEq(() => L.set(L.iftes(R.not, L.setOp(1)), 3, 0), 1)
- testEq(() => L.transform(L.iftes(R.not, L.setOp(1), L.setOp(0)), null), 1)
- testEq(() => L.transform(L.iftes(R.not, L.setOp(1), L.setOp(0)), 2), 0)
testEq(
() =>
- L.transform(
- L.iftes(R.equals(1), L.setOp(-1), R.equals(2), L.setOp(1), L.setOp(2)),
- -1
+ L.get(
+ L.condOf(
+ ['c', L.elems],
+ [R.equals(1), ['d', 0]],
+ [R.equals(2), ['d', 1]],
+ [R.equals(3), ['d', 2]]
+ ),
+ {
+ c: [3, 1, 2],
+ d: ['a', 'b', 'c']
+ }
),
- 2
+ 'a'
)
testEq(
() =>
- L.transform(
- L.iftes(R.equals(1), L.setOp(-1), R.equals(2), L.setOp(1), L.setOp(2)),
- 2
+ L.get(
+ L.condOf(
+ ['c', L.elems],
+ [R.equals(1), ['d', 0]],
+ [R.equals(2), ['d', 1]],
+ [R.equals(3), ['d', 2]]
+ ),
+ {
+ c: [3, -1, 2],
+ d: ['a', 'b', 'c']
+ }
),
- 1
+ 'b'
)
})
+describe('L.ifElse', () => {
+ testEq(() => L.set(L.ifElse(R.not, L.setOp(1), L.zero), 3, 2), 2)
+ testEq(() => L.set(L.ifElse(R.not, L.setOp(1), L.zero), 3, 0), 1)
+ testEq(() => L.transform(L.ifElse(R.not, L.setOp(1), L.setOp(0)), null), 1)
+ testEq(() => L.transform(L.ifElse(R.not, L.setOp(1), L.setOp(0)), 2), 0)
+})
+
describe('L.singleton', () => {
testEq(() => L.get(L.singleton, ['too', 'long']), undefined)
testEq(() => L.get(L.singleton, 'too-long'), undefined)
@@ -2141,11 +2155,11 @@ describe('ix', () => {
}
)
- testEq(
- () => L.selectAs((v, i) => [v, i], ['foo', L.setIx('bar')], {foo: 101}),
- [101, 'bar']
- )
- testEq(() => L.selectAs(x => x + 1, x => x * 2, 3), 7)
+ testEq(() => L.getAs((v, i) => [v, i], ['foo', L.setIx('bar')], {foo: 101}), [
+ 101,
+ 'bar'
+ ])
+ testEq(() => L.getAs(x => x + 1, x => x * 2, 3), 7)
})
describe('async', () => {
@@ -2199,11 +2213,6 @@ if (process.env.NODE_ENV !== 'production') {
testThrows(() => X.prop(x => x))
testThrows(() => X.prop())
- testThrows(() => L.get(L.elems, []))
- testThrows(() => L.get(L.values, {}))
- testThrows(() => L.get(L.branch({a: []}), {}))
- testThrows(() => L.get(L.matches(/a/g), 'foo'))
-
testThrows(() => L.set(L.props('length'), 'lol', undefined))
testThrows(() => L.set(L.slice(undefined, undefined), 11, []))
testThrows(() => L.pick(new XYZ(1, 2, 3)))
@@ -2259,3 +2268,11 @@ describe('cloning avoidance', () => {
z: NaN
})
})
+
+describe('obsoleted', () => {
+ testEq(() => L.select([L.elems, 'x'], [{}, {x: 101}]), 101)
+ testEq(
+ () => L.selectAs(x => x + 1, [L.elems, 'x', L.optional], [{}, {x: 100}]),
+ 101
+ )
+})
diff --git a/test/types.js b/test/types.js
index 23b33f2b..33575299 100644
--- a/test/types.js
+++ b/test/types.js
@@ -66,12 +66,12 @@ const template = c => T.lazy(rec => T.props(T.or(c, rec)))
// Internals
-export const Constant = T_functor
-
export const Identity = T_monad
export const IdentityAsync = T_monad
+export const Select = T_applicative
+
export const toFunction = T.fn(
[T_optic],
T_opticFnOf(T.or(T_monad, T_applicative, T_functor))
@@ -119,7 +119,6 @@ export const ifElse = T.fn(
[T.fn([T_maybeDataO, T_index], T.any), T_optic, T_optic],
T_optic
)
-export const iftes = T.fnVarN(2, T.any, T_optic)
export const orElse = T.fn([T_optic, T_optic], T_optic)
// Querying
@@ -141,7 +140,7 @@ export const tieIx = T.fn([T.fn([T_index, T_index], T_index), T_optic], T_optic)
// Debugging
-export const getLog = T.fn([T_lens, T_maybeDataI], T_maybeDataO)
+export const getLog = T.fn([T_traversal, T_maybeDataI], T_maybeDataO)
export const log = T.fnVarN(0, T.string, T_optic)
// Operations on transforms
@@ -246,6 +245,13 @@ export const forEachWith = T.fn(
T.any
)
+export const get = T.fn([T_traversal, T_maybeDataI], T_maybeDataO)
+
+export const getAs = T.fn(
+ [T.fn([T_maybeDataO, T_index], T.any), T_traversal, T_maybeDataI],
+ T.any
+)
+
export const isDefined = T.fn([T_traversal, T_maybeDataI], T.boolean)
export const isEmpty = T.fn([T_traversal, T_maybeDataI], T.boolean)
@@ -299,10 +305,6 @@ export const selectAs = T.fn(
T.any
)
-// Operations on lenses
-
-export const get = T.fn([T_lens, T_maybeDataI], T_maybeDataO)
-
// Creating new lenses
export const lens = T.fn(