Repository: gcanti/fp-ts
Pull request #899 (2019-07-05 07:49:45)

version 1.19.4


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0575c63d..36e222b6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -14,6 +14,12 @@
 **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.19.4
+
+- **Bug Fix**
+  - fix `PipeableBifunctor` definition (@gcanti)
+  - fix `chunksOf` implementation, fix #897 (@gcanti)
+
 # 1.19.3
 
 - **Polish**
diff --git a/docs/modules/pipeable.ts.md b/docs/modules/pipeable.ts.md
index c01f8e61..0329a7c3 100644
--- a/docs/modules/pipeable.ts.md
+++ b/docs/modules/pipeable.ts.md
@@ -246,7 +246,7 @@ export interface PipeableApply4<F extends URIS4> extends PipeableFunctor4<F> {
 ```ts
 export interface PipeableBifunctor<F> {
   readonly bimap: <L, A, M, B>(f: (l: L) => M, g: (a: A) => B) => (fa: HKT2<F, L, A>) => HKT2<F, M, B>
-  readonly mapLeft: <L, A, M>(f: (l: L) => M) => (fa: HKT2<F, L, A>) => HKT2<F, M, A>
+  readonly mapLeft: <L, M>(f: (l: L) => M) => <A>(fa: HKT2<F, L, A>) => HKT2<F, M, A>
 }
 ```
 
@@ -257,7 +257,7 @@ export interface PipeableBifunctor<F> {
 ```ts
 export interface PipeableBifunctor2<F extends URIS2> {
   readonly bimap: <L, A, M, B>(f: (l: L) => M, g: (a: A) => B) => (fa: Kind2<F, L, A>) => Kind2<F, M, B>
-  readonly mapLeft: <L, A, M>(f: (l: L) => M) => (fa: Kind2<F, L, A>) => Kind2<F, M, A>
+  readonly mapLeft: <L, M>(f: (l: L) => M) => <A>(fa: Kind2<F, L, A>) => Kind2<F, M, A>
 }
 ```
 
@@ -268,7 +268,7 @@ export interface PipeableBifunctor2<F extends URIS2> {
 ```ts
 export interface PipeableBifunctor3<F extends URIS3> {
   readonly bimap: <L, A, M, B>(f: (l: L) => M, g: (a: A) => B) => <U>(fa: Kind3<F, U, L, A>) => Kind3<F, U, M, B>
-  readonly mapLeft: <L, A, M>(f: (l: L) => M) => <U>(fa: Kind3<F, U, L, A>) => Kind3<F, U, M, A>
+  readonly mapLeft: <L, M>(f: (l: L) => M) => <U, A>(fa: Kind3<F, U, L, A>) => Kind3<F, U, M, A>
 }
 ```
 
@@ -282,7 +282,7 @@ export interface PipeableBifunctor4<F extends URIS4> {
     f: (l: L) => M,
     g: (a: A) => B
   ) => <X, U>(fa: Kind4<F, X, U, L, A>) => Kind4<F, X, U, M, B>
-  readonly mapLeft: <L, A, M>(f: (l: L) => M) => <X, U>(fa: Kind4<F, X, U, L, A>) => Kind4<F, X, U, M, A>
+  readonly mapLeft: <L, M>(f: (l: L) => M) => <X, U, A>(fa: Kind4<F, X, U, L, A>) => Kind4<F, X, U, M, A>
 }
 ```
 
diff --git a/dtslint/ts3.1/index.ts b/dtslint/ts3.1/index.ts
index 4764639e..e8335094 100644
--- a/dtslint/ts3.1/index.ts
+++ b/dtslint/ts3.1/index.ts
@@ -28,6 +28,7 @@ import * as Ring from '../../lib/Ring'
 import * as Field from '../../lib/Field'
 import * as NEA2v from '../../lib/NonEmptyArray2v'
 import * as Map from '../../lib/Map'
+import { pipe } from '../../src/pipeable'
 
 const double = (n: number): number => n * 2
 const len = (s: string): number => s.length
@@ -452,3 +453,13 @@ declare function isStringWithKey(i: 'a' | 'b', x: unknown): x is string
 
 filterableWithIndexMap.filterWithIndex(Map.empty as Map<'a' | 'b', string | number>, isStringWithKey) // $ExpectType Map<"a" | "b", string>
 filterableWithIndexMap.partitionWithIndex(Map.empty as Map<'a' | 'b', string | number>, isStringWithKey) // $ExpectType Separated<Map<"a" | "b", string | number>, Map<"a" | "b", string>>
+
+//
+// pipeable
+//
+
+// $ExpectType Either<number, number>
+pipe(
+  E.right<string, number>(1),
+  E.mapLeft<string, number>(e => e.length)
+)
diff --git a/jest.config.js b/jest.config.js
index 0d0e9592..78e29494 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -2,7 +2,7 @@ module.exports = {
   preset: 'ts-jest',
   testEnvironment: 'node',
   collectCoverage: true,
-  collectCoverageFrom: ['src/**/*.{ts}', '!src/Free.ts'],
+  collectCoverageFrom: ['src/**/*.ts', '!src/Free.ts'],
   transform: {
     '^.+\\.tsx?$': 'ts-jest'
   },
diff --git a/package.json b/package.json
index d8f828d9..57ba2e62 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "fp-ts",
-  "version": "1.19.3",
+  "version": "1.19.4",
   "description": "Functional programming in TypeScript",
   "files": [
     "lib",
diff --git a/src/Array.ts b/src/Array.ts
index b7d68f1d..852e3ad7 100644
--- a/src/Array.ts
+++ b/src/Array.ts
@@ -1375,7 +1375,7 @@ export const split = <A>(n: number, as: Array<A>): [Array<A>, Array<A>] => {
 
 function _chunksOf<A>(as: Array<A>, n: number): Array<Array<A>> {
   // tslint:disable-next-line: deprecation
-  return isOutOfBound(n - 1, as) ? [as] : chop(as, as => split(n, as))
+  return as.length === 0 ? empty : isOutOfBound(n - 1, as) ? [as] : chop(as, as => split(n, as))
 }
 
 /**
diff --git a/src/pipeable.ts b/src/pipeable.ts
index 6dd4a5ad..79016e81 100644
--- a/src/pipeable.ts
+++ b/src/pipeable.ts
@@ -336,17 +336,17 @@ export interface PipeableExtend4<F extends URIS4> extends PipeableFunctor4<F> {
 
 export interface PipeableBifunctor<F> {
   readonly bimap: <L, A, M, B>(f: (l: L) => M, g: (a: A) => B) => (fa: HKT2<F, L, A>) => HKT2<F, M, B>
-  readonly mapLeft: <L, A, M>(f: (l: L) => M) => (fa: HKT2<F, L, A>) => HKT2<F, M, A>
+  readonly mapLeft: <L, M>(f: (l: L) => M) => <A>(fa: HKT2<F, L, A>) => HKT2<F, M, A>
 }
 
 export interface PipeableBifunctor2<F extends URIS2> {
   readonly bimap: <L, A, M, B>(f: (l: L) => M, g: (a: A) => B) => (fa: Kind2<F, L, A>) => Kind2<F, M, B>
-  readonly mapLeft: <L, A, M>(f: (l: L) => M) => (fa: Kind2<F, L, A>) => Kind2<F, M, A>
+  readonly mapLeft: <L, M>(f: (l: L) => M) => <A>(fa: Kind2<F, L, A>) => Kind2<F, M, A>
 }
 
 export interface PipeableBifunctor3<F extends URIS3> {
   readonly bimap: <L, A, M, B>(f: (l: L) => M, g: (a: A) => B) => <U>(fa: Kind3<F, U, L, A>) => Kind3<F, U, M, B>
-  readonly mapLeft: <L, A, M>(f: (l: L) => M) => <U>(fa: Kind3<F, U, L, A>) => Kind3<F, U, M, A>
+  readonly mapLeft: <L, M>(f: (l: L) => M) => <U, A>(fa: Kind3<F, U, L, A>) => Kind3<F, U, M, A>
 }
 
 export interface PipeableBifunctor4<F extends URIS4> {
@@ -354,7 +354,7 @@ export interface PipeableBifunctor4<F extends URIS4> {
     f: (l: L) => M,
     g: (a: A) => B
   ) => <X, U>(fa: Kind4<F, X, U, L, A>) => Kind4<F, X, U, M, B>
-  readonly mapLeft: <L, A, M>(f: (l: L) => M) => <X, U>(fa: Kind4<F, X, U, L, A>) => Kind4<F, X, U, M, A>
+  readonly mapLeft: <L, M>(f: (l: L) => M) => <X, U, A>(fa: Kind4<F, X, U, L, A>) => Kind4<F, X, U, M, A>
 }
 
 export interface PipeableFoldable<F> {


Test patch:
diff --git a/test/Array.ts b/test/Array.ts
index 7961a34e..9b83f822 100644
--- a/test/Array.ts
+++ b/test/Array.ts
@@ -81,6 +81,7 @@ import { fold as foldMonoid, monoidString, monoidSum } from '../src/Monoid'
 import { fromPredicate, getEq as getOptionEq, none, option, Option, some } from '../src/Option'
 import { ord, ordNumber, ordString } from '../src/Ord'
 import { showString } from '../src/Show'
+import { isDeepStrictEqual } from 'util'
 
 const p = (n: number) => n > 2
 
@@ -510,6 +511,7 @@ describe('Array', () => {
   })
 
   it('foldMap', () => {
+    // tslint:disable-next-line: deprecation
     const old = F.foldMap(array, monoidString)
     const foldMap = array.foldMap(monoidString)
     const x1 = ['a', 'b', 'c']
@@ -522,6 +524,7 @@ describe('Array', () => {
   })
 
   it('foldr', () => {
+    // tslint:disable-next-line: deprecation
     const old = F.foldr(array)
     const foldr = array.foldr
     const x1 = ['a', 'b', 'c']
@@ -760,41 +763,60 @@ describe('Array', () => {
     assert.deepStrictEqual(splitAt(3)([1, 2]), [[1, 2], []])
   })
 
-  it('chunksOf', () => {
-    // tslint:disable-next-line: deprecation
-    assert.deepStrictEqual(chunksOf([1, 2, 3, 4, 5], 2), [[1, 2], [3, 4], [5]])
-    // tslint:disable-next-line: deprecation
-    assert.deepStrictEqual(chunksOf([1, 2, 3, 4, 5, 6], 2), [[1, 2], [3, 4], [5, 6]])
-    // tslint:disable-next-line: deprecation
-    assert.deepStrictEqual(chunksOf([1, 2, 3, 4, 5], 5), [[1, 2, 3, 4, 5]])
-    // tslint:disable-next-line: deprecation
-    assert.deepStrictEqual(chunksOf([1, 2, 3, 4, 5], 6), [[1, 2, 3, 4, 5]])
-    // tslint:disable-next-line: deprecation
-    assert.deepStrictEqual(chunksOf([1, 2, 3, 4, 5], 1), [[1], [2], [3], [4], [5]])
-    // tslint:disable-next-line: deprecation
-    assert.deepStrictEqual(chunksOf([], 1), [[]])
-    // tslint:disable-next-line: deprecation
-    assert.deepStrictEqual(chunksOf([], 2), [[]])
-    // tslint:disable-next-line: deprecation
-    assert.deepStrictEqual(chunksOf([], 0), [[]])
-    // tslint:disable-next-line: deprecation
-    assert.deepStrictEqual(chunksOf([1, 2], 0), [[1, 2]])
-    // tslint:disable-next-line: deprecation
-    assert.deepStrictEqual(chunksOf([1, 2], 10), [[1, 2]])
-    // tslint:disable-next-line: deprecation
-    assert.deepStrictEqual(chunksOf([1, 2], -1), [[1, 2]])
+  describe('chunksOf', () => {
+    it('should split an array into length-n pieces', () => {
+      // tslint:disable-next-line: deprecation
+      assert.deepStrictEqual(chunksOf([1, 2, 3, 4, 5], 2), [[1, 2], [3, 4], [5]])
+      // tslint:disable-next-line: deprecation
+      assert.deepStrictEqual(chunksOf([1, 2, 3, 4, 5, 6], 2), [[1, 2], [3, 4], [5, 6]])
+      // tslint:disable-next-line: deprecation
+      assert.deepStrictEqual(chunksOf([1, 2, 3, 4, 5], 5), [[1, 2, 3, 4, 5]])
+      // tslint:disable-next-line: deprecation
+      assert.deepStrictEqual(chunksOf([1, 2, 3, 4, 5], 6), [[1, 2, 3, 4, 5]])
+      // tslint:disable-next-line: deprecation
+      assert.deepStrictEqual(chunksOf([1, 2, 3, 4, 5], 1), [[1], [2], [3], [4], [5]])
+      // tslint:disable-next-line: deprecation
+      assert.deepStrictEqual(chunksOf([1, 2], 0), [[1, 2]])
+      // tslint:disable-next-line: deprecation
+      assert.deepStrictEqual(chunksOf([1, 2], 10), [[1, 2]])
+      // tslint:disable-next-line: deprecation
+      assert.deepStrictEqual(chunksOf([1, 2], -1), [[1, 2]])
+
+      assert.deepStrictEqual(chunksOf(2)([1, 2, 3, 4, 5]), [[1, 2], [3, 4], [5]])
+      assert.deepStrictEqual(chunksOf(2)([1, 2, 3, 4, 5, 6]), [[1, 2], [3, 4], [5, 6]])
+      assert.deepStrictEqual(chunksOf(5)([1, 2, 3, 4, 5]), [[1, 2, 3, 4, 5]])
+      assert.deepStrictEqual(chunksOf(6)([1, 2, 3, 4, 5]), [[1, 2, 3, 4, 5]])
+      assert.deepStrictEqual(chunksOf(1)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]])
+      assert.deepStrictEqual(chunksOf(0)([1, 2]), [[1, 2]])
+      assert.deepStrictEqual(chunksOf(10)([1, 2]), [[1, 2]])
+      assert.deepStrictEqual(chunksOf(-1)([1, 2]), [[1, 2]])
+    })
+
+    // #897
+    it('returns an empty array if provided an empty array', () => {
+      assert.deepStrictEqual(chunksOf(1)([]), [])
+      assert.deepStrictEqual(chunksOf(2)([]), [])
+      assert.deepStrictEqual(chunksOf(0)([]), [])
+    })
 
-    assert.deepStrictEqual(chunksOf(2)([1, 2, 3, 4, 5]), [[1, 2], [3, 4], [5]])
-    assert.deepStrictEqual(chunksOf(2)([1, 2, 3, 4, 5, 6]), [[1, 2], [3, 4], [5, 6]])
-    assert.deepStrictEqual(chunksOf(5)([1, 2, 3, 4, 5]), [[1, 2, 3, 4, 5]])
-    assert.deepStrictEqual(chunksOf(6)([1, 2, 3, 4, 5]), [[1, 2, 3, 4, 5]])
-    assert.deepStrictEqual(chunksOf(1)([1, 2, 3, 4, 5]), [[1], [2], [3], [4], [5]])
-    assert.deepStrictEqual(chunksOf(1)([]), [[]])
-    assert.deepStrictEqual(chunksOf(2)([]), [[]])
-    assert.deepStrictEqual(chunksOf(0)([]), [[]])
-    assert.deepStrictEqual(chunksOf(0)([1, 2]), [[1, 2]])
-    assert.deepStrictEqual(chunksOf(10)([1, 2]), [[1, 2]])
-    assert.deepStrictEqual(chunksOf(-1)([1, 2]), [[1, 2]])
+    // #897
+    it('should respect the law: chunksOf(n)(xs).concat(chunksOf(n)(ys)) == chunksOf(n)(xs.concat(ys)))', () => {
+      const xs: Array<number> = []
+      const ys = [1, 2]
+      assert.deepStrictEqual(chunksOf(2)(xs).concat(chunksOf(2)(ys)), chunksOf(2)(xs.concat(ys)))
+      fc.assert(
+        fc.property(
+          fc.array(fc.integer()).filter(xs => xs.length % 2 === 0), // Ensures `xs.length` is even
+          fc.array(fc.integer()),
+          fc.integer(1, 1).map(x => x * 2), // Generates `n` to be even so that it evenly divides `xs`
+          (xs, ys, n) => {
+            const as = chunksOf(n)(xs).concat(chunksOf(n)(ys))
+            const bs = chunksOf(n)(xs.concat(ys))
+            isDeepStrictEqual(as, bs)
+          }
+        )
+      )
+    })
   })
 
   it('makeBy', () => {
diff --git a/test/NonEmptyArray2v.ts b/test/NonEmptyArray2v.ts
index a9dbe343..4601a52c 100644
--- a/test/NonEmptyArray2v.ts
+++ b/test/NonEmptyArray2v.ts
@@ -40,7 +40,7 @@ import * as C from '../src/Const'
 import { eqNumber } from '../src/Eq'
 import { showString } from '../src/Show'
 
-describe.only('NonEmptyArray2v', () => {
+describe('NonEmptyArray2v', () => {
   it('make', () => {
     // tslint:disable-next-line: deprecation
     assert.deepStrictEqual(make(1, [2]), fromNonEmptyArray([1, 2]))
@@ -137,6 +137,7 @@ describe.only('NonEmptyArray2v', () => {
   })
 
   it('foldMap', () => {
+    // tslint:disable-next-line: deprecation
     const old = F.foldMap(nonEmptyArray, monoidString)
     const foldMap = nonEmptyArray.foldMap(monoidString)
     const x1 = fromNonEmptyArray(['a', 'b', 'c'])
@@ -146,6 +147,7 @@ describe.only('NonEmptyArray2v', () => {
   })
 
   it('foldr', () => {
+    // tslint:disable-next-line: deprecation
     const old = F.foldr(nonEmptyArray)
     const foldr = nonEmptyArray.foldr
     const x1 = fromNonEmptyArray(['a', 'b', 'c'])