Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added retry #141

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 56 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ parts. [Try Lenses!](https://calmm-js.github.io/partial.lenses/playground.html)
* [`L.transform(optic, maybeData) ~> maybeData`](#L-transform "L.transform: POptic s a -> Maybe s -> Maybe s") <small><sup>v11.7.0</sup></small>
* [Sequencing](#sequencing)
* [`L.seq(...transforms) ~> transform`](#L-seq "L.seq: (...PTransform s a) -> PTransform s a") <small><sup>v9.4.0</sup></small>
* [Backtracking](#backtracking)
* [`L.retry((maybeValue, index) => transform, transform) ~> transform`](#L-retry "L.retry: ((Maybe t, Index) -> PTransform s u) -> PTransform s t -> PTransform s u") <small><sup>v13.3.0</sup></small>
* [Transforming](#transforming)
* [`L.assignOp(object) ~> optic`](#L-assignOp "L.assignOp: {p1: a1, ...ps} -> POptic {p1: a1, ...ps, ...o} {p1: a1, ...ps}") <small><sup>v11.13.0</sup></small>
* [`L.modifyOp((maybeValue, index) => maybeValue) ~> optic`](#L-modifyOp "L.modifyOp: ((Maybe a, Index) -> Maybe a) -> POptic a a") <small><sup>v11.7.0</sup></small>
Expand Down Expand Up @@ -856,15 +858,16 @@ Now, optics are composable in several ways and in each of those ways there is an
operation to perform the composition and laws on how such composed optics
behave. Here is a table of the means of composition supported by this library:

| | Operation(s) | Semantics
| ------------------------- | ----------------------------------------------------------------------------------- | -----------------------------------------------------------------------------------------
| [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)
| Picking | [`L.pick({...prop:lens})`](#L-pick) | <a href="https://en.wikipedia.org/wiki/Product_(category_theory)">Product</a> 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) | <a href="https://en.wikipedia.org/wiki/Monad_(functional_programming)">Monad</a> over [transforms](#transforms)
| | Operation(s) | Semantics
| ----------------------------- | ----------------------------------------------------------------------------------- | -----------------------------------------------------------------------------------------
| [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)
| Picking | [`L.pick({...prop:lens})`](#L-pick) | <a href="https://en.wikipedia.org/wiki/Product_(category_theory)">Product</a> 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) | <a href="https://en.wikipedia.org/wiki/Monad_(functional_programming)">Monad</a> over [transforms](#transforms)
| [Backtracking](#backtracking) | [`L.retry((maybeValue, index) => transform, transform)`](#L-retry) | <a href="https://en.wikipedia.org/wiki/Monad_(functional_programming)">Monad</a> over [transforms](#transforms)

The above table and, in particular, the semantics column is by no means
complete. In particular, the documentation of this library does not generally
Expand Down Expand Up @@ -1381,18 +1384,23 @@ empty or read-only [zero](#L-zero).

`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
combinator for querying with optics. `L.chain(y2yzO, xyO)` is equivalent to

```jsx
L.compose(optic, L.choose((maybeValue, index) =>
maybeValue === undefined
L.compose(xyO, L.choose((yMaybe, index) =>
yMaybe === undefined
? L.zero
: toOptic(maybeValue, index)))
: y2yzO(yMaybe, 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.
optics as providing a
[Monad](https://github.com/rpominov/static-land/blob/master/docs/spec.md#monad)
and
[Alternative](https://github.com/rpominov/static-land/blob/master/docs/spec.md#alternative)
aka
[MonadPlus](https://en.wikibooks.org/wiki/Haskell/Alternative_and_MonadPlus#MonadPlus).

##### <a id="L-choice"></a> [≡](#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") <small><sup>v2.1.0</sup></small>

Expand Down Expand Up @@ -1645,11 +1653,42 @@ combined together as a
[`Monad`](https://github.com/rpominov/static-land/blob/master/docs/spec.md#monad)

```jsx
chain(x2t, t) = L.seq(t, L.choose(x2t))
of(x) = L.setOp(x)
chain(y2yzt, xyT) = L.seq(xyT, L.choose(y2yzT))
of(x) = L.setOp(x)
```

which is not the same as the [querying monad](#L-chain) or the [backtracking
monad](#L-retry).

#### <a id="backtracking"></a> [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#backtracking) Backtracking

The [`L.retry`](#L-retry) combinator allows one to build
[transforms](#transforms) that modify their focus in multiple different ways and
choose one of the results.

##### <a id="L-retry"></a> [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#L-retry) [`L.retry((maybeValue, index) => transform, transform) ~> transform`](#L-retry "L.retry: ((Maybe t, Index) -> PTransform s u) -> PTransform s t -> PTransform s u") <small><sup>v13.3.0</sup></small>

`L.retry` creates a transform that first modifies the data in the focus with
given transform and then modifies the same data in the focus with the transform
returned by the given function.

Note that `L.retry` with [`L.seqOp`](#L-setOp) forms another
[`Monad`](https://github.com/rpominov/static-land/blob/master/docs/spec.md#monad):

```jsx
chain(y2xyT, xyT) = L.retry(x2xyT, xyT)
of(x) = L.setOp(x)
```

which is not the same as the [querying monad](#L-chain).
which is not the same as the [querying monad](#L-chain) or the [sequencing
monad](#L-seq).

Note that `L.retry(t2suT, stT)` can be implemented using [`L.choose`](#L-choose)
and [`L.seq`](#L-seq):

```jsx
L.choose((s, i) => L.seq(stT, L.choose(t => L.seq(L.setOp(s), t2suT(t, i)))))
```

#### <a id="transforming"></a> [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/index.html#transforming) Transforming

Expand Down
9 changes: 9 additions & 0 deletions bench/bench.js
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,15 @@ R.forEach(
L.seq(L.modifyOp(dec),
L.choose(n => n === 0 ? L.identity : rec))),
100)`
],
[
`L.transform(I.seq(L.identity,
L.retry(L.setOp),
L.retry(L.setOp),
L.retry(L.setOp),
L.retry(L.setOp),
L.retry(L.setOp)),
101)`
]
]
)
Expand Down
15 changes: 15 additions & 0 deletions src/partial.lenses.js
Original file line number Diff line number Diff line change
Expand Up @@ -894,6 +894,21 @@ export const seq = (process.env.NODE_ENV === 'production'
return r
})

// Backtracking

export const retry = (process.env.NODE_ENV === 'production'
? I.curry
: C.res(C.par(2, C.ef(reqMonad('retry')))))(
(yi2xzT, xyT) => (
(xyT = toFunction(xyT)),
(x, i, M, xi2yM) =>
M.chain(
y => toFunction(yi2xzT(y, i))(x, i, M, xi2yM),
xyT(x, i, M, xi2yM)
)
)
)

// Creating new traversals

export const branchOr = (process.env.NODE_ENV === 'production'
Expand Down
11 changes: 11 additions & 0 deletions test/tests.js
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,7 @@ describe('arities', () => {
replace: 2,
required: 1,
reread: 1,
retry: 2,
reverse: 4,
rewrite: 1,
seemsArrayLike: 1,
Expand Down Expand Up @@ -1211,6 +1212,16 @@ describe('L.seq', () => {
testEq(`collectM(L.seq(1, 0, 2), ["b", "a", "c"])`, ['a', 'b', 'c'])
})

describe('L.retry', () => {
testEq(
`L.transform([L.values,
L.retry((x, i) => L.modifyOp((y, i) => [{x, i}, {y, i}]),
L.modifyOp((x, i) => ({x, i})))],
{x: 42})`,
{x: [{x: {x: 42, i: 'x'}, i: 'x'}, {y: 42, i: 'x'}]}
)
})

describe('lazy folds', () => {
testEq(`L.select(flatten, [[[[[[[[[[101]]]]]]]]]])`, 101)
testEq(`L.select(L.elems, [])`, undefined)
Expand Down
7 changes: 7 additions & 0 deletions test/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,13 @@ export const log = T.fnVarN(0, T.string, T_optic)

export const seq = T.fnVarN(0, T_optic, T_transform)

// Backtracking

export const retry = T.fn(
[T.fn([T_maybeDataO, T_index], T_transform), T_transform],
T_transform
)

// Creating new traversals

export const branchOr = T.fn([T_optic, template(T_traversal)], T_traversal)
Expand Down