Repository: gcanti/fp-ts
Pull request #872 (2019-05-15 13:04:27)

Add `foldIO` and `foldIOEither` to `IOEither`
Copied from TaskEither module


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index eeb4bced..a3101bc0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,13 @@
 **Note**: Gaps between patch versions are faulty/broken releases. **Note**: A feature tagged as Experimental is in a
 high state of flux, you're at risk of it changing without notice.
 
+# 1.18.2
+
+- **Polish**
+  - fix `NonEmptyArray` definition (@gcanti)
+- **Deprecation**
+  - deprecate `NonEmptyArray.make` in favour of `cons` (@gcanti)
+
 # 1.18.1
 
 - **Bug Fix**
diff --git a/docs/modules/NonEmptyArray2v.ts.md b/docs/modules/NonEmptyArray2v.ts.md
index 71a9a30c..dea0771a 100644
--- a/docs/modules/NonEmptyArray2v.ts.md
+++ b/docs/modules/NonEmptyArray2v.ts.md
@@ -37,7 +37,7 @@ Data structure which represents non-empty arrays
 - [head (function)](#head-function)
 - [insertAt (function)](#insertat-function)
 - [last (function)](#last-function)
-- [make (function)](#make-function)
+- [~~make~~ (function)](#make-function)
 - [max (function)](#max-function)
 - [min (function)](#min-function)
 - [modifyAt (function)](#modifyat-function)
@@ -54,8 +54,8 @@ Data structure which represents non-empty arrays
 ```ts
 export interface NonEmptyArray<A> extends Array<A> {
   0: A
-  map: <B>(f: (a: A, index: number, nea: NonEmptyArray<A>) => B) => NonEmptyArray<B>
-  concat: (as: Array<A>) => NonEmptyArray<A>
+  map<B>(f: (a: A, index: number, nea: NonEmptyArray<A>) => B): NonEmptyArray<B>
+  concat(as: Array<A>): NonEmptyArray<A>
 }
 ```
 
@@ -380,7 +380,9 @@ export function last<A>(nea: NonEmptyArray<A>): A { ... }
 
 Added in v1.15.0
 
-# make (function)
+# ~~make~~ (function)
+
+Use `cons` instead
 
 **Signature**
 
diff --git a/docs/modules/index.md b/docs/modules/index.md
index 4cc108ae..9cbb3f56 100644
--- a/docs/modules/index.md
+++ b/docs/modules/index.md
@@ -58,7 +58,7 @@ In your code you will make heavy use of data types to model your domain. Data ty
 * [Map](./Map.ts.html)
 * [MonadIO](./MonadIO.ts.html)
 * [MonadTask](./MonadTask.ts.html)
-* [NonEmptyArray](./NonEmptyArray.ts.html)
+* [NonEmptyArray](./NonEmptyArray2v.ts.html)
 * [Option](./Option.ts.html)
 * [OptionT](./OptionT.ts.html)
 * [Pair](./Pair.ts.html)
diff --git a/package-lock.json b/package-lock.json
index dd698290..901f4278 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1565,13 +1565,13 @@
       }
     },
     "docs-ts": {
-      "version": "0.0.2",
-      "resolved": "https://registry.npmjs.org/docs-ts/-/docs-ts-0.0.2.tgz",
-      "integrity": "sha512-YM3ccE7iYs4G1bS5tc8zHwTCiJ2I7Rf01fyvg0hz8m1Q3tsKRAg8rHubg6i+aWMxl5iX5wUJe4x/T81YMQ5EFw==",
+      "version": "0.0.3",
+      "resolved": "https://registry.npmjs.org/docs-ts/-/docs-ts-0.0.3.tgz",
+      "integrity": "sha512-FObrpmHmZQPqIhlo7hYVMxyveBQpduLxR48g17eSzZOYdSOIdR5lnJK6MNbukDU5vDscuS3S1EQGrOSMAhE8MQ==",
       "dev": true,
       "requires": {
         "doctrine": "^3.0.0",
-        "fp-ts": "^1.14.2",
+        "fp-ts": "^1.18.1",
         "fs-extra": "^7.0.1",
         "glob": "^7.1.3",
         "markdown-toc": "^1.2.0",
@@ -2491,9 +2491,9 @@
       "dev": true
     },
     "fp-ts": {
-      "version": "1.17.3",
-      "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.17.3.tgz",
-      "integrity": "sha512-r4gHfAWaRrYPsmdzRl1U9CkpbdOi8fPg5F5KiazAadENz5DKdWEaCDPl2Tf92fvkZGD/ekZ3EHu3gtXIVcsXtA==",
+      "version": "1.18.1",
+      "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.18.1.tgz",
+      "integrity": "sha512-esfGgsuc0S9Q23YnoWQH4DrnBIJ7LCbA7AsUKFwawVsYmVZ7Cl+3cjd8nAhJLWwpUKDRsBsMJOf+uX4ZakZMsQ==",
       "dev": true
     },
     "fragment-cache": {
diff --git a/package.json b/package.json
index 32457b54..1421ab5f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "fp-ts",
-  "version": "1.18.1",
+  "version": "1.18.2",
   "description": "Functional programming in TypeScript",
   "files": [
     "lib",
@@ -44,7 +44,7 @@
     "@types/prettier": "1.10.0",
     "benchmark": "2.1.4",
     "chalk": "2.2.0",
-    "docs-ts": "0.0.2",
+    "docs-ts": "0.0.3",
     "doctoc": "^1.4.0",
     "doctrine": "2.0.0",
     "dtslint": "github:gcanti/dtslint",
diff --git a/src/IOEither.ts b/src/IOEither.ts
index 85f7ec0c..051332bc 100644
--- a/src/IOEither.ts
+++ b/src/IOEither.ts
@@ -68,6 +68,18 @@ export class IOEither<L, A> {
   fold<R>(left: (l: L) => R, right: (a: A) => R): IO<R> {
     return foldT(left, right, this.value)
   }
+  /**
+   * Similar to `fold`, but the result is flattened.
+   */
+  foldIO<R>(left: (l: L) => IO<R>, right: (a: A) => IO<R>): IO<R> {
+    return this.value.chain(fa => fa.fold(left, right))
+  }
+  /**
+   * Similar to `fold`, but the result is flattened.
+   */
+  foldIOEither<M, B>(onLeft: (l: L) => IOEither<M, B>, onRight: (a: A) => IOEither<M, B>): IOEither<M, B> {
+    return new IOEither(this.value.chain(e => e.fold(onLeft, onRight).value))
+  }
   mapLeft<M>(f: (l: L) => M): IOEither<M, A> {
     return new IOEither(this.value.map(e => e.mapLeft(f)))
   }
diff --git a/src/NonEmptyArray2v.ts b/src/NonEmptyArray2v.ts
index 7e53870d..70672e63 100644
--- a/src/NonEmptyArray2v.ts
+++ b/src/NonEmptyArray2v.ts
@@ -31,8 +31,8 @@ export type URI = typeof URI
  */
 export interface NonEmptyArray<A> extends Array<A> {
   0: A
-  map: <B>(f: (a: A, index: number, nea: NonEmptyArray<A>) => B) => NonEmptyArray<B>
-  concat: (as: Array<A>) => NonEmptyArray<A>
+  map<B>(f: (a: A, index: number, nea: NonEmptyArray<A>) => B): NonEmptyArray<B>
+  concat(as: Array<A>): NonEmptyArray<A>
 }
 
 /**
@@ -46,10 +46,13 @@ export const getShow = <A>(S: Show<A>): Show<NonEmptyArray<A>> => {
 }
 
 /**
+ * Use `cons` instead
+ *
  * @since 1.15.0
+ * @deprecated
  */
 export function make<A>(head: A, tail: Array<A>): NonEmptyArray<A> {
-  return [head, ...tail] as any
+  return A.cons(head, tail)
 }
 
 /**
@@ -207,7 +210,7 @@ export const groupBy = <A>(as: Array<A>, f: (a: A) => string): { [key: string]:
     if (r.hasOwnProperty(k)) {
       r[k].push(a)
     } else {
-      r[k] = make(a, [])
+      r[k] = cons(a, [])
     }
   }
   return r
diff --git a/src/Record.ts b/src/Record.ts
index 37fa4e1f..4c2d7f88 100644
--- a/src/Record.ts
+++ b/src/Record.ts
@@ -12,6 +12,12 @@ import { fromEquals, Setoid } from './Setoid'
 import { Unfoldable, Unfoldable1 } from './Unfoldable'
 import { Show, showString } from './Show'
 
+const _hasOwnProperty = Object.prototype.hasOwnProperty
+
+function hasOwnProperty<K extends string, A>(k: K, d: Record<K, A>): boolean {
+  return _hasOwnProperty.call(d, k)
+}
+
 /**
  * @since 1.17.0
  */
@@ -118,7 +124,7 @@ export function remove<KS extends string, K extends string, A>(
 ): Record<string extends K ? string : Exclude<KS, K>, A>
 export function remove<A>(k: string, d: Record<string, A>): Record<string, A>
 export function remove<A>(k: string, d: Record<string, A>): Record<string, A> {
-  if (!d.hasOwnProperty(k)) {
+  if (!hasOwnProperty(k, d)) {
     return d
   }
   const r = Object.assign({}, d)
@@ -143,7 +149,7 @@ export const pop = <A>(k: string, d: Record<string, A>): Option<[A, Record<strin
  */
 export const isSubrecord = <A>(S: Setoid<A>) => (d1: Record<string, A>, d2: Record<string, A>): boolean => {
   for (let k in d1) {
-    if (!d2.hasOwnProperty(k) || !S.equals(d1[k], d2[k])) {
+    if (!hasOwnProperty(k, d2) || !S.equals(d1[k], d2[k])) {
       return false
     }
   }
@@ -193,7 +199,7 @@ export function getMonoid<A>(S: Semigroup<A>): Monoid<Record<string, A>> {
  * @since 1.10.0
  */
 export const lookup = <A>(key: string, fa: Record<string, A>): Option<A> => {
-  return fa.hasOwnProperty(key) ? optionSome(fa[key]) : none
+  return hasOwnProperty(key, fa) ? optionSome(fa[key]) : none
 }
 
 /**
@@ -645,7 +651,7 @@ export function filterWithKey<A>(fa: Record<string, A>, p: (key: string, a: A) =
   const r: Record<string, A> = {}
   let changed = false
   for (const key in fa) {
-    if (fa.hasOwnProperty(key)) {
+    if (hasOwnProperty(key, fa)) {
       const a = fa[key]
       if (p(key, a)) {
         r[key] = a
@@ -682,7 +688,7 @@ export function fromFoldable<F>(
 ): <A>(ta: HKT<F, [string, A]>, onConflict: (existing: A, a: A) => A) => Record<string, A> {
   return <A>(ta: HKT<F, [string, A]>, f: (existing: A, a: A) => A) => {
     return F.reduce<[string, A], Record<string, A>>(ta, {}, (b, [k, a]) => {
-      b[k] = b.hasOwnProperty(k) ? f(b[k], a) : a
+      b[k] = hasOwnProperty(k, b) ? f(b[k], a) : a
       return b
     })
   }
@@ -750,7 +756,7 @@ export function fromFoldableMap<F, B>(
   return <A>(ta: HKT<F, A>, f: (a: A) => [string, B]) => {
     return F.reduce<A, Record<string, B>>(ta, {}, (r, a) => {
       const [k, b] = f(a)
-      r[k] = r.hasOwnProperty(k) ? M.concat(r[k], b) : b
+      r[k] = hasOwnProperty(k, r) ? M.concat(r[k], b) : b
       return r
     })
   }


Test patch:
diff --git a/test/IOEither.ts b/test/IOEither.ts
index 28b675b5..e9d07fa4 100644
--- a/test/IOEither.ts
+++ b/test/IOEither.ts
@@ -60,6 +60,49 @@ describe('IOEither', () => {
     assert.strictEqual(b2, true)
   })
 
+  it('foldIO', () => {
+    const whenLeft = () => io.of('left')
+    const whenRight = () => io.of('right')
+
+    const r1 = fromLeft<string, number>('a')
+      .foldIO(whenLeft, whenRight)
+      .run()
+    const r2 = ioEither
+      .of<string, number>(1)
+      .foldIO(whenLeft, whenRight)
+      .run()
+
+    assert.deepStrictEqual(r1, 'left')
+    assert.deepStrictEqual(r2, 'right')
+  })
+
+  it('foldIOEither', () => {
+    const whenLeft = (s: string) =>
+      s.length >= 2 ? ioEither.of<boolean, string>('okleft') : fromLeft<boolean, string>(false)
+    const whenRight = (n: number) =>
+      n >= 2 ? ioEither.of<boolean, string>('okright') : fromLeft<boolean, string>(true)
+
+    const r1 = fromLeft<string, number>('a')
+      .foldIOEither(whenLeft, whenRight)
+      .run()
+    const r2 = fromLeft<string, number>('aa')
+      .foldIOEither(whenLeft, whenRight)
+      .run()
+    const r3 = ioEither
+      .of<string, number>(1)
+      .foldIOEither(whenLeft, whenRight)
+      .run()
+    const r4 = ioEither
+      .of<string, number>(2)
+      .foldIOEither(whenLeft, whenRight)
+      .run()
+
+    assert.deepStrictEqual(r1, eitherLeft(false))
+    assert.deepStrictEqual(r2, eitherRight('okleft'))
+    assert.deepStrictEqual(r3, eitherLeft(true))
+    assert.deepStrictEqual(r4, eitherRight('okright'))
+  })
+
   it('bimap', () => {
     const f = (s: string): number => s.length
     const g = (n: number): boolean => n > 2
diff --git a/test/NonEmptyArray2v.ts b/test/NonEmptyArray2v.ts
index 5608a61a..f5eb49e2 100644
--- a/test/NonEmptyArray2v.ts
+++ b/test/NonEmptyArray2v.ts
@@ -42,6 +42,7 @@ import { showString } from '../src/Show'
 
 describe.only('NonEmptyArray2v', () => {
   it('make', () => {
+    // tslint:disable-next-line: deprecation
     assert.deepStrictEqual(make(1, [2]), fromNonEmptyArray([1, 2]))
   })
 
@@ -304,12 +305,12 @@ describe.only('NonEmptyArray2v', () => {
 
   it('modifyAt', () => {
     const double = (n: number): number => n * 2
-    assert.deepStrictEqual(modifyAt(make<number>(1, []), 1, double), none)
-    assert.deepStrictEqual(modifyAt(make<number>(1, [2]), 1, double), some(make(1, [4])))
+    assert.deepStrictEqual(modifyAt(cons(1, []), 1, double), none)
+    assert.deepStrictEqual(modifyAt(cons(1, [2]), 1, double), some(cons(1, [4])))
   })
 
   it('copy', () => {
-    const nea1 = make<number>(1, [])
+    const nea1 = cons(1, [])
     const nea2 = copy(nea1)
     assert.deepStrictEqual(nea2, nea1)
     assert.strictEqual(nea2 === nea1, false)
@@ -403,7 +404,7 @@ describe.only('NonEmptyArray2v', () => {
 
   it('getShow', () => {
     const S = getShow(showString)
-    assert.strictEqual(S.show(make<string>('a', [])), `make("a", [])`)
-    assert.strictEqual(S.show(make<string>('a', ['b', 'c'])), `make("a", ["b", "c"])`)
+    assert.strictEqual(S.show(cons('a', [])), `make("a", [])`)
+    assert.strictEqual(S.show(cons('a', ['b', 'c'])), `make("a", ["b", "c"])`)
   })
 })