Repository: gcanti/fp-ts
Pull request #780 (2019-03-18 18:34:46)

New Feature: make all getTuple* variadic


Patch:
diff --git a/README.md b/README.md
index ae249c3b..8f760aaa 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 <h3 align="center">
   <a href="https://gcanti.github.io/fp-ts/">
-    <img src="fp-ts-logo.png">
+    <img src="./docs/fp-ts-logo.png">
   </a>
 </h3>
 
@@ -36,6 +36,7 @@ A distinctive feature of `fp-ts` with respect to other functional libraries is i
 
 <!-- START doctoc generated TOC please keep comment here to allow auto update -->
 <!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+
 **Table of contents**
 
 - [Installation](#installation)
@@ -67,12 +68,12 @@ The stable version is tested against **TypeScript 3.3.3**, but should run with T
 
 - [Docs](https://gcanti.github.io/fp-ts)
 - [Tutorial](https://gcanti.github.io/fp-ts/basics/)
-- [Ecosystem](https://gcanti.github.io/fp-ts/introduction/ecosystem/)
+- [Ecosystem](https://gcanti.github.io/fp-ts/introduction/ecosystem)
 - [API Reference](https://gcanti.github.io/fp-ts/modules/)
 
 # Development
 
-- [Code conventions](https://github.com/gcanti/fp-ts/introduction/code-conventions)
+- [Code conventions](https://gcanti.github.io/fp-ts/introduction/code-conventions)
 
 # License
 
diff --git a/docs/modules/Monoid.ts.md b/docs/modules/Monoid.ts.md
index c53b451b..5e9c0c88 100644
--- a/docs/modules/Monoid.ts.md
+++ b/docs/modules/Monoid.ts.md
@@ -246,10 +246,26 @@ Added in v1.14.0
 
 # getTupleMonoid (function)
 
+Given a tuple of monoids returns a monoid for the tuple
+
 **Signature**
 
 ```ts
-export const getTupleMonoid = <A, B>(MA: Monoid<A>, MB: Monoid<B>): Monoid<[A, B]> => ...
+export const getTupleMonoid = <T extends Array<Monoid<any>>>(
+  ...monoids: T
+): Monoid<{ [K in keyof T]: T[K] extends Semigroup<infer A> ? A : never }> => ...
+```
+
+**Example**
+
+```ts
+import { getTupleMonoid, monoidString, monoidSum, monoidAll } from 'fp-ts/lib/Monoid'
+
+const M1 = getTupleMonoid(monoidString, monoidSum)
+assert.deepStrictEqual(M1.concat(['a', 1], ['b', 2]), ['ab', 3])
+
+const M2 = getTupleMonoid(monoidString, monoidSum, monoidAll)
+assert.deepStrictEqual(M2.concat(['a', 1, true], ['b', 2, false]), ['ab', 3, false])
 ```
 
 Added in v1.0.0
diff --git a/docs/modules/NonEmptyArray.ts.md b/docs/modules/NonEmptyArray.ts.md
index 493501f9..bbe5b04b 100644
--- a/docs/modules/NonEmptyArray.ts.md
+++ b/docs/modules/NonEmptyArray.ts.md
@@ -475,6 +475,8 @@ Added in v1.14.0
 
 ## ~~index~~ (method)
 
+Use `lookup` instead
+
 **Signature**
 
 ```ts
@@ -490,7 +492,7 @@ Find the first element which satisfies a predicate (or a refinement) function
 **Signature**
 
 ```ts
-findFirst<B extends A>(predicate: Refinement<A, B>): Option<B>
+findFirst<B extends A>(refinement: Refinement<A, B>): Option<B>
 findFirst(predicate: Predicate<A>): Option<A> { ... }
 ```
 
@@ -683,7 +685,6 @@ export const URI = ...
 ```ts
 export const nonEmptyArray: Monad1<URI> &
   Comonad1<URI> &
-  Foldable2v1<URI> &
   TraversableWithIndex1<URI, number> &
   FunctorWithIndex1<URI, number> &
   FoldableWithIndex1<URI, number> = ...
@@ -693,7 +694,7 @@ Added in v1.0.0
 
 # fromArray (function)
 
-Builds `NonEmptyArray` from `Array` returning `Option.none` or `Option.some` depending on amount of values in passed array
+Builds a `NonEmptyArray` from an `Array` returning `none` if `as` is an empty array
 
 **Signature**
 
@@ -705,7 +706,7 @@ Added in v1.0.0
 
 # getSemigroup (function)
 
-Builds `Semigroup` instance for `NonEmptyArray` of specified type arument
+Builds a `Semigroup` instance for `NonEmptyArray`
 
 **Signature**
 
diff --git a/docs/modules/Option.ts.md b/docs/modules/Option.ts.md
index e8a4b38e..efaed823 100644
--- a/docs/modules/Option.ts.md
+++ b/docs/modules/Option.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Option.ts
-nav_order: 60
+nav_order: 61
 parent: Modules
 ---
 
diff --git a/docs/modules/OptionT.ts.md b/docs/modules/OptionT.ts.md
index b0d896b5..5543c2ee 100644
--- a/docs/modules/OptionT.ts.md
+++ b/docs/modules/OptionT.ts.md
@@ -1,6 +1,6 @@
 ---
 title: OptionT.ts
-nav_order: 61
+nav_order: 62
 parent: Modules
 ---
 
diff --git a/docs/modules/Ord.ts.md b/docs/modules/Ord.ts.md
index dd8da039..2fbf4a09 100644
--- a/docs/modules/Ord.ts.md
+++ b/docs/modules/Ord.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Ord.ts
-nav_order: 62
+nav_order: 63
 parent: Modules
 ---
 
@@ -173,10 +173,25 @@ Added in v1.0.0
 
 # getTupleOrd (function)
 
+Given a tuple of `Ord`s returns an `Ord` for the tuple
+
 **Signature**
 
 ```ts
-export const getTupleOrd = <A, B>(OA: Ord<A>, OB: Ord<B>): Ord<[A, B]> => ...
+export const getTupleOrd = <T extends Array<Ord<any>>>(
+  ...ords: T
+): Ord<{ [K in keyof T]: T[K] extends Ord<infer A> ? A : never }> => ...
+```
+
+**Example**
+
+```ts
+import { getTupleOrd, ordString, ordNumber, ordBoolean } from 'fp-ts/lib/Ord'
+
+const O = getTupleOrd(ordString, ordNumber, ordBoolean)
+assert.strictEqual(O.compare(['a', 1, true], ['b', 2, true]), -1)
+assert.strictEqual(O.compare(['a', 1, true], ['a', 2, true]), -1)
+assert.strictEqual(O.compare(['a', 1, true], ['a', 1, false]), 1)
 ```
 
 Added in v1.14.3
diff --git a/docs/modules/Ordering.ts.md b/docs/modules/Ordering.ts.md
index 4642b633..5dbc4dc2 100644
--- a/docs/modules/Ordering.ts.md
+++ b/docs/modules/Ordering.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Ordering.ts
-nav_order: 63
+nav_order: 64
 parent: Modules
 ---
 
diff --git a/docs/modules/Pair.ts.md b/docs/modules/Pair.ts.md
index 5ae5270e..c2bc2962 100644
--- a/docs/modules/Pair.ts.md
+++ b/docs/modules/Pair.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Pair.ts
-nav_order: 64
+nav_order: 65
 parent: Modules
 ---
 
diff --git a/docs/modules/Plus.ts.md b/docs/modules/Plus.ts.md
index b71274fa..1297f726 100644
--- a/docs/modules/Plus.ts.md
+++ b/docs/modules/Plus.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Plus.ts
-nav_order: 65
+nav_order: 66
 parent: Modules
 ---
 
diff --git a/docs/modules/Profunctor.ts.md b/docs/modules/Profunctor.ts.md
index 3a1f9520..a94cad73 100644
--- a/docs/modules/Profunctor.ts.md
+++ b/docs/modules/Profunctor.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Profunctor.ts
-nav_order: 66
+nav_order: 67
 parent: Modules
 ---
 
diff --git a/docs/modules/Random.ts.md b/docs/modules/Random.ts.md
index d382331e..15e0de18 100644
--- a/docs/modules/Random.ts.md
+++ b/docs/modules/Random.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Random.ts
-nav_order: 67
+nav_order: 68
 parent: Modules
 ---
 
diff --git a/docs/modules/Reader.ts.md b/docs/modules/Reader.ts.md
index 32d38a11..977290ad 100644
--- a/docs/modules/Reader.ts.md
+++ b/docs/modules/Reader.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Reader.ts
-nav_order: 68
+nav_order: 69
 parent: Modules
 ---
 
diff --git a/docs/modules/ReaderT.ts.md b/docs/modules/ReaderT.ts.md
index 7c804257..a8d32d11 100644
--- a/docs/modules/ReaderT.ts.md
+++ b/docs/modules/ReaderT.ts.md
@@ -1,6 +1,6 @@
 ---
 title: ReaderT.ts
-nav_order: 69
+nav_order: 70
 parent: Modules
 ---
 
diff --git a/docs/modules/ReaderTaskEither.ts.md b/docs/modules/ReaderTaskEither.ts.md
index b1bad276..c788b164 100644
--- a/docs/modules/ReaderTaskEither.ts.md
+++ b/docs/modules/ReaderTaskEither.ts.md
@@ -1,6 +1,6 @@
 ---
 title: ReaderTaskEither.ts
-nav_order: 70
+nav_order: 71
 parent: Modules
 ---
 
diff --git a/docs/modules/Record.ts.md b/docs/modules/Record.ts.md
index bc9d30d3..356d5e19 100644
--- a/docs/modules/Record.ts.md
+++ b/docs/modules/Record.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Record.ts
-nav_order: 71
+nav_order: 72
 parent: Modules
 ---
 
diff --git a/docs/modules/Ring.ts.md b/docs/modules/Ring.ts.md
index 81a06230..a58c911b 100644
--- a/docs/modules/Ring.ts.md
+++ b/docs/modules/Ring.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Ring.ts
-nav_order: 72
+nav_order: 73
 parent: Modules
 ---
 
@@ -62,10 +62,28 @@ Added in v1.0.0
 
 # getTupleRing (function)
 
+Given a tuple of `Ring`s returns a `Ring` for the tuple
+
 **Signature**
 
 ```ts
-export const getTupleRing = <A, B>(RA: Ring<A>, RB: Ring<B>): Ring<[A, B]> => ...
+export const getTupleRing = <T extends Array<Ring<any>>>(
+  ...rings: T
+): Ring<{ [K in keyof T]: T[K] extends Ring<infer A> ? A : never }> => ...
+```
+
+**Example**
+
+```ts
+import { getTupleRing } from 'fp-ts/lib/Ring'
+import { fieldNumber } from 'fp-ts/lib/Field'
+
+const R = getTupleRing(fieldNumber, fieldNumber, fieldNumber)
+assert.deepStrictEqual(R.add([1, 2, 3], [4, 5, 6]), [5, 7, 9])
+assert.deepStrictEqual(R.mul([1, 2, 3], [4, 5, 6]), [4, 10, 18])
+assert.deepStrictEqual(R.one, [1, 1, 1])
+assert.deepStrictEqual(R.sub([1, 2, 3], [4, 5, 6]), [-3, -3, -3])
+assert.deepStrictEqual(R.zero, [0, 0, 0])
 ```
 
 Added in v1.14.3
diff --git a/docs/modules/Semigroup.ts.md b/docs/modules/Semigroup.ts.md
index 4d79af48..8f4b3283 100644
--- a/docs/modules/Semigroup.ts.md
+++ b/docs/modules/Semigroup.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Semigroup.ts
-nav_order: 73
+nav_order: 74
 parent: Modules
 ---
 
@@ -277,10 +277,26 @@ Added in v1.14.0
 
 # getTupleSemigroup (function)
 
+Given a tuple of semigroups returns a semigroup for the tuple
+
 **Signature**
 
 ```ts
-export const getTupleSemigroup = <A, B>(SA: Semigroup<A>, SB: Semigroup<B>): Semigroup<[A, B]> => ...
+export const getTupleSemigroup = <T extends Array<Semigroup<any>>>(
+  ...semigroups: T
+): Semigroup<{ [K in keyof T]: T[K] extends Semigroup<infer A> ? A : never }> => ...
+```
+
+**Example**
+
+```ts
+import { getTupleSemigroup, semigroupString, semigroupSum, semigroupAll } from 'fp-ts/lib/Semigroup'
+
+const S1 = getTupleSemigroup(semigroupString, semigroupSum)
+assert.deepStrictEqual(S1.concat(['a', 1], ['b', 2]), ['ab', 3])
+
+const S2 = getTupleSemigroup(semigroupString, semigroupSum, semigroupAll)
+assert.deepStrictEqual(S2.concat(['a', 1, true], ['b', 2, false]), ['ab', 3, false])
 ```
 
 Added in v1.14.0
diff --git a/docs/modules/Semigroupoid.ts.md b/docs/modules/Semigroupoid.ts.md
index d2b218a4..d408ade4 100644
--- a/docs/modules/Semigroupoid.ts.md
+++ b/docs/modules/Semigroupoid.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Semigroupoid.ts
-nav_order: 74
+nav_order: 75
 parent: Modules
 ---
 
diff --git a/docs/modules/Semiring.ts.md b/docs/modules/Semiring.ts.md
index d6222862..9db9cb43 100644
--- a/docs/modules/Semiring.ts.md
+++ b/docs/modules/Semiring.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Semiring.ts
-nav_order: 75
+nav_order: 76
 parent: Modules
 ---
 
diff --git a/docs/modules/Set.ts.md b/docs/modules/Set.ts.md
index 25dc5c81..72358e60 100644
--- a/docs/modules/Set.ts.md
+++ b/docs/modules/Set.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Set.ts
-nav_order: 76
+nav_order: 77
 parent: Modules
 ---
 
diff --git a/docs/modules/Setoid.ts.md b/docs/modules/Setoid.ts.md
index 30f5c82c..b793c404 100644
--- a/docs/modules/Setoid.ts.md
+++ b/docs/modules/Setoid.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Setoid.ts
-nav_order: 77
+nav_order: 78
 parent: Modules
 ---
 
@@ -160,10 +160,26 @@ Added in v1.14.2
 
 # getTupleSetoid (function)
 
+Given a tuple of `Setoid`s returns a `Setoid` for the tuple
+
 **Signature**
 
 ```ts
-export const getTupleSetoid = <A, B>(SA: Setoid<A>, SB: Setoid<B>): Setoid<[A, B]> => ...
+export const getTupleSetoid = <T extends Array<Setoid<any>>>(
+  ...setoids: T
+): Setoid<{ [K in keyof T]: T[K] extends Setoid<infer A> ? A : never }> => ...
+```
+
+**Example**
+
+```ts
+import { getTupleSetoid, setoidString, setoidNumber, setoidBoolean } from 'fp-ts/lib/Setoid'
+
+const S = getTupleSetoid(setoidString, setoidNumber, setoidBoolean)
+assert.strictEqual(S.equals(['a', 1, true], ['a', 1, true]), true)
+assert.strictEqual(S.equals(['a', 1, true], ['b', 1, true]), false)
+assert.strictEqual(S.equals(['a', 1, true], ['a', 2, true]), false)
+assert.strictEqual(S.equals(['a', 1, true], ['a', 1, false]), false)
 ```
 
 Added in v1.14.2
diff --git a/docs/modules/State.ts.md b/docs/modules/State.ts.md
index 7ccbbc93..625e26ff 100644
--- a/docs/modules/State.ts.md
+++ b/docs/modules/State.ts.md
@@ -1,6 +1,6 @@
 ---
 title: State.ts
-nav_order: 78
+nav_order: 79
 parent: Modules
 ---
 
diff --git a/docs/modules/StateT.ts.md b/docs/modules/StateT.ts.md
index 57c7340e..613c5b25 100644
--- a/docs/modules/StateT.ts.md
+++ b/docs/modules/StateT.ts.md
@@ -1,6 +1,6 @@
 ---
 title: StateT.ts
-nav_order: 79
+nav_order: 80
 parent: Modules
 ---
 
diff --git a/docs/modules/Store.ts.md b/docs/modules/Store.ts.md
index 3c6ab181..bcda55f5 100644
--- a/docs/modules/Store.ts.md
+++ b/docs/modules/Store.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Store.ts
-nav_order: 80
+nav_order: 81
 parent: Modules
 ---
 
diff --git a/docs/modules/StrMap.ts.md b/docs/modules/StrMap.ts.md
index d530fdec..d44d2cf3 100644
--- a/docs/modules/StrMap.ts.md
+++ b/docs/modules/StrMap.ts.md
@@ -1,6 +1,6 @@
 ---
 title: StrMap.ts
-nav_order: 81
+nav_order: 82
 parent: Modules
 ---
 
diff --git a/docs/modules/Strong.ts.md b/docs/modules/Strong.ts.md
index 051447bf..72095531 100644
--- a/docs/modules/Strong.ts.md
+++ b/docs/modules/Strong.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Strong.ts
-nav_order: 82
+nav_order: 83
 parent: Modules
 ---
 
diff --git a/docs/modules/Task.ts.md b/docs/modules/Task.ts.md
index e569acd8..ae78ad85 100644
--- a/docs/modules/Task.ts.md
+++ b/docs/modules/Task.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Task.ts
-nav_order: 83
+nav_order: 84
 parent: Modules
 ---
 
diff --git a/docs/modules/TaskEither.ts.md b/docs/modules/TaskEither.ts.md
index 17baa4f1..a5e2583f 100644
--- a/docs/modules/TaskEither.ts.md
+++ b/docs/modules/TaskEither.ts.md
@@ -1,6 +1,6 @@
 ---
 title: TaskEither.ts
-nav_order: 84
+nav_order: 85
 parent: Modules
 ---
 
diff --git a/docs/modules/These.ts.md b/docs/modules/These.ts.md
index 25eec201..4c5e0595 100644
--- a/docs/modules/These.ts.md
+++ b/docs/modules/These.ts.md
@@ -1,6 +1,6 @@
 ---
 title: These.ts
-nav_order: 85
+nav_order: 86
 parent: Modules
 ---
 
diff --git a/docs/modules/Trace.ts.md b/docs/modules/Trace.ts.md
index 037d04af..ca86afc2 100644
--- a/docs/modules/Trace.ts.md
+++ b/docs/modules/Trace.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Trace.ts
-nav_order: 86
+nav_order: 87
 parent: Modules
 ---
 
diff --git a/docs/modules/Traversable.ts.md b/docs/modules/Traversable.ts.md
index 9230e06c..b4bc740c 100644
--- a/docs/modules/Traversable.ts.md
+++ b/docs/modules/Traversable.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Traversable.ts
-nav_order: 87
+nav_order: 88
 parent: Modules
 ---
 
diff --git a/docs/modules/Traversable2v.ts.md b/docs/modules/Traversable2v.ts.md
index 2e780e1a..1bdc1534 100644
--- a/docs/modules/Traversable2v.ts.md
+++ b/docs/modules/Traversable2v.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Traversable2v.ts
-nav_order: 88
+nav_order: 89
 parent: Modules
 ---
 
diff --git a/docs/modules/TraversableWithIndex.ts.md b/docs/modules/TraversableWithIndex.ts.md
index 11a382dc..4cf80fb3 100644
--- a/docs/modules/TraversableWithIndex.ts.md
+++ b/docs/modules/TraversableWithIndex.ts.md
@@ -1,6 +1,6 @@
 ---
 title: TraversableWithIndex.ts
-nav_order: 89
+nav_order: 90
 parent: Modules
 ---
 
diff --git a/docs/modules/Tree.ts.md b/docs/modules/Tree.ts.md
index a9319832..cd25ae3e 100644
--- a/docs/modules/Tree.ts.md
+++ b/docs/modules/Tree.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Tree.ts
-nav_order: 90
+nav_order: 91
 parent: Modules
 ---
 
diff --git a/docs/modules/Tuple.ts.md b/docs/modules/Tuple.ts.md
index b41b1901..d0117d66 100644
--- a/docs/modules/Tuple.ts.md
+++ b/docs/modules/Tuple.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Tuple.ts
-nav_order: 91
+nav_order: 92
 parent: Modules
 ---
 
diff --git a/docs/modules/Unfoldable.ts.md b/docs/modules/Unfoldable.ts.md
index abba0286..5c5fc0f0 100644
--- a/docs/modules/Unfoldable.ts.md
+++ b/docs/modules/Unfoldable.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Unfoldable.ts
-nav_order: 92
+nav_order: 93
 parent: Modules
 ---
 
diff --git a/docs/modules/Validation.ts.md b/docs/modules/Validation.ts.md
index 3a8ba97c..ff3e0fbe 100644
--- a/docs/modules/Validation.ts.md
+++ b/docs/modules/Validation.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Validation.ts
-nav_order: 93
+nav_order: 94
 parent: Modules
 ---
 
diff --git a/docs/modules/Witherable.ts.md b/docs/modules/Witherable.ts.md
index e2cdabef..661b9515 100644
--- a/docs/modules/Witherable.ts.md
+++ b/docs/modules/Witherable.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Witherable.ts
-nav_order: 94
+nav_order: 95
 parent: Modules
 ---
 
diff --git a/docs/modules/Writer.ts.md b/docs/modules/Writer.ts.md
index d9861a4b..6439aebc 100644
--- a/docs/modules/Writer.ts.md
+++ b/docs/modules/Writer.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Writer.ts
-nav_order: 95
+nav_order: 96
 parent: Modules
 ---
 
diff --git a/docs/modules/Zipper.ts.md b/docs/modules/Zipper.ts.md
index f9e286c5..5cb5104d 100644
--- a/docs/modules/Zipper.ts.md
+++ b/docs/modules/Zipper.ts.md
@@ -1,6 +1,6 @@
 ---
 title: Zipper.ts
-nav_order: 96
+nav_order: 97
 parent: Modules
 ---
 
diff --git a/docs/modules/function.ts.md b/docs/modules/function.ts.md
index 9258e1b3..7a39af1f 100644
--- a/docs/modules/function.ts.md
+++ b/docs/modules/function.ts.md
@@ -644,7 +644,7 @@ Added in v1.0.0
 **Signature**
 
 ```ts
-export const tuple = <A, B>(a: A, b: B): [A, B] => ...
+export const tuple = <T extends Array<any>>(...t: T): T => ...
 ```
 
 Added in v1.0.0
diff --git a/docs/modules/index.ts.md b/docs/modules/index.ts.md
index 66c4d973..6929e82a 100644
--- a/docs/modules/index.ts.md
+++ b/docs/modules/index.ts.md
@@ -66,6 +66,7 @@ parent: Modules
 - [monoid (export)](#monoid-export)
 - [monoidal (export)](#monoidal-export)
 - [nonEmptyArray (export)](#nonemptyarray-export)
+- [nonEmptyArray2v (export)](#nonemptyarray2v-export)
 - [option (export)](#option-export)
 - [optionT (export)](#optiont-export)
 - [ord (export)](#ord-export)
@@ -570,6 +571,14 @@ export { monoidal }
 export { nonEmptyArray }
 ```
 
+# nonEmptyArray2v (export)
+
+**Signature**
+
+```ts
+export { nonEmptyArray2v }
+```
+
 # option (export)
 
 **Signature**
diff --git a/dtslint/ts3.1/index.ts b/dtslint/ts3.1/index.ts
index d9d825ee..9a65daaa 100644
--- a/dtslint/ts3.1/index.ts
+++ b/dtslint/ts3.1/index.ts
@@ -24,6 +24,8 @@ import * as SM from '../../src/StrMap'
 import * as Fo from '../../src/Foldable'
 import * as Or from '../../src/Ord'
 import * as Fu from '../../src/function'
+import * as Ring from '../../src/Ring'
+import * as Field from '../../src/Field'
 
 const double = (n: number): number => n * 2
 const len = (s: string): number => s.length
@@ -267,6 +269,18 @@ declare const fromFoldableF1: Fo.Foldable<'Test'>
 declare const fromFoldableInput1: H.HKT<'Test', ['a' | 'b', number]>
 const fromFoldable1 = R.fromFoldable(fromFoldableF1)(fromFoldableInput1, a => a) // $ExpectType Record<"a" | "b", number>
 
+//
+// Setoid
+//
+
+const Setoid1 = Se.getTupleSetoid(Se.setoidString, Se.setoidNumber, Se.setoidBoolean) // $ExpectType Setoid<[string, number, boolean]>
+
+//
+// Ord
+//
+
+const Ord1 = Or.getTupleOrd(Or.ordString, Or.ordNumber, Or.ordBoolean) // $ExpectType Ord<[string, number, boolean]>
+
 //
 // Semigroup
 //
@@ -274,6 +288,8 @@ const fromFoldable1 = R.fromFoldable(fromFoldableF1)(fromFoldableInput1, a => a)
 const Sem1 = S.getDictionarySemigroup(S.semigroupSum) // $ExpectType Semigroup<Record<string, number>>
 const Sem2 = S.getDictionarySemigroup<Keys, number>(S.semigroupSum) // $ExpectType Semigroup<Record<Keys, number>>
 
+const Sem3 = S.getTupleSemigroup(S.semigroupString, S.semigroupSum, S.semigroupAll) // $ExpectType Semigroup<[string, number, boolean]>
+
 //
 // Monoid
 //
@@ -281,6 +297,14 @@ const Sem2 = S.getDictionarySemigroup<Keys, number>(S.semigroupSum) // $ExpectTy
 const Mon3 = Mon.getDictionaryMonoid(S.semigroupSum) // $ExpectType Monoid<Record<string, number>>
 const Mon4 = Mon.getDictionaryMonoid<Keys, number>(S.semigroupSum) // $ExpectType Monoid<Record<Keys, number>>
 
+const Mon5 = Mon.getTupleMonoid(Mon.monoidString, Mon.monoidSum, Mon.monoidAll) // $ExpectType Monoid<[string, number, boolean]>
+
+//
+// Ring
+//
+
+const Ring1 = Ring.getTupleRing(Field.fieldNumber, Field.fieldNumber, Field.fieldNumber) // $ExpectType Ring<[number, number, number]>
+
 //
 // StrMap
 //
diff --git a/src/Monoid.ts b/src/Monoid.ts
index 6f7b9e9e..1054b70c 100644
--- a/src/Monoid.ts
+++ b/src/Monoid.ts
@@ -33,13 +33,26 @@ export const fold = <A>(M: Monoid<A>): ((as: Array<A>) => A) => {
 }
 
 /**
+ * Given a tuple of monoids returns a monoid for the tuple
+ *
+ * @example
+ * import { getTupleMonoid, monoidString, monoidSum, monoidAll } from 'fp-ts/lib/Monoid'
+ *
+ * const M1 = getTupleMonoid(monoidString, monoidSum)
+ * assert.deepStrictEqual(M1.concat(['a', 1], ['b', 2]), ['ab', 3])
+ *
+ * const M2 = getTupleMonoid(monoidString, monoidSum, monoidAll)
+ * assert.deepStrictEqual(M2.concat(['a', 1, true], ['b', 2, false]), ['ab', 3, false])
+ *
  * @since 1.0.0
  */
-export const getTupleMonoid = <A, B>(MA: Monoid<A>, MB: Monoid<B>): Monoid<[A, B]> => {
+export const getTupleMonoid = <T extends Array<Monoid<any>>>(
+  ...monoids: T
+): Monoid<{ [K in keyof T]: T[K] extends Semigroup<infer A> ? A : never }> => {
   return {
-    ...getTupleSemigroup(MA, MB),
-    empty: [MA.empty, MB.empty]
-  }
+    ...getTupleSemigroup(...monoids),
+    empty: monoids.map(m => m.empty)
+  } as any
 }
 
 /**
diff --git a/src/Ord.ts b/src/Ord.ts
index 001ffe6c..336b77da 100644
--- a/src/Ord.ts
+++ b/src/Ord.ts
@@ -156,12 +156,31 @@ export const getSemigroup = <A = never>(): Semigroup<Ord<A>> => {
 }
 
 /**
+ * Given a tuple of `Ord`s returns an `Ord` for the tuple
+ *
+ * @example
+ * import { getTupleOrd, ordString, ordNumber, ordBoolean } from 'fp-ts/lib/Ord'
+ *
+ * const O = getTupleOrd(ordString, ordNumber, ordBoolean)
+ * assert.strictEqual(O.compare(['a', 1, true], ['b', 2, true]), -1)
+ * assert.strictEqual(O.compare(['a', 1, true], ['a', 2, true]), -1)
+ * assert.strictEqual(O.compare(['a', 1, true], ['a', 1, false]), 1)
+ *
  * @since 1.14.3
  */
-export const getTupleOrd = <A, B>(OA: Ord<A>, OB: Ord<B>): Ord<[A, B]> => {
-  return fromCompare(([xa, xb], [ya, yb]) => {
-    const r = OA.compare(xa, ya)
-    return r === 0 ? OB.compare(xb, yb) : r
+export const getTupleOrd = <T extends Array<Ord<any>>>(
+  ...ords: T
+): Ord<{ [K in keyof T]: T[K] extends Ord<infer A> ? A : never }> => {
+  const len = ords.length
+  return fromCompare((x, y) => {
+    let i = 0
+    for (; i < len - 1; i++) {
+      const r = ords[i].compare(x[i], y[i])
+      if (r !== 0) {
+        return r
+      }
+    }
+    return ords[i].compare(x[i], y[i])
   })
 }
 
diff --git a/src/Ring.ts b/src/Ring.ts
index cbcabf01..3ce0db77 100644
--- a/src/Ring.ts
+++ b/src/Ring.ts
@@ -36,16 +36,31 @@ export const negate = <A>(ring: Ring<A>) => (a: A): A => {
 }
 
 /**
+ * Given a tuple of `Ring`s returns a `Ring` for the tuple
+ *
+ * @example
+ * import { getTupleRing } from 'fp-ts/lib/Ring'
+ * import { fieldNumber } from 'fp-ts/lib/Field'
+ *
+ * const R = getTupleRing(fieldNumber, fieldNumber, fieldNumber)
+ * assert.deepStrictEqual(R.add([1, 2, 3], [4, 5, 6]), [5, 7, 9])
+ * assert.deepStrictEqual(R.mul([1, 2, 3], [4, 5, 6]), [4, 10, 18])
+ * assert.deepStrictEqual(R.one, [1, 1, 1])
+ * assert.deepStrictEqual(R.sub([1, 2, 3], [4, 5, 6]), [-3, -3, -3])
+ * assert.deepStrictEqual(R.zero, [0, 0, 0])
+ *
  * @since 1.14.3
  */
-export const getTupleRing = <A, B>(RA: Ring<A>, RB: Ring<B>): Ring<[A, B]> => {
+export const getTupleRing = <T extends Array<Ring<any>>>(
+  ...rings: T
+): Ring<{ [K in keyof T]: T[K] extends Ring<infer A> ? A : never }> => {
   return {
-    add: ([a1, b1], [a2, b2]) => [RA.add(a1, a2), RB.add(b1, b2)],
-    zero: [RA.zero, RB.zero],
-    mul: ([a1, b1], [a2, b2]) => [RA.mul(a1, a2), RB.mul(b1, b2)],
-    one: [RA.one, RB.one],
-    sub: ([a1, b1], [a2, b2]) => [RA.sub(a1, a2), RB.sub(b1, b2)]
-  }
+    add: (x: any, y: any) => rings.map((R, i) => R.add(x[i], y[i])),
+    zero: rings.map(R => R.zero),
+    mul: (x: any, y: any) => rings.map((R, i) => R.mul(x[i], y[i])),
+    one: rings.map(R => R.one),
+    sub: (x: any, y: any) => rings.map((R, i) => R.sub(x[i], y[i]))
+  } as any
 }
 
 /**
diff --git a/src/Semigroup.ts b/src/Semigroup.ts
index e398c59c..096c7542 100644
--- a/src/Semigroup.ts
+++ b/src/Semigroup.ts
@@ -33,11 +33,24 @@ export const getLastSemigroup = <A = never>(): Semigroup<A> => {
 }
 
 /**
+ * Given a tuple of semigroups returns a semigroup for the tuple
+ *
+ * @example
+ * import { getTupleSemigroup, semigroupString, semigroupSum, semigroupAll } from 'fp-ts/lib/Semigroup'
+ *
+ * const S1 = getTupleSemigroup(semigroupString, semigroupSum)
+ * assert.deepStrictEqual(S1.concat(['a', 1], ['b', 2]), ['ab', 3])
+ *
+ * const S2 = getTupleSemigroup(semigroupString, semigroupSum, semigroupAll)
+ * assert.deepStrictEqual(S2.concat(['a', 1, true], ['b', 2, false]), ['ab', 3, false])
+ *
  * @since 1.14.0
  */
-export const getTupleSemigroup = <A, B>(SA: Semigroup<A>, SB: Semigroup<B>): Semigroup<[A, B]> => {
+export const getTupleSemigroup = <T extends Array<Semigroup<any>>>(
+  ...semigroups: T
+): Semigroup<{ [K in keyof T]: T[K] extends Semigroup<infer A> ? A : never }> => {
   return {
-    concat: ([xa, xb], [ya, yb]) => [SA.concat(xa, ya), SB.concat(xb, yb)]
+    concat: (x, y) => semigroups.map((s, i) => s.concat(x[i], y[i])) as any
   }
 }
 
diff --git a/src/Setoid.ts b/src/Setoid.ts
index 25a689b4..2b0dadb7 100644
--- a/src/Setoid.ts
+++ b/src/Setoid.ts
@@ -85,10 +85,23 @@ export const getRecordSetoid = <O extends { [key: string]: any }>(
 }
 
 /**
+ * Given a tuple of `Setoid`s returns a `Setoid` for the tuple
+ *
+ * @example
+ * import { getTupleSetoid, setoidString, setoidNumber, setoidBoolean } from 'fp-ts/lib/Setoid'
+ *
+ * const S = getTupleSetoid(setoidString, setoidNumber, setoidBoolean)
+ * assert.strictEqual(S.equals(['a', 1, true], ['a', 1, true]), true)
+ * assert.strictEqual(S.equals(['a', 1, true], ['b', 1, true]), false)
+ * assert.strictEqual(S.equals(['a', 1, true], ['a', 2, true]), false)
+ * assert.strictEqual(S.equals(['a', 1, true], ['a', 1, false]), false)
+ *
  * @since 1.14.2
  */
-export const getTupleSetoid = <A, B>(SA: Setoid<A>, SB: Setoid<B>): Setoid<[A, B]> => {
-  return fromEquals((a, b) => SA.equals(a[0], b[0]) && SB.equals(a[1], b[1]))
+export const getTupleSetoid = <T extends Array<Setoid<any>>>(
+  ...setoids: T
+): Setoid<{ [K in keyof T]: T[K] extends Setoid<infer A> ? A : never }> => {
+  return fromEquals((x, y) => setoids.every((S, i) => S.equals(x[i], y[i])))
 }
 
 /**


Test patch:
diff --git a/test/Monoid.ts b/test/Monoid.ts
index 58135ee5..c1e09cc9 100644
--- a/test/Monoid.ts
+++ b/test/Monoid.ts
@@ -13,12 +13,20 @@ import {
   monoidSum,
   getProductMonoid,
   getMeetMonoid,
-  getJoinMonoid
+  getJoinMonoid,
+  getTupleMonoid
 } from '../src/Monoid'
 import { semigroupSum } from '../src/Semigroup'
 import { boundedNumber } from '../src/Bounded'
 
 describe('Monoid', () => {
+  it('getTupleMonoid', () => {
+    const M1 = getTupleMonoid(monoidString, monoidSum)
+    assert.deepStrictEqual(M1.concat(['a', 1], ['b', 2]), ['ab', 3])
+    const M2 = getTupleMonoid(monoidString, monoidSum, monoidAll)
+    assert.deepStrictEqual(M2.concat(['a', 1, true], ['b', 2, false]), ['ab', 3, false])
+  })
+
   it('fold', () => {
     assert.strictEqual(fold(monoidSum)([1, 2, 3]), 6)
   })
diff --git a/test/Ord.ts b/test/Ord.ts
index f52fc154..de1bbb86 100644
--- a/test/Ord.ts
+++ b/test/Ord.ts
@@ -12,10 +12,19 @@ import {
   ordString,
   lessThanOrEq,
   greaterThanOrEq,
-  fromCompare
+  fromCompare,
+  getTupleOrd,
+  ordBoolean
 } from '../src/Ord'
 
 describe('Ord', () => {
+  it('getTupleOrd', () => {
+    const O = getTupleOrd(ordString, ordNumber, ordBoolean)
+    assert.strictEqual(O.compare(['a', 1, true], ['b', 2, true]), -1)
+    assert.strictEqual(O.compare(['a', 1, true], ['a', 2, true]), -1)
+    assert.strictEqual(O.compare(['a', 1, true], ['a', 1, false]), 1)
+  })
+
   it('getSemigroup', () => {
     type T = [number, string]
     const tuples: Array<T> = [[2, 'c'], [1, 'b'], [2, 'a'], [1, 'c']]
diff --git a/test/Ring.ts b/test/Ring.ts
index ee759cd3..599bc83c 100644
--- a/test/Ring.ts
+++ b/test/Ring.ts
@@ -1,8 +1,17 @@
 import * as assert from 'assert'
 import { fieldNumber } from '../src/Field'
-import { getProductRing, negate, getFunctionRing } from '../src/Ring'
+import { getProductRing, negate, getFunctionRing, getTupleRing } from '../src/Ring'
 
 describe('Ring', () => {
+  it('getTupleRing', () => {
+    const R = getTupleRing(fieldNumber, fieldNumber, fieldNumber)
+    assert.deepStrictEqual(R.add([1, 2, 3], [4, 5, 6]), [5, 7, 9])
+    assert.deepStrictEqual(R.mul([1, 2, 3], [4, 5, 6]), [4, 10, 18])
+    assert.deepStrictEqual(R.one, [1, 1, 1])
+    assert.deepStrictEqual(R.sub([1, 2, 3], [4, 5, 6]), [-3, -3, -3])
+    assert.deepStrictEqual(R.zero, [0, 0, 0])
+  })
+
   it('getProductRing', () => {
     // tslint:disable-next-line: deprecation
     const R = getProductRing(fieldNumber, fieldNumber)
diff --git a/test/Semigroup.ts b/test/Semigroup.ts
index 57d2ca3f..8a195905 100644
--- a/test/Semigroup.ts
+++ b/test/Semigroup.ts
@@ -13,10 +13,20 @@ import {
   getRecordSemigroup,
   semigroupProduct,
   semigroupSum,
-  semigroupVoid
+  semigroupVoid,
+  getTupleSemigroup,
+  semigroupString,
+  semigroupAll
 } from '../src/Semigroup'
 
 describe('Semigroup', () => {
+  it('getTupleSemigroup', () => {
+    const S1 = getTupleSemigroup(semigroupString, semigroupSum)
+    assert.deepStrictEqual(S1.concat(['a', 1], ['b', 2]), ['ab', 3])
+    const S2 = getTupleSemigroup(semigroupString, semigroupSum, semigroupAll)
+    assert.deepStrictEqual(S2.concat(['a', 1, true], ['b', 2, false]), ['ab', 3, false])
+  })
+
   it('fold', () => {
     assert.strictEqual(fold(monoidString)('')(['a', 'b', 'c']), 'abc')
   })
diff --git a/test/Setoid.ts b/test/Setoid.ts
index 80b51246..3134bd8a 100644
--- a/test/Setoid.ts
+++ b/test/Setoid.ts
@@ -6,10 +6,20 @@ import {
   setoidDate,
   setoidNumber,
   setoidString,
-  fromEquals
+  fromEquals,
+  getTupleSetoid,
+  setoidBoolean
 } from '../src/Setoid'
 
 describe('Setoid', () => {
+  it('getTupleSetoid', () => {
+    const S = getTupleSetoid(setoidString, setoidNumber, setoidBoolean)
+    assert.strictEqual(S.equals(['a', 1, true], ['a', 1, true]), true)
+    assert.strictEqual(S.equals(['a', 1, true], ['b', 1, true]), false)
+    assert.strictEqual(S.equals(['a', 1, true], ['a', 2, true]), false)
+    assert.strictEqual(S.equals(['a', 1, true], ['a', 1, false]), false)
+  })
+
   interface Person {
     name: string
     age: number