Repository: calmm-js/partial.lenses
Pull request #62 (2017-03-11 15:28:54)

Added `complement` isomorphism

codecov-io: # [Codecov](https://codecov.io/gh/calmm-js/partial.lenses/pull/62?src=pr&el=h1) Report
> Merging [#62](https://codecov.io/gh/calmm-js/partial.lenses/pull/62?src=pr&el=desc) into [master](https://codecov.io/gh/calmm-js/partial.lenses/commit/cfac88d3ad87cd21964acc0ad87f69a6a639eb80?src=pr&el=desc) will **not change** coverage.
> The diff coverage is `100%`.


```diff
@@          Coverage Diff          @@
##           master    #62   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files           1      1           
  Lines         434    435    +1     
=====================================
+ Hits          434    435    +1
```


| [Impacted Files](https://codecov.io/gh/calmm-js/partial.lenses/pull/62?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/partial.lenses.js](https://codecov.io/gh/calmm-js/partial.lenses/compare/cfac88d3ad87cd21964acc0ad87f69a6a639eb80...58c39f10665cf342eb918554f9a96c7a21e3bac9?src=pr&el=tree#diff-c3JjL3BhcnRpYWwubGVuc2VzLmpz) | `100% <100%> (ø)` | :white_check_mark: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/calmm-js/partial.lenses/pull/62?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/calmm-js/partial.lenses/pull/62?src=pr&el=footer). Last update [cfac88d...58c39f1](https://codecov.io/gh/calmm-js/partial.lenses/compare/cfac88d3ad87cd21964acc0ad87f69a6a639eb80...58c39f10665cf342eb918554f9a96c7a21e3bac9?src=pr&el=footer&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

Patch:
diff --git a/README.md b/README.md
index 5e41588..b526808 100644
--- a/README.md
+++ b/README.md
@@ -116,6 +116,7 @@ parts.  [▶ Try Lenses!](https://calmm-js.github.io/partial.lenses/)
     * [Creating new isomorphisms](#creating-new-isomorphisms)
       * [`L.iso(maybeData => maybeValue, maybeValue => maybeData) ~> isomorphism`](#L-iso "L.iso: (Maybe s -> Maybe a) -> (Maybe a -> Maybe s) -> PIso s a")
     * [Isomorphisms and combinators](#isomorphisms-and-combinators)
+      * [`L.complement ~> isomorphism`](#L-complement "L.complement: PIso Boolean Boolean")
       * [`L.identity ~> isomorphism`](#L-identity "L.identity: PIso s s")
       * [`L.inverse(isomorphism) ~> isomorphism`](#L-inverse "L.inverse: PIso a b -> PIso b a")
 * [Examples](#examples)
@@ -2206,6 +2207,22 @@ L.modify(L.identity, f, x) = f(x)
   L.compose(l, L.identity) = l
 ```
 
+##### <a id="L-complement"></a> [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/#L-complement) [`L.complement ~> isomorphism`](#L-complement "L.complement: PIso Boolean Boolean")
+
+`L.complement` is an isomorphism that performs logical negation of any
+non-`undefined` value when either read or written through.
+
+For example:
+
+```js
+L.set([L.complement, L.log()],
+      "Could be anything truthy",
+      "Also converted to bool")
+// get false
+// set "Could be anything truthy"
+// false
+```
+
 ##### <a id="L-inverse"></a> [≡](#contents) [▶](https://calmm-js.github.io/partial.lenses/#L-inverse) [`L.inverse(isomorphism) ~> isomorphism`](#L-inverse "L.inverse: PIso a b -> PIso b a")
 
 `L.inverse` returns the inverse of the given isomorphism.  Note that this
diff --git a/src/partial.lenses.js b/src/partial.lenses.js
index 92dcd2e..179a98b 100644
--- a/src/partial.lenses.js
+++ b/src/partial.lenses.js
@@ -30,6 +30,8 @@ const cpair = x => xs => [x, xs]
 
 const unto = c => x => void 0 !== x ? x : c
 
+const notPartial = x => void 0 !== x ? !x : x
+
 const seemsArrayLike = x =>
   x instanceof Object && (x = x.length, x === (x >> 0) && 0 <= x) ||
   isString(x)
@@ -869,6 +871,8 @@ export const iso =
 
 // Isomorphisms and combinators
 
+export const complement = iso(notPartial, notPartial)
+
 export const identity = (_F, xi2yF, x, i) => xi2yF(x, i)
 
 export const inverse = iso => (F, xi2yF, x, i) =>


Test patch:
diff --git a/test/core.js b/test/core.js
index 0cd17d1..5f8fa71 100644
--- a/test/core.js
+++ b/test/core.js
@@ -24,6 +24,8 @@ const foldx = concat => R.curry((f, r, t, s) =>
   concatAs((x, i) => r => f(r, x, i), {empty: () => R.identity, concat}, t, s)(r))
 const concatDefined = m => R.curry((t, s) => concat(m, [t, optional], s))
 
+const toPartial = f => x => isDefined(x) ? f(x) : x
+
 // Optics
 
 export const toFunction = L.toFunction
@@ -210,5 +212,6 @@ export const iso = lens
 
 // Isomorphisms and combinators
 
+export const complement = iso(toPartial(R.not), toPartial(R.not))
 export const identity = iso(R.identity, R.identity)
 export const inverse = i => iso(getInverse(i), get(i))
diff --git a/test/tests.js b/test/tests.js
index 6b4fdea..14339cb 100644
--- a/test/tests.js
+++ b/test/tests.js
@@ -155,6 +155,7 @@ describe("arities", () => {
     choose: 1,
     collect: 2,
     collectAs: 3,
+    complement: 4,
     compose: 0,
     concat: 3,
     concatAs: 4,
@@ -650,6 +651,13 @@ describe("L.inverse", () => {
   testEq(`L.remove(["x", L.inverse(offBy1)], {x:1})`, undefined)
 })
 
+describe("L.complement", () => {
+  testEq(`L.get(L.complement, undefined)`, undefined)
+  testEq(`L.set(L.complement, undefined, true)`, undefined)
+  testEq(`L.get(L.complement, true)`, false)
+  testEq(`L.set(L.complement, true, undefined)`, false)
+})
+
 describe("L.branch", () => {
   testEq(`L.modify(L.branch({}), x => x+1, null)`, null)
   testEq(`L.modify(L.branch({}), x => x+1, "anything")`, "anything")
diff --git a/test/types.js b/test/types.js
index 7c51fce..5469e99 100644
--- a/test/types.js
+++ b/test/types.js
@@ -222,5 +222,6 @@ export const iso = T.fn([T.fn([T_maybeData], T_maybeData),
 
 // Isomorphisms and combinators
 
+export const complement = T_isomorphism
 export const identity = T_isomorphism
 export const inverse = T.fn([T_isomorphism], T_isomorphism)