Repository: gcanti/fp-ts
Pull request #1272 (2020-07-21 04:48:19)

Foldable: add `toArray`
👋 

I saw an open PR, "Added Option.toArray" (https://github.com/gcanti/fp-ts/pull/1155), and noticed @gcanti recommended instead adding `toArray` to Foldable.

I went ahead and added it. However, I'm not 100% sure about the implementation.

In 2.x+, `Array` is deprecated in favor of `ReadOnlyArray`, but the original implementation of `toArray` uses a combination of `reduce` and `arr.push`.

**Possible problem**: `.push` does not exist on `ReadOnlyArray` since it mutates the array in place. This method is probably faster, but it might not desirable in the 2.x+ world.

Would it make sense to add another instance, `toReadOnlyArray`?

**Update**: I went ahead and also added a `toReadOnlyArray` instance. There appeared to be some prior art in the Map, Set, and Record modules.

I'm open to feedback and/or edits.

---
- [x] Fork [the repository](https://github.com/gcanti/fp-ts) and create your branch from `master`.
- [x] Run `npm install` in the repository root.
- [x] If you've fixed a bug or added code that should be tested, add tests!
- [x] Ensure the test suite passes (`npm test`).

gcanti: @newswim thanks for this PR, I would only keep the readonly array version (which is future proof, `fp-ts@3.x` will drop all mutable modules https://github.com/gcanti/fp-ts/issues/1226)

```ts
/**
 * Transforms a `Foldable` into a read-only array.
 *
 * @example
 * import { toArray } from 'fp-ts/lib/Foldable'
 * import { tree, make } from 'fp-ts/lib/Tree'
 *
 * const t = make(1, [make(2, []), make(3, []), make(4, [])])
 * assert.deepStrictEqual(toArray(tree)(t), [1, 2, 3, 4])
 *
 * @since 2.8.0
 */
export function toArray<F extends URIS4>(F: Foldable4<F>): <S, R, E, A>(fa: Kind4<F, S, R, E, A>) => ReadonlyArray<A>
export function toArray<F extends URIS3>(F: Foldable3<F>): <R, E, A>(fa: Kind3<F, R, E, A>) => ReadonlyArray<A>
export function toArray<F extends URIS3, E>(F: Foldable3C<F, E>): <R, A>(fa: Kind3<F, R, E, A>) => ReadonlyArray<A>
export function toArray<F extends URIS2>(F: Foldable2<F>): <E, A>(fa: Kind2<F, E, A>) => ReadonlyArray<A>
export function toArray<F extends URIS2, E>(F: Foldable2C<F, E>): <A>(fa: Kind2<F, E, A>) => ReadonlyArray<A>
export function toArray<F extends URIS>(F: Foldable1<F>): <A>(fa: Kind<F, A>) => ReadonlyArray<A>
export function toArray<F>(F: Foldable<F>): <A>(fa: HKT<F, A>) => ReadonlyArray<A>
export function toArray<F>(F: Foldable<F>): <A>(fa: HKT<F, A>) => ReadonlyArray<A> {
  return <A>(fa: HKT<F, A>) =>
    // tslint:disable-next-line: readonly-array
    F.reduce(fa, [], (acc: Array<A>, a) => {
      acc.push(a)
      return acc
    })
}
```

p.s.
docs are auto-generated by running `npm run docs`

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1d180917..e4f5c78a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -11,9 +11,28 @@
 > - [Experimental]
 > - [Deprecation]
 
-**Note**: Gaps between patch versions are faulty/broken releases. **Note**: A feature tagged as Experimental is in a
+**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.
 
+# 2.7.1
+
+- **Bug Fix**
+  - `ReadonlyArray`
+    - fix `FunctorWithIndex` instance name (@gcanti)
+    - fix `Functor` instance name (@gcanti)
+- **Polish**
+  - `Array`
+    - relax `sort` signature (@gcanti)
+    - relax `sortBy` signature (@gcanti)
+  - `Map`
+    - export `mapWithIndex` (@gcanti)
+  - `ReadonlyArray`
+    - relax `sort` signature (@gcanti)
+    - relax `sortBy` signature (@gcanti)
+  - `ReadonlyMap`
+    - export `mapWithIndex` (@gcanti)
+
 # 2.7.0
 
 - **Bug Fix**
diff --git a/docs/modules/Array.ts.md b/docs/modules/Array.ts.md
index ca0ae615..e8bf1196 100644
--- a/docs/modules/Array.ts.md
+++ b/docs/modules/Array.ts.md
@@ -59,7 +59,7 @@ Added in v2.0.0
   - [traverseWithIndex](#traversewithindex)
 - [Unfoldable](#unfoldable)
   - [unfold](#unfold)
-- [Whitherable](#whitherable)
+- [Witherable](#witherable)
   - [wilt](#wilt)
   - [wither](#wither)
 - [combinators](#combinators)
@@ -124,7 +124,7 @@ Added in v2.0.0
   - [URI](#uri)
   - [URI (type alias)](#uri-type-alias)
   - [Unfoldable](#unfoldable-1)
-  - [Witherable](#witherable)
+  - [Witherable](#witherable-1)
   - [array](#array)
   - [getEq](#geteq)
   - [getMonoid](#getmonoid)
@@ -545,7 +545,7 @@ export declare const unfold: <A, B>(b: B, f: (b: B) => Option<[A, B]>) => A[]
 
 Added in v2.6.6
 
-# Whitherable
+# Witherable
 
 ## wilt
 
@@ -841,7 +841,7 @@ Sort the elements of an array in increasing order, creating a new array
 **Signature**
 
 ```ts
-export declare const sort: <A>(O: Ord<A>) => (as: A[]) => A[]
+export declare const sort: <B>(O: Ord<B>) => <A extends B>(as: A[]) => A[]
 ```
 
 **Example**
@@ -863,7 +863,7 @@ etc...
 **Signature**
 
 ```ts
-export declare const sortBy: <A>(ords: Ord<A>[]) => (as: A[]) => A[]
+export declare const sortBy: <B>(ords: Ord<B>[]) => <A extends B>(as: A[]) => A[]
 ```
 
 **Example**
diff --git a/docs/modules/Foldable.ts.md b/docs/modules/Foldable.ts.md
index 366f4c9d..64bbcc01 100644
--- a/docs/modules/Foldable.ts.md
+++ b/docs/modules/Foldable.ts.md
@@ -32,6 +32,7 @@ Added in v2.0.0
   - [foldM](#foldm)
   - [getFoldableComposition](#getfoldablecomposition)
   - [intercalate](#intercalate)
+  - [toArray](#toarray)
   - [traverse\_](#traverse_)
 
 ---
@@ -399,6 +400,38 @@ assert.strictEqual(intercalate(monoidString, tree)('|', t), 'a|b|c|d')
 
 Added in v2.0.0
 
+## toArray
+
+Transforms a `Foldable` into a read-only array.
+
+**Signature**
+
+```ts
+export declare function toArray<F extends URIS4>(
+  F: Foldable4<F>
+): <S, R, E, A>(fa: Kind4<F, S, R, E, A>) => ReadonlyArray<A>
+export declare function toArray<F extends URIS3>(F: Foldable3<F>): <R, E, A>(fa: Kind3<F, R, E, A>) => ReadonlyArray<A>
+export declare function toArray<F extends URIS3, E>(
+  F: Foldable3C<F, E>
+): <R, A>(fa: Kind3<F, R, E, A>) => ReadonlyArray<A>
+export declare function toArray<F extends URIS2>(F: Foldable2<F>): <E, A>(fa: Kind2<F, E, A>) => ReadonlyArray<A>
+export declare function toArray<F extends URIS2, E>(F: Foldable2C<F, E>): <A>(fa: Kind2<F, E, A>) => ReadonlyArray<A>
+export declare function toArray<F extends URIS>(F: Foldable1<F>): <A>(fa: Kind<F, A>) => ReadonlyArray<A>
+export declare function toArray<F>(F: Foldable<F>): <A>(fa: HKT<F, A>) => ReadonlyArray<A>
+```
+
+**Example**
+
+```ts
+import { toArray } from 'fp-ts/lib/Foldable'
+import { tree, make } from 'fp-ts/lib/Tree'
+
+const t = make(1, [make(2, []), make(3, []), make(4, [])])
+assert.deepStrictEqual(toArray(tree)(t), [1, 2, 3, 4])
+```
+
+Added in v2.8.0
+
 ## traverse\_
 
 Traverse a data structure, performing some effects encoded by an `Applicative` functor at each value, ignoring the
diff --git a/docs/modules/Map.ts.md b/docs/modules/Map.ts.md
index e56b6507..ba725df2 100644
--- a/docs/modules/Map.ts.md
+++ b/docs/modules/Map.ts.md
@@ -22,6 +22,8 @@ Added in v2.0.0
   - [partitionMap](#partitionmap)
 - [Functor](#functor)
   - [map](#map)
+- [FunctorWithIndex](#functorwithindex)
+  - [mapWithIndex](#mapwithindex)
 - [combinators](#combinators)
   - [deleteAt](#deleteat)
   - [insertAt](#insertat)
@@ -142,11 +144,23 @@ use the type constructor `F` to represent some computational context.
 **Signature**
 
 ```ts
-export declare const map: <A, B>(f: (a: A) => B) => <E>(fa: Map<E, A>) => Map<E, B>
+export declare const map: <A, B>(f: (a: A) => B) => <K>(fa: Map<K, A>) => Map<K, B>
 ```
 
 Added in v2.0.0
 
+# FunctorWithIndex
+
+## mapWithIndex
+
+**Signature**
+
+```ts
+export declare const mapWithIndex: <K, A, B>(f: (k: K, a: A) => B) => (fa: Map<K, A>) => Map<K, B>
+```
+
+Added in v2.7.1
+
 # combinators
 
 ## deleteAt
diff --git a/docs/modules/Option.ts.md b/docs/modules/Option.ts.md
index 902d8ed2..4bff4cbb 100644
--- a/docs/modules/Option.ts.md
+++ b/docs/modules/Option.ts.md
@@ -59,7 +59,7 @@ Added in v2.0.0
 - [Traversable](#traversable)
   - [sequence](#sequence)
   - [traverse](#traverse)
-- [Whitherable](#whitherable)
+- [Witherable](#witherable)
   - [wilt](#wilt)
   - [wither](#wither)
 - [combinators](#combinators)
@@ -96,7 +96,7 @@ Added in v2.0.0
   - [Traversable](#traversable-1)
   - [URI](#uri)
   - [URI (type alias)](#uri-type-alias)
-  - [Witherable](#witherable)
+  - [Witherable](#witherable-1)
   - [getApplyMonoid](#getapplymonoid)
   - [getApplySemigroup](#getapplysemigroup)
   - [getEq](#geteq)
@@ -427,7 +427,7 @@ export declare const traverse: PipeableTraverse1<'Option'>
 
 Added in v2.6.3
 
-# Whitherable
+# Witherable
 
 ## wilt
 
diff --git a/docs/modules/ReadonlyArray.ts.md b/docs/modules/ReadonlyArray.ts.md
index 8ef7f126..0d544051 100644
--- a/docs/modules/ReadonlyArray.ts.md
+++ b/docs/modules/ReadonlyArray.ts.md
@@ -61,7 +61,7 @@ Added in v2.5.0
   - [traverseWithIndex](#traversewithindex)
 - [Unfoldable](#unfoldable)
   - [unfold](#unfold)
-- [Whitherable](#whitherable)
+- [Witherable](#witherable)
   - [wilt](#wilt)
   - [wither](#wither)
 - [combinators](#combinators)
@@ -108,14 +108,14 @@ Added in v2.5.0
   - [Foldable](#foldable-1)
   - [FoldableWithIndex](#foldablewithindex-1)
   - [Functor](#functor-1)
+  - [FunctorWithIndex](#functorwithindex-1)
   - [Monad](#monad-1)
   - [Traversable](#traversable-1)
   - [TraversableWithIndex](#traversablewithindex-1)
   - [URI](#uri)
   - [URI (type alias)](#uri-type-alias)
   - [Unfoldable](#unfoldable-1)
-  - [Witherable](#witherable)
-  - [functorArray](#functorarray)
+  - [Witherable](#witherable-1)
   - [getEq](#geteq)
   - [getMonoid](#getmonoid)
   - [getOrd](#getord)
@@ -564,7 +564,7 @@ export declare const unfold: <A, B>(b: B, f: (b: B) => Option<readonly [A, B]>)
 
 Added in v2.6.6
 
-# Whitherable
+# Witherable
 
 ## wilt
 
@@ -841,7 +841,7 @@ Sort the elements of an array in increasing order, creating a new array
 **Signature**
 
 ```ts
-export declare function sort<A>(O: Ord<A>): (as: ReadonlyArray<A>) => ReadonlyArray<A>
+export declare const sort: <B>(O: Ord<B>) => <A extends B>(as: readonly A[]) => readonly A[]
 ```
 
 **Example**
@@ -863,7 +863,7 @@ etc...
 **Signature**
 
 ```ts
-export declare function sortBy<A>(ords: ReadonlyArray<Ord<A>>): (as: ReadonlyArray<A>) => ReadonlyArray<A>
+export declare function sortBy<B>(ords: ReadonlyArray<Ord<B>>): <A extends B>(as: ReadonlyArray<A>) => ReadonlyArray<A>
 ```
 
 **Example**
@@ -1324,7 +1324,17 @@ Added in v2.7.0
 **Signature**
 
 ```ts
-export declare const Functor: FunctorWithIndex1<'ReadonlyArray', number>
+export declare const Functor: Functor1<'ReadonlyArray'>
+```
+
+Added in v2.7.0
+
+## FunctorWithIndex
+
+**Signature**
+
+```ts
+export declare const FunctorWithIndex: FunctorWithIndex1<'ReadonlyArray', number>
 ```
 
 Added in v2.7.0
@@ -1399,16 +1409,6 @@ export declare const Witherable: Witherable1<'ReadonlyArray'>
 
 Added in v2.7.0
 
-## functorArray
-
-**Signature**
-
-```ts
-export declare const functorArray: Functor1<'ReadonlyArray'>
-```
-
-Added in v2.7.0
-
 ## getEq
 
 Derives an `Eq` over the `ReadonlyArray` of a given element type from the `Eq` of that type. The derived `Eq` defines two
diff --git a/docs/modules/ReadonlyMap.ts.md b/docs/modules/ReadonlyMap.ts.md
index c9229501..de76c18f 100644
--- a/docs/modules/ReadonlyMap.ts.md
+++ b/docs/modules/ReadonlyMap.ts.md
@@ -22,6 +22,8 @@ Added in v2.5.0
   - [partitionMap](#partitionmap)
 - [Functor](#functor)
   - [map](#map)
+- [FunctorWithIndex](#functorwithindex)
+  - [mapWithIndex](#mapwithindex)
 - [combinators](#combinators)
   - [deleteAt](#deleteat)
   - [insertAt](#insertat)
@@ -154,6 +156,18 @@ export declare const map: <A, B>(f: (a: A) => B) => <K>(fa: ReadonlyMap<K, A>) =
 
 Added in v2.5.0
 
+# FunctorWithIndex
+
+## mapWithIndex
+
+**Signature**
+
+```ts
+export declare const mapWithIndex: <K, A, B>(f: (k: K, a: A) => B) => (fa: ReadonlyMap<K, A>) => ReadonlyMap<K, B>
+```
+
+Added in v2.7.1
+
 # combinators
 
 ## deleteAt
diff --git a/docs/modules/ReadonlyRecord.ts.md b/docs/modules/ReadonlyRecord.ts.md
index d02db74d..d33fbf84 100644
--- a/docs/modules/ReadonlyRecord.ts.md
+++ b/docs/modules/ReadonlyRecord.ts.md
@@ -24,7 +24,7 @@ Added in v2.5.0
   - [foldMap](#foldmap)
   - [reduce](#reduce)
   - [reduceRight](#reduceright)
-- [Whitherable](#whitherable)
+- [Witherable](#witherable)
   - [wilt](#wilt)
   - [wither](#wither)
 - [combinators](#combinators)
@@ -52,7 +52,7 @@ Added in v2.5.0
   - [TraversableWithIndex](#traversablewithindex)
   - [URI](#uri)
   - [URI (type alias)](#uri-type-alias)
-  - [Witherable](#witherable)
+  - [Witherable](#witherable-1)
   - [getEq](#geteq)
   - [getMonoid](#getmonoid)
   - [getShow](#getshow)
@@ -200,7 +200,7 @@ export declare const reduceRight: <A, B>(b: B, f: (a: A, b: B) => B) => (fa: Rea
 
 Added in v2.5.0
 
-# Whitherable
+# Witherable
 
 ## wilt
 
diff --git a/docs/modules/Record.ts.md b/docs/modules/Record.ts.md
index fde041a1..416d2da6 100644
--- a/docs/modules/Record.ts.md
+++ b/docs/modules/Record.ts.md
@@ -24,7 +24,7 @@ Added in v2.0.0
   - [foldMap](#foldmap)
   - [reduce](#reduce)
   - [reduceRight](#reduceright)
-- [Whitherable](#whitherable)
+- [Witherable](#witherable)
   - [wilt](#wilt)
   - [wither](#wither)
 - [instances](#instances)
@@ -39,7 +39,7 @@ Added in v2.0.0
   - [TraversableWithIndex](#traversablewithindex)
   - [URI](#uri)
   - [URI (type alias)](#uri-type-alias)
-  - [Witherable](#witherable)
+  - [Witherable](#witherable-1)
   - [record](#record)
 - [utils](#utils)
   - [collect](#collect)
@@ -189,7 +189,7 @@ export declare const reduceRight: <A, B>(b: B, f: (a: A, b: B) => B) => (fa: Rec
 
 Added in v2.0.0
 
-# Whitherable
+# Witherable
 
 ## wilt
 
diff --git a/dtslint/ts3.5/Array.ts b/dtslint/ts3.5/Array.ts
index 320bf974..ab2f8d41 100644
--- a/dtslint/ts3.5/Array.ts
+++ b/dtslint/ts3.5/Array.ts
@@ -1,6 +1,7 @@
 import * as _ from '../../src/Array'
 import { pipe } from '../../src/pipeable'
 import { eqNumber } from '../../src/Eq'
+import { Ord } from '../../src/Ord'
 
 declare const us: Array<unknown>
 declare const ns: Array<number>
@@ -133,3 +134,31 @@ _.zip(['a', 'b']) // $ExpectType <A>(as: A[]) => [A, string][]
 
 _.cons(0, [1, 2]) // $ExpectType NonEmptyArray<number>
 _.cons(0) // $ExpectType (tail: number[]) => NonEmptyArray<number>
+
+//
+// sort
+//
+
+declare const ord1: Ord<{ readonly a: string }>
+interface X1 {
+  readonly a: string
+  readonly b: number
+}
+declare const x1s: Array<X1>
+
+_.sort(ord1)(x1s) // $ExpectType X1[]
+
+//
+// sortBy
+//
+
+declare const ord2: Ord<X1>
+declare const ord3: Ord<X1>
+interface X2 {
+  readonly a: string
+  readonly b: number
+  readonly c: boolean
+}
+declare const x2s: Array<X2>
+
+_.sortBy([ord2, ord3])(x2s) // $ExpectType X2[]
diff --git a/dtslint/ts3.5/ReadonlyArray.ts b/dtslint/ts3.5/ReadonlyArray.ts
index cc6ffc52..d11f4b1e 100644
--- a/dtslint/ts3.5/ReadonlyArray.ts
+++ b/dtslint/ts3.5/ReadonlyArray.ts
@@ -1,6 +1,7 @@
 import * as _ from '../../src/ReadonlyArray'
 import { pipe } from '../../src/pipeable'
 import { eqNumber } from '../../src/Eq'
+import { Ord } from '../../src/Ord'
 
 declare const rus: ReadonlyArray<unknown>
 declare const rns: ReadonlyArray<number>
@@ -124,3 +125,31 @@ _.zip(['a', 'b']) // $ExpectType <A>(as: readonly A[]) => readonly (readonly [A,
 
 _.cons(0, [1, 2]) // $ExpectType ReadonlyNonEmptyArray<number>
 _.cons(0) // $ExpectType (tail: readonly number[]) => ReadonlyNonEmptyArray<number>
+
+//
+// sort
+//
+
+declare const ord1: Ord<{ readonly a: string }>
+interface X1 {
+  readonly a: string
+  readonly b: number
+}
+declare const x1s: ReadonlyArray<X1>
+
+_.sort(ord1)(x1s) // $ExpectType ReadonlyArray<X1>
+
+//
+// sortBy
+//
+
+declare const ord2: Ord<X1>
+declare const ord3: Ord<X1>
+interface X2 {
+  readonly a: string
+  readonly b: number
+  readonly c: boolean
+}
+declare const x2s: ReadonlyArray<X2>
+
+_.sortBy([ord2, ord3])(x2s) // $ExpectType ReadonlyArray<X2>
diff --git a/package-lock.json b/package-lock.json
index 4673e3a4..5161958b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,6 +1,6 @@
 {
   "name": "fp-ts",
-  "version": "2.7.0",
+  "version": "2.7.1",
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
@@ -6811,9 +6811,9 @@
       "dev": true
     },
     "typescript": {
-      "version": "3.9.2",
-      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.2.tgz",
-      "integrity": "sha512-q2ktq4n/uLuNNShyayit+DTobV2ApPEo/6so68JaD5ojvc/6GClBipedB9zNWYxRSAlZXAe405Rlijzl6qDiSw==",
+      "version": "3.9.7",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-3.9.7.tgz",
+      "integrity": "sha512-BLbiRkiBzAwsjut4x/dsibSTB6yWpwT5qWmC2OfuCg3GgVQCSgMs4vEctYPhsaGtd0AeuuHMkjZ2h2WG8MSzRw==",
       "dev": true
     },
     "unc-path-regex": {
diff --git a/package.json b/package.json
index 0593bf15..e39f36fd 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "fp-ts",
-  "version": "2.7.0",
+  "version": "2.7.1",
   "description": "Functional programming in TypeScript",
   "files": [
     "lib",
@@ -56,7 +56,7 @@
     "tslint": "5.11.0",
     "tslint-config-standard": "8.0.1",
     "tslint-immutable": "^6.0.1",
-    "typescript": "^3.9.2"
+    "typescript": "^3.9.7"
   },
   "tags": [
     "typescript",
diff --git a/src/Array.ts b/src/Array.ts
index bf961b03..df752016 100644
--- a/src/Array.ts
+++ b/src/Array.ts
@@ -676,7 +676,7 @@ export const lefts: <E, A>(as: Array<Either<E, A>>) => Array<E> = RA.lefts as an
  * @category combinators
  * @since 2.0.0
  */
-export const sort: <A>(O: Ord<A>) => (as: Array<A>) => Array<A> = RA.sort as any
+export const sort: <B>(O: Ord<B>) => <A extends B>(as: Array<A>) => Array<A> = RA.sort as any
 
 /**
  * Apply a function to pairs of elements at the same index in two arrays, collecting the results in a new array. If one
@@ -801,7 +801,7 @@ export const uniq: <A>(E: Eq<A>) => (as: Array<A>) => Array<A> = RA.uniq as any
  * @category combinators
  * @since 2.0.0
  */
-export const sortBy: <A>(ords: Array<Ord<A>>) => (as: Array<A>) => Array<A> = RA.sortBy as any
+export const sortBy: <B>(ords: Array<Ord<B>>) => <A extends B>(as: Array<A>) => Array<A> = RA.sortBy as any
 
 /**
  * A useful recursion pattern for processing an array to produce a new array, often used for "chopping" up the input
@@ -980,7 +980,7 @@ export const of: <A>(a: A) => Array<A> = RA.of as any
 const map_: Monad1<URI>['map'] = RA.Monad.map as any
 const ap_: Monad1<URI>['ap'] = RA.Monad.ap as any
 const chain_: Monad1<URI>['chain'] = RA.Monad.chain as any
-const mapWithIndex_: FunctorWithIndex1<URI, number>['mapWithIndex'] = RA.Functor.mapWithIndex as any
+const mapWithIndex_: FunctorWithIndex1<URI, number>['mapWithIndex'] = RA.FunctorWithIndex.mapWithIndex as any
 const filter_: Filterable1<URI>['filter'] = RA.Filterable.filter as any
 const filterMap_: Filterable1<URI>['filterMap'] = RA.Filterable.filterMap as any
 const partition_: Filterable1<URI>['partition'] = RA.Filterable.partition as any
@@ -1233,13 +1233,13 @@ export const sequence: Traversable1<URI>['sequence'] = RA.sequence as any
 export const traverseWithIndex: PipeableTraverseWithIndex1<URI, number> = RA.traverseWithIndex as any
 
 /**
- * @category Whitherable
+ * @category Witherable
  * @since 2.6.5
  */
 export const wither: PipeableWither1<URI> = RA.wither as any
 
 /**
- * @category Whitherable
+ * @category Witherable
  * @since 2.6.5
  */
 export const wilt: PipeableWilt1<URI> = RA.wilt as any
diff --git a/src/Foldable.ts b/src/Foldable.ts
index 0ef5dc1f..e704b591 100644
--- a/src/Foldable.ts
+++ b/src/Foldable.ts
@@ -295,6 +295,34 @@ export function intercalate<M, F>(M: Monoid<M>, F: Foldable<F>): (sep: M, fm: HK
   }
 }
 
+/**
+ * Transforms a `Foldable` into a read-only array.
+ *
+ * @example
+ * import { toArray } from 'fp-ts/lib/Foldable'
+ * import { tree, make } from 'fp-ts/lib/Tree'
+ *
+ * const t = make(1, [make(2, []), make(3, []), make(4, [])])
+ * assert.deepStrictEqual(toArray(tree)(t), [1, 2, 3, 4])
+ *
+ * @since 2.8.0
+ */
+export function toArray<F extends URIS4>(F: Foldable4<F>): <S, R, E, A>(fa: Kind4<F, S, R, E, A>) => ReadonlyArray<A>
+export function toArray<F extends URIS3>(F: Foldable3<F>): <R, E, A>(fa: Kind3<F, R, E, A>) => ReadonlyArray<A>
+export function toArray<F extends URIS3, E>(F: Foldable3C<F, E>): <R, A>(fa: Kind3<F, R, E, A>) => ReadonlyArray<A>
+export function toArray<F extends URIS2>(F: Foldable2<F>): <E, A>(fa: Kind2<F, E, A>) => ReadonlyArray<A>
+export function toArray<F extends URIS2, E>(F: Foldable2C<F, E>): <A>(fa: Kind2<F, E, A>) => ReadonlyArray<A>
+export function toArray<F extends URIS>(F: Foldable1<F>): <A>(fa: Kind<F, A>) => ReadonlyArray<A>
+export function toArray<F>(F: Foldable<F>): <A>(fa: HKT<F, A>) => ReadonlyArray<A>
+export function toArray<F>(F: Foldable<F>): <A>(fa: HKT<F, A>) => ReadonlyArray<A> {
+  return <A>(fa: HKT<F, A>) =>
+    // tslint:disable-next-line: readonly-array
+    F.reduce(fa, [], (acc: Array<A>, a) => {
+      acc.push(a)
+      return acc
+    })
+}
+
 // TODO: remove in v3
 /**
  * Traverse a data structure, performing some effects encoded by an `Applicative` functor at each value, ignoring the
diff --git a/src/Map.ts b/src/Map.ts
index cc42b583..bb6c0748 100644
--- a/src/Map.ts
+++ b/src/Map.ts
@@ -277,7 +277,13 @@ export const filterMap: <A, B>(f: (a: A) => Option<B>) => <K>(fa: Map<K, A>) =>
  * @category Functor
  * @since 2.0.0
  */
-export const map: <A, B>(f: (a: A) => B) => <E>(fa: Map<E, A>) => Map<E, B> = RM.map as any
+export const map: <A, B>(f: (a: A) => B) => <K>(fa: Map<K, A>) => Map<K, B> = RM.map as any
+
+/**
+ * @category FunctorWithIndex
+ * @since 2.7.1
+ */
+export const mapWithIndex: <K, A, B>(f: (k: K, a: A) => B) => (fa: Map<K, A>) => Map<K, B> = RM.mapWithIndex as any
 
 /**
  * @category Filterable
diff --git a/src/Option.ts b/src/Option.ts
index 80434e9e..e724de45 100644
--- a/src/Option.ts
+++ b/src/Option.ts
@@ -645,7 +645,7 @@ export const sequence: Traversable1<URI>['sequence'] = <F>(F: ApplicativeHKT<F>)
 }
 
 /**
- * @category Whitherable
+ * @category Witherable
  * @since 2.6.5
  */
 export const wither: PipeableWither1<URI> = <F>(
@@ -656,7 +656,7 @@ export const wither: PipeableWither1<URI> = <F>(
 }
 
 /**
- * @category Whitherable
+ * @category Witherable
  * @since 2.6.5
  */
 export const wilt: PipeableWilt1<URI> = <F>(
diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts
index 96b5a02d..933da0f9 100644
--- a/src/ReadonlyArray.ts
+++ b/src/ReadonlyArray.ts
@@ -973,9 +973,8 @@ export function lefts<E, A>(as: ReadonlyArray<Either<E, A>>): ReadonlyArray<E> {
  * @category combinators
  * @since 2.5.0
  */
-export function sort<A>(O: Ord<A>): (as: ReadonlyArray<A>) => ReadonlyArray<A> {
-  return (as) => (isEmpty(as) ? as : as.slice().sort(O.compare))
-}
+export const sort = <B>(O: Ord<B>) => <A extends B>(as: ReadonlyArray<A>): ReadonlyArray<A> =>
+  isEmpty(as) ? as : as.slice().sort(O.compare)
 
 // TODO: curry and make data-last in v3
 /**
@@ -1171,8 +1170,8 @@ export function uniq<A>(E: Eq<A>): (as: ReadonlyArray<A>) => ReadonlyArray<A> {
  * @category combinators
  * @since 2.5.0
  */
-export function sortBy<A>(ords: ReadonlyArray<Ord<A>>): (as: ReadonlyArray<A>) => ReadonlyArray<A> {
-  const M = getOrdMonoid<A>()
+export function sortBy<B>(ords: ReadonlyArray<Ord<B>>): <A extends B>(as: ReadonlyArray<A>) => ReadonlyArray<A> {
+  const M = getOrdMonoid<B>()
   return sort(ords.reduce(M.concat, M.empty))
 }
 
@@ -1878,7 +1877,7 @@ export const traverseWithIndex: PipeableTraverseWithIndex1<URI, number> = <F>(
 }
 
 /**
- * @category Whitherable
+ * @category Witherable
  * @since 2.6.5
  */
 export const wither: PipeableWither1<URI> = <F>(
@@ -1889,7 +1888,7 @@ export const wither: PipeableWither1<URI> = <F>(
 }
 
 /**
- * @category Whitherable
+ * @category Witherable
  * @since 2.6.5
  */
 export const wilt: PipeableWilt1<URI> = <F>(
@@ -1948,7 +1947,7 @@ declare module './HKT' {
  * @category instances
  * @since 2.7.0
  */
-export const functorArray: Functor1<URI> = {
+export const Functor: Functor1<URI> = {
   URI,
   map: map_
 }
@@ -1957,7 +1956,7 @@ export const functorArray: Functor1<URI> = {
  * @category instances
  * @since 2.7.0
  */
-export const Functor: FunctorWithIndex1<URI, number> = {
+export const FunctorWithIndex: FunctorWithIndex1<URI, number> = {
   URI,
   map: map_,
   mapWithIndex: mapWithIndex_
diff --git a/src/ReadonlyMap.ts b/src/ReadonlyMap.ts
index 350a9017..cabcdb23 100644
--- a/src/ReadonlyMap.ts
+++ b/src/ReadonlyMap.ts
@@ -654,6 +654,14 @@ export const filterMap: <A, B>(f: (a: A) => Option<B>) => <K>(fa: ReadonlyMap<K,
  */
 export const map: <A, B>(f: (a: A) => B) => <K>(fa: ReadonlyMap<K, A>) => ReadonlyMap<K, B> = (f) => (fa) => map_(fa, f)
 
+/**
+ * @category FunctorWithIndex
+ * @since 2.7.1
+ */
+export const mapWithIndex: <K, A, B>(f: (k: K, a: A) => B) => (fa: ReadonlyMap<K, A>) => ReadonlyMap<K, B> = (f) => (
+  fa
+) => mapWithIndex_(fa, f)
+
 /**
  * @category Filterable
  * @since 2.5.0
diff --git a/src/ReadonlyNonEmptyArray.ts b/src/ReadonlyNonEmptyArray.ts
index df29a616..c2ddcd6c 100644
--- a/src/ReadonlyNonEmptyArray.ts
+++ b/src/ReadonlyNonEmptyArray.ts
@@ -376,8 +376,8 @@ export const unzip: <A, B>(
 // non-pipeables
 // -------------------------------------------------------------------------------------
 
-const map_: Functor1<URI>['map'] = RA.functorArray.map as any
-const mapWithIndex_: FunctorWithIndex1<URI, number>['mapWithIndex'] = RA.Functor.mapWithIndex as any
+const map_: Functor1<URI>['map'] = RA.Functor.map as any
+const mapWithIndex_: FunctorWithIndex1<URI, number>['mapWithIndex'] = RA.FunctorWithIndex.mapWithIndex as any
 const ap_: Apply1<URI>['ap'] = RA.Applicative.ap as any
 const chain_: Monad1<URI>['chain'] = RA.Monad.chain as any
 const extend_: Extend1<URI>['extend'] = RA.Extend.extend as any
diff --git a/src/ReadonlyRecord.ts b/src/ReadonlyRecord.ts
index 7b1a18d6..7ba7d7af 100644
--- a/src/ReadonlyRecord.ts
+++ b/src/ReadonlyRecord.ts
@@ -523,7 +523,7 @@ export function sequence<F>(
 }
 
 /**
- * @category Whitherable
+ * @category Witherable
  * @since 2.6.5
  */
 export const wither: PipeableWither1<URI> = <F>(
@@ -534,7 +534,7 @@ export const wither: PipeableWither1<URI> = <F>(
 }
 
 /**
- * @category Whitherable
+ * @category Witherable
  * @since 2.6.5
  */
 export const wilt: PipeableWilt1<URI> = <F>(
diff --git a/src/Record.ts b/src/Record.ts
index d7debe06..d4576679 100644
--- a/src/Record.ts
+++ b/src/Record.ts
@@ -329,13 +329,13 @@ export function sequence<F>(F: Applicative<F>): <A>(ta: Record<string, HKT<F, A>
 }
 
 /**
- * @category Whitherable
+ * @category Witherable
  * @since 2.6.5
  */
 export const wither: PipeableWither1<URI> = RR.wither as any
 
 /**
- * @category Whitherable
+ * @category Witherable
  * @since 2.6.5
  */
 export const wilt: PipeableWilt1<URI> = RR.wilt as any


Test patch:
diff --git a/test/Array.ts b/test/Array.ts
index 319dc0db..a14ee555 100644
--- a/test/Array.ts
+++ b/test/Array.ts
@@ -599,6 +599,23 @@ describe('Array', () => {
 
   it('sort', () => {
     assert.deepStrictEqual(_.sort(Ord.ordNumber)([3, 2, 1]), [1, 2, 3])
+    assert.strictEqual(_.sort(Ord.ordNumber)(_.empty), _.empty)
+    const byName = pipe(
+      Ord.ordString,
+      Ord.contramap((x: { readonly name: string }) => x.name)
+    )
+    assert.deepStrictEqual(
+      _.sort(byName)([
+        { name: 'b', age: 0 },
+        { name: 'a', age: 1 },
+        { name: 'c', age: 2 }
+      ]),
+      [
+        { name: 'a', age: 1 },
+        { name: 'b', age: 0 },
+        { name: 'c', age: 2 }
+      ]
+    )
   })
 
   it('zipWith', () => {
diff --git a/test/Compactable.ts b/test/Compactable.ts
index fd83fa56..c204fc25 100644
--- a/test/Compactable.ts
+++ b/test/Compactable.ts
@@ -6,7 +6,7 @@ import { left, right } from '../src/Either'
 
 describe('Compactable', () => {
   it('getCompactableComposition', () => {
-    const C = getCompactableComposition(A.functorArray, { ...A.functorArray, ...A.Compactable })
+    const C = getCompactableComposition(A.Functor, { ...A.Functor, ...A.Compactable })
     assert.deepStrictEqual(
       C.compact([
         [some(1), none],
diff --git a/test/Filterable.ts b/test/Filterable.ts
index a204aaf3..6a5a6fdf 100644
--- a/test/Filterable.ts
+++ b/test/Filterable.ts
@@ -6,7 +6,7 @@ import { right, left } from '../src/Either'
 
 describe('Filterable', () => {
   it('getFilterableComposition', () => {
-    const F = getFilterableComposition(A.functorArray, A.Filterable)
+    const F = getFilterableComposition(A.Functor, A.Filterable)
     assert.deepStrictEqual(
       F.filter(
         [
diff --git a/test/Foldable.ts b/test/Foldable.ts
index ab2dd116..dcd06b04 100644
--- a/test/Foldable.ts
+++ b/test/Foldable.ts
@@ -1,9 +1,10 @@
 import * as assert from 'assert'
 import * as A from '../src/ReadonlyArray'
-import { foldM, getFoldableComposition, intercalate, traverse_ } from '../src/Foldable'
 import * as I from '../src/IO'
-import { monoidString } from '../src/Monoid'
 import * as O from '../src/Option'
+import * as T from '../src/Tree'
+import { monoidString } from '../src/Monoid'
+import { foldM, getFoldableComposition, intercalate, traverse_, toArray } from '../src/Foldable'
 
 export const ArrayOptionURI = 'ArrayOption'
 
@@ -45,6 +46,17 @@ describe('Foldable', () => {
     assert.deepStrictEqual(intercalate(monoidString, A.Foldable)(',', ['a', 'b', 'c']), 'a,b,c')
   })
 
+  it('toArray', () => {
+    // Option
+    const optionToArray = toArray(O.option)
+    assert.deepStrictEqual(optionToArray(O.some(1)), [1])
+    assert.deepStrictEqual(optionToArray(O.none), [])
+
+    // Tree
+    const treeToArray = toArray(T.tree)
+    assert.deepStrictEqual(treeToArray(T.make(1, [T.make(2, []), T.make(3, []), T.make(4, [])])), [1, 2, 3, 4])
+  })
+
   it('traverse_', () => {
     let log = ''
     const append = (s: String) => () => (log += s)
diff --git a/test/Functor.ts b/test/Functor.ts
index 9d04836c..9b69b7d2 100644
--- a/test/Functor.ts
+++ b/test/Functor.ts
@@ -5,7 +5,7 @@ import * as option from '../src/Option'
 
 describe('Functor', () => {
   it('getFunctorComposition', () => {
-    const arrayOption = getFunctorComposition(A.functorArray, option.Functor)
+    const arrayOption = getFunctorComposition(A.Functor, option.Functor)
     const double = (a: number) => a * 2
     assert.deepStrictEqual(arrayOption.map([option.some(1)], double), [option.some(2)])
   })
diff --git a/test/FunctorWithIndex.ts b/test/FunctorWithIndex.ts
index ae736b27..27ba828f 100644
--- a/test/FunctorWithIndex.ts
+++ b/test/FunctorWithIndex.ts
@@ -4,7 +4,7 @@ import { getFunctorWithIndexComposition } from '../src/FunctorWithIndex'
 
 describe('FunctorWithIndex', () => {
   it('getFunctorComposition', () => {
-    const arrayOfArray = getFunctorWithIndexComposition(A.Functor, A.Functor)
+    const arrayOfArray = getFunctorWithIndexComposition(A.FunctorWithIndex, A.FunctorWithIndex)
     const f = ([i, j]: readonly [number, number], a: string) => a + i + j
     assert.deepStrictEqual(
       arrayOfArray.mapWithIndex(
diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts
index cf200986..1924642f 100644
--- a/test/ReadonlyArray.ts
+++ b/test/ReadonlyArray.ts
@@ -596,6 +596,22 @@ describe('ReadonlyArray', () => {
   it('sort', () => {
     assert.deepStrictEqual(_.sort(Ord.ordNumber)([3, 2, 1]), [1, 2, 3])
     assert.strictEqual(_.sort(Ord.ordNumber)(_.empty), _.empty)
+    const byName = pipe(
+      Ord.ordString,
+      Ord.contramap((x: { readonly name: string }) => x.name)
+    )
+    assert.deepStrictEqual(
+      _.sort(byName)([
+        { name: 'b', age: 0 },
+        { name: 'a', age: 1 },
+        { name: 'c', age: 2 }
+      ]),
+      [
+        { name: 'a', age: 1 },
+        { name: 'b', age: 0 },
+        { name: 'c', age: 2 }
+      ]
+    )
   })
 
   it('zipWith', () => {
@@ -920,7 +936,7 @@ describe('ReadonlyArray', () => {
       readonly bar: () => number
     }
     const f = (a: number, x?: Foo) => (x !== undefined ? `${a}${x.bar()}` : `${a}`)
-    assert.deepStrictEqual(_.functorArray.map([1, 2], f), ['1', '2'])
+    assert.deepStrictEqual(_.Functor.map([1, 2], f), ['1', '2'])
     assert.deepStrictEqual(pipe([1, 2], _.map(f)), ['1', '2'])
   })
 
diff --git a/test/ReadonlyMap.ts b/test/ReadonlyMap.ts
index 2902ec82..2d96877d 100644
--- a/test/ReadonlyMap.ts
+++ b/test/ReadonlyMap.ts
@@ -791,16 +791,6 @@ describe('ReadonlyMap', () => {
       assert.deepStrictEqual(log, ['a', 'b'])
     })
 
-    it('mapWithIndex', () => {
-      const mapWithIndex = W.mapWithIndex
-      const aa1 = new Map<User, number>([[{ id: 'aa' }, 1]])
-      const aa3 = new Map<User, number>([[{ id: 'aa' }, 3]])
-      assert.deepStrictEqual(
-        mapWithIndex(aa1, (k, a) => a + k.id.length),
-        aa3
-      )
-    })
-
     it('reduce', () => {
       const d1 = new Map<User, string>([
         [{ id: 'k1' }, 'a'],
@@ -1134,4 +1124,16 @@ describe('ReadonlyMap', () => {
     assert.deepStrictEqual(bs, as)
     assert.notStrictEqual(bs, as)
   })
+
+  it('mapWithIndex', () => {
+    const aa1 = new Map<User, number>([[{ id: 'aa' }, 1]])
+    const aa3 = new Map<User, number>([[{ id: 'aa' }, 3]])
+    assert.deepStrictEqual(
+      pipe(
+        aa1,
+        _.mapWithIndex((k, a) => a + k.id.length)
+      ),
+      aa3
+    )
+  })
 })
diff --git a/test/Store.ts b/test/Store.ts
index a71cab7e..a562221a 100644
--- a/test/Store.ts
+++ b/test/Store.ts
@@ -80,7 +80,7 @@ describe('Store', () => {
     assert.deepStrictEqual(
       pipe(
         wa,
-        _.experiment(A.functorArray)((s) => [s, s + 'a'])
+        _.experiment(A.Functor)((s) => [s, s + 'a'])
       ),
       [1, 2]
     )
diff --git a/test/pipeable.ts b/test/pipeable.ts
index 52972bd6..0a12077d 100644
--- a/test/pipeable.ts
+++ b/test/pipeable.ts
@@ -14,7 +14,7 @@ describe('pipeable', () => {
   })
 
   it('Functor', () => {
-    const { map } = pipeable(A.functorArray)
+    const { map } = pipeable(A.Functor)
     assert.deepStrictEqual(map((n: number) => n * 2)([1, 2, 3]), [2, 4, 6])
   })
 
@@ -24,7 +24,7 @@ describe('pipeable', () => {
   })
 
   it('FunctorWithIndex', () => {
-    const { mapWithIndex } = pipeable(A.Functor)
+    const { mapWithIndex } = pipeable(A.FunctorWithIndex)
     assert.deepStrictEqual(mapWithIndex((i, n: number) => n * 2 + i)([1, 2, 3]), [2, 5, 8])
   })