Repository: gcanti/fp-ts
Pull request #1421 (2021-03-04 10:34:03)

version 2.10.0 rc.4
Related: https://github.com/gcanti/fp-ts/issues/1420


Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7091135e..c3cd2dbc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,27 @@
 **Note**: A feature tagged as Experimental is in a
 high state of flux, you're at risk of it changing without notice.
 
+# 2.10.0-rc.4
+
+- **Deprecations**
+  - `Array`
+    - deprecate `empty`
+  - `Map`
+    - deprecate `empty`
+  - `NonEmptyArray`
+    - deprecate `filter` in favour of `Array`'s `filter`
+    - deprecate `filterWithIndex` in favour of `Array`'s `filterWithIndex`
+  - `ReadonlyNonEmptyArray`
+    - deprecate `filter` in favour of `ReadonlyArray`'s `filter`
+    - deprecate `filterWithIndex` in favour of `ReadonlyArray`'s `filterWithIndex`
+  - `Record`
+    - deprecate `empty`
+- **Polish**
+  - `NonEmptyArray`
+    - remove duplicated `append`, `prepend`, `isNonEmpty`
+  - `ReadonlyNonEmptyArray`
+    - remove duplicated `append`, `prepend`, `isNonEmpty`
+
 # 2.10.0-rc.3
 
 - **Deprecations**
diff --git a/docs/modules/Array.ts.md b/docs/modules/Array.ts.md
index 1d055992..cdea8371 100644
--- a/docs/modules/Array.ts.md
+++ b/docs/modules/Array.ts.md
@@ -159,7 +159,6 @@ Added in v2.0.0
   - [chunksOf](#chunksof)
   - [deleteAt](#deleteat)
   - [elem](#elem)
-  - [empty](#empty)
   - [every](#every)
   - [findIndex](#findindex)
   - [findLastIndex](#findlastindex)
@@ -173,6 +172,7 @@ Added in v2.0.0
   - [splitAt](#splitat)
   - [unzip](#unzip)
   - [updateAt](#updateat)
+  - [~~empty~~](#empty)
 
 ---
 
@@ -675,7 +675,8 @@ Added in v2.0.0
 
 ## dropLeft
 
-Drop a number of elements from the start of an array, creating a new array
+Drop a number of elements from the start of an `Array`, creating a new `Array`.
+If `n` (must be a natural number) is negative a copy of the input is returned.
 
 **Signature**
 
@@ -715,7 +716,8 @@ Added in v2.0.0
 
 ## dropRight
 
-Drop a number of elements from the end of an array, creating a new array
+Drop a number of elements from the end of an `ReadonlyArray`, creating a new `ReadonlyArray`.
+If `n` (must be a natural number) is negative a copy of the input is returned.
 
 **Signature**
 
@@ -1033,8 +1035,8 @@ Added in v2.0.0
 
 ## takeLeft
 
-Keep only a number of elements from the start of an array, creating a new array.
-`n` must be a natural number
+Keep only a number of elements from the start of an `Array`, creating a new `Array`.
+If `n` (must be a natural number) is out of bound a copy of the input is returned.
 
 **Signature**
 
@@ -1075,8 +1077,8 @@ Added in v2.0.0
 
 ## takeRight
 
-Keep only a number of elements from the end of an array, creating a new array.
-`n` must be a natural number
+Keep only a number of elements from the end of an `Array`, creating a new `Array`.
+If `n` (must be a natural number) is out of bound a copy of the input is returned.
 
 **Signature**
 
@@ -1230,7 +1232,7 @@ Added in v2.10.0
 
 ## comprehension
 
-Array comprehension
+`Array` comprehension.
 
 ```
 [ f(x, y, ...) | x ← xs, y ← ys, ..., g(x, y, ...) ]
@@ -1286,7 +1288,8 @@ Added in v2.0.0
 
 ## makeBy
 
-Return a list of length `n` with element `i` initialized with `f(i)`
+Return an `Array` of length `n` with element `i` initialized with `f(i)`.
+If `n` (must be a natural number) is negative return `[]`.
 
 **Signature**
 
@@ -1328,7 +1331,7 @@ Added in v2.10.0
 
 ## range
 
-Create an array containing a range of integers, including both endpoints
+Create an `Array` containing a range of integers, including both endpoints.
 
 **Signature**
 
@@ -1348,7 +1351,7 @@ Added in v2.0.0
 
 ## replicate
 
-Create an array containing a value repeated the specified number of times
+Create an `Array` containing a value repeated the specified number of times.
 
 **Signature**
 
@@ -2197,18 +2200,6 @@ assert.strictEqual(pipe([1, 2, 3], elem(N.Eq)(0)), false)
 
 Added in v2.0.0
 
-## empty
-
-An empty array
-
-**Signature**
-
-```ts
-export declare const empty: never[]
-```
-
-Added in v2.0.0
-
 ## every
 
 **Signature**
@@ -2467,3 +2458,15 @@ assert.deepStrictEqual(updateAt(1, 1)([]), none)
 ```
 
 Added in v2.0.0
+
+## ~~empty~~
+
+Use a new `[]` instead.
+
+**Signature**
+
+```ts
+export declare const empty: never[]
+```
+
+Added in v2.0.0
diff --git a/docs/modules/Map.ts.md b/docs/modules/Map.ts.md
index 9bee3028..37b13678 100644
--- a/docs/modules/Map.ts.md
+++ b/docs/modules/Map.ts.md
@@ -52,7 +52,6 @@ Added in v2.0.0
 - [utils](#utils)
   - [collect](#collect)
   - [elem](#elem)
-  - [empty](#empty)
   - [isEmpty](#isempty)
   - [isSubmap](#issubmap)
   - [keys](#keys)
@@ -67,6 +66,7 @@ Added in v2.0.0
   - [toUnfoldable](#tounfoldable)
   - [updateAt](#updateat)
   - [values](#values)
+  - [~~empty~~](#empty)
 
 ---
 
@@ -453,16 +453,6 @@ export declare const elem: <A>(E: Eq<A>) => { (a: A): <K>(m: Map<K, A>) => boole
 
 Added in v2.0.0
 
-## empty
-
-**Signature**
-
-```ts
-export declare const empty: Map<never, never>
-```
-
-Added in v2.0.0
-
 ## isEmpty
 
 Test whether or not a map is empty
@@ -641,3 +631,15 @@ export declare const values: <A>(O: Ord<A>) => <K>(m: Map<K, A>) => A[]
 ```
 
 Added in v2.0.0
+
+## ~~empty~~
+
+Use a `new Map()` instead.
+
+**Signature**
+
+```ts
+export declare const empty: Map<never, never>
+```
+
+Added in v2.0.0
diff --git a/docs/modules/NonEmptyArray.ts.md b/docs/modules/NonEmptyArray.ts.md
index 557a6db6..443c15e7 100644
--- a/docs/modules/NonEmptyArray.ts.md
+++ b/docs/modules/NonEmptyArray.ts.md
@@ -6,7 +6,16 @@ parent: Modules
 
 ## NonEmptyArray overview
 
-Data structure which represents non-empty arrays
+Data structure which represents non-empty arrays.
+
+```ts
+export type NonEmptyArray<A> = Array<A> & {
+  0: A
+}
+```
+
+Note that you don't need any conversion, a `NonEmptyArray` is an `Array`,
+so all `Array`'s APIs can be used with a `NonEmptyArray` without further ado.
 
 Added in v2.0.0
 
@@ -45,8 +54,6 @@ Added in v2.0.0
   - [concat](#concat)
   - [copy](#copy)
   - [duplicate](#duplicate)
-  - [filter](#filter)
-  - [filterWithIndex](#filterwithindex)
   - [flap](#flap)
   - [flatten](#flatten)
   - [foldMap](#foldmap)
@@ -65,12 +72,12 @@ Added in v2.0.0
   - [updateAt](#updateat)
   - [zip](#zip)
   - [zipWith](#zipwith)
+  - [~~filterWithIndex~~](#filterwithindex)
+  - [~~filter~~](#filter)
   - [~~prependToAll~~](#prependtoall)
 - [constructors](#constructors)
-  - [append](#append)
   - [fromArray](#fromarray)
   - [fromReadonlyNonEmptyArray](#fromreadonlynonemptyarray)
-  - [prepend](#prepend)
   - [~~cons~~](#cons)
   - [~~snoc~~](#snoc)
 - [destructors](#destructors)
@@ -78,8 +85,6 @@ Added in v2.0.0
   - [unprepend](#unprepend)
   - [~~uncons~~](#uncons)
   - [~~unsnoc~~](#unsnoc)
-- [guards](#guards)
-  - [isNonEmpty](#isnonempty)
 - [instances](#instances)
   - [Alt](#alt-1)
   - [Applicative](#applicative)
@@ -382,31 +387,6 @@ export declare const duplicate: <A>(ma: NonEmptyArray<A>) => NonEmptyArray<NonEm
 
 Added in v2.5.0
 
-## filter
-
-**Signature**
-
-```ts
-export declare function filter<A, B extends A>(
-  refinement: Refinement<A, B>
-): (as: NonEmptyArray<A>) => Option<NonEmptyArray<B>>
-export declare function filter<A>(predicate: Predicate<A>): (as: NonEmptyArray<A>) => Option<NonEmptyArray<A>>
-```
-
-Added in v2.0.0
-
-## filterWithIndex
-
-**Signature**
-
-```ts
-export declare const filterWithIndex: <A>(
-  predicate: (i: number, a: A) => boolean
-) => (as: NonEmptyArray<A>) => O.Option<NonEmptyArray<A>>
-```
-
-Added in v2.0.0
-
 ## flap
 
 Derivable from `Functor`.
@@ -664,40 +644,48 @@ export declare const zipWith: <A, B, C>(
 
 Added in v2.5.1
 
-## ~~prependToAll~~
+## ~~filterWithIndex~~
 
-Use `prependAll` instead.
+Use `Array`'s `filterWithIndex` instead.
 
 **Signature**
 
 ```ts
-export declare const prependToAll: <A>(middle: A) => (as: NonEmptyArray<A>) => NonEmptyArray<A>
+export declare const filterWithIndex: <A>(
+  predicate: (i: number, a: A) => boolean
+) => (as: NonEmptyArray<A>) => O.Option<NonEmptyArray<A>>
 ```
 
-Added in v2.9.0
-
-# constructors
+Added in v2.0.0
 
-## append
+## ~~filter~~
 
-Append an element to the end of a `ReadonlyArray`, creating a new `ReadonlyNonEmptyArray`.
+Use `Array`'s `filter` instead.
 
 **Signature**
 
 ```ts
-export declare const append: <A>(end: A) => (init: A[]) => NonEmptyArray<A>
+export declare function filter<A, B extends A>(
+  refinement: Refinement<A, B>
+): (as: NonEmptyArray<A>) => Option<NonEmptyArray<B>>
+export declare function filter<A>(predicate: Predicate<A>): (as: NonEmptyArray<A>) => Option<NonEmptyArray<A>>
 ```
 
-**Example**
+Added in v2.0.0
 
-```ts
-import { append } from 'fp-ts/NonEmptyArray'
-import { pipe } from 'fp-ts/function'
+## ~~prependToAll~~
 
-assert.deepStrictEqual(pipe([1, 2, 3], append(4)), [1, 2, 3, 4])
+Use `prependAll` instead.
+
+**Signature**
+
+```ts
+export declare const prependToAll: <A>(middle: A) => (as: NonEmptyArray<A>) => NonEmptyArray<A>
 ```
 
-Added in v2.10.0
+Added in v2.9.0
+
+# constructors
 
 ## fromArray
 
@@ -721,30 +709,9 @@ export declare const fromReadonlyNonEmptyArray: <A>(as: RNEA.ReadonlyNonEmptyArr
 
 Added in v2.10.0
 
-## prepend
-
-Prepend an element to the front of a `ReadonlyArray`, creating a new `ReadonlyNonEmptyArray`.
-
-**Signature**
-
-```ts
-export declare const prepend: <A>(head: A) => (tail: A[]) => NonEmptyArray<A>
-```
-
-**Example**
-
-```ts
-import { prepend } from 'fp-ts/NonEmptyArray'
-import { pipe } from 'fp-ts/function'
-
-assert.deepStrictEqual(pipe([2, 3, 4], prepend(1)), [1, 2, 3, 4])
-```
-
-Added in v2.10.0
-
 ## ~~cons~~
 
-Use `prepend` instead.
+Use `Array`'s `prepend` instead.
 
 **Signature**
 
@@ -757,7 +724,7 @@ Added in v2.0.0
 
 ## ~~snoc~~
 
-Use `append` instead.
+Use `Array`'s `append` instead.
 
 **Signature**
 
@@ -833,20 +800,6 @@ export declare const unsnoc: <A>(as: NonEmptyArray<A>) => [A[], A]
 
 Added in v2.9.0
 
-# guards
-
-## isNonEmpty
-
-Test whether a `Array` is non empty.
-
-**Signature**
-
-```ts
-export declare const isNonEmpty: <A>(as: A[]) => as is NonEmptyArray<A>
-```
-
-Added in v2.10.0
-
 # instances
 
 ## Alt
diff --git a/docs/modules/ReadonlyArray.ts.md b/docs/modules/ReadonlyArray.ts.md
index 9e0bf51a..bd9b4b18 100644
--- a/docs/modules/ReadonlyArray.ts.md
+++ b/docs/modules/ReadonlyArray.ts.md
@@ -674,7 +674,8 @@ Added in v2.5.0
 
 ## dropLeft
 
-Drop a number of elements from the start of an array, creating a new array
+Drop a number of elements from the start of an `ReadonlyArray`, creating a new `ReadonlyArray`.
+If `n` (must be a natural number) is negative the input is returned.
 
 **Signature**
 
@@ -714,7 +715,8 @@ Added in v2.5.0
 
 ## dropRight
 
-Drop a number of elements from the end of an array, creating a new array
+Drop a number of elements from the end of an `ReadonlyArray`, creating a new `ReadonlyArray`.
+If `n` (must be a natural number) is negative the input is returned.
 
 **Signature**
 
@@ -1014,8 +1016,8 @@ Added in v2.5.0
 
 ## takeLeft
 
-Keep only a number of elements from the start of a `ReadonlyArray`, creating a new `ReadonlyArray`.
-The input `n` must be a natural number.
+Keep only a number of elements from the start of an `ReadonlyArray`, creating a new `ReadonlyArray`.
+If `n` (must be a natural number) is out of bound the input is returned.
 
 **Signature**
 
@@ -1192,7 +1194,7 @@ Added in v2.10.0
 
 ## comprehension
 
-Array comprehension
+`ReadonlyArray` comprehension.
 
 ```
 [ f(x, y, ...) | x ← xs, y ← ys, ..., g(x, y, ...) ]
@@ -1266,7 +1268,8 @@ Added in v2.5.0
 
 ## makeBy
 
-Return a list of length `n` with element `i` initialized with `f(i)`
+Return a `ReadonlyArray` of length `n` with element `i` initialized with `f(i)`.
+If `n` (must be a natural number) is negative return `empty`.
 
 **Signature**
 
@@ -1308,7 +1311,7 @@ Added in v2.10.0
 
 ## range
 
-Create an array containing a range of integers, including both endpoints
+Create a `ReadonlyArray` containing a range of integers, including both endpoints.
 
 **Signature**
 
@@ -1328,7 +1331,7 @@ Added in v2.5.0
 
 ## replicate
 
-Create an array containing a value repeated the specified number of times
+Create a `ReadonlyArray` containing a value repeated the specified number of times.
 
 **Signature**
 
@@ -2464,8 +2467,8 @@ Added in v2.5.0
 
 ## takeRight
 
-Keep only a number of elements from the end of a `ReadonlyArray`, creating a new `ReadonlyArray`.
-The input `n` must be a natural number.
+Keep only a number of elements from the end of an `ReadonlyArray`, creating a new `ReadonlyArray`.
+If `n` (must be a natural number) is out of bound the input is returned.
 
 **Signature**
 
diff --git a/docs/modules/ReadonlyNonEmptyArray.ts.md b/docs/modules/ReadonlyNonEmptyArray.ts.md
index 718207cb..9b3a8250 100644
--- a/docs/modules/ReadonlyNonEmptyArray.ts.md
+++ b/docs/modules/ReadonlyNonEmptyArray.ts.md
@@ -6,7 +6,16 @@ parent: Modules
 
 ## ReadonlyNonEmptyArray overview
 
-Data structure which represents non-empty arrays
+Data structure which represents non-empty readonly arrays.
+
+```ts
+export type ReadonlyNonEmptyArray<A> = ReadonlyArray<A> & {
+  readonly 0: A
+}
+```
+
+Note that you don't need any conversion, a `ReadonlyNonEmptyArray` is a `ReadonlyArray`,
+so all `ReadonlyArray`'s APIs can be used with a `ReadonlyNonEmptyArray` without further ado.
 
 Added in v2.5.0
 
@@ -51,8 +60,6 @@ Added in v2.5.0
   - [chunksOf](#chunksof)
   - [concat](#concat)
   - [duplicate](#duplicate)
-  - [filter](#filter)
-  - [filterWithIndex](#filterwithindex)
   - [flap](#flap)
   - [flatten](#flatten)
   - [foldMap](#foldmap)
@@ -70,13 +77,13 @@ Added in v2.5.0
   - [updateAt](#updateat)
   - [zip](#zip)
   - [zipWith](#zipwith)
+  - [~~filterWithIndex~~](#filterwithindex)
+  - [~~filter~~](#filter)
   - [~~insertAt~~](#insertat)
   - [~~prependToAll~~](#prependtoall)
 - [constructors](#constructors)
-  - [append](#append)
   - [fromArray](#fromarray)
   - [fromReadonlyArray](#fromreadonlyarray)
-  - [prepend](#prepend)
   - [~~cons~~](#cons)
   - [~~snoc~~](#snoc)
 - [destructors](#destructors)
@@ -84,8 +91,6 @@ Added in v2.5.0
   - [unprepend](#unprepend)
   - [~~uncons~~](#uncons)
   - [~~unsnoc~~](#unsnoc)
-- [guards](#guards)
-  - [isNonEmpty](#isnonempty)
 - [instances](#instances)
   - [Alt](#alt-1)
   - [Applicative](#applicative)
@@ -453,33 +458,6 @@ export declare const duplicate: <A>(ma: ReadonlyNonEmptyArray<A>) => ReadonlyNon
 
 Added in v2.5.0
 
-## filter
-
-**Signature**
-
-```ts
-export declare function filter<A, B extends A>(
-  refinement: Refinement<A, B>
-): (as: ReadonlyNonEmptyArray<A>) => Option<ReadonlyNonEmptyArray<B>>
-export declare function filter<A>(
-  predicate: Predicate<A>
-): (as: ReadonlyNonEmptyArray<A>) => Option<ReadonlyNonEmptyArray<A>>
-```
-
-Added in v2.5.0
-
-## filterWithIndex
-
-**Signature**
-
-```ts
-export declare const filterWithIndex: <A>(
-  predicate: (i: number, a: A) => boolean
-) => (as: ReadonlyNonEmptyArray<A>) => O.Option<ReadonlyNonEmptyArray<A>>
-```
-
-Added in v2.5.0
-
 ## flap
 
 Derivable from `Functor`.
@@ -747,52 +725,62 @@ export declare const zipWith: <A, B, C>(
 
 Added in v2.5.1
 
-## ~~insertAt~~
+## ~~filterWithIndex~~
 
-Use `ReadonlyArray`'s `insertAt` instead.
+Use `ReadonlyArray`'s `filterWithIndex` instead.
 
 **Signature**
 
 ```ts
-export declare const insertAt: <A>(i: number, a: A) => (as: readonly A[]) => O.Option<ReadonlyNonEmptyArray<A>>
+export declare const filterWithIndex: <A>(
+  predicate: (i: number, a: A) => boolean
+) => (as: ReadonlyNonEmptyArray<A>) => O.Option<ReadonlyNonEmptyArray<A>>
 ```
 
 Added in v2.5.0
 
-## ~~prependToAll~~
+## ~~filter~~
 
-Use `prependAll` instead.
+Use `ReadonlyArray`'s `filter` instead.
 
 **Signature**
 
 ```ts
-export declare const prependToAll: <A>(middle: A) => (as: ReadonlyNonEmptyArray<A>) => ReadonlyNonEmptyArray<A>
+export declare function filter<A, B extends A>(
+  refinement: Refinement<A, B>
+): (as: ReadonlyNonEmptyArray<A>) => Option<ReadonlyNonEmptyArray<B>>
+export declare function filter<A>(
+  predicate: Predicate<A>
+): (as: ReadonlyNonEmptyArray<A>) => Option<ReadonlyNonEmptyArray<A>>
 ```
 
-Added in v2.9.0
-
-# constructors
+Added in v2.5.0
 
-## append
+## ~~insertAt~~
 
-Append an element to the end of a `ReadonlyArray`, creating a new `ReadonlyNonEmptyArray`.
+Use `ReadonlyArray`'s `insertAt` instead.
 
 **Signature**
 
 ```ts
-export declare const append: <A>(end: A) => (init: readonly A[]) => ReadonlyNonEmptyArray<A>
+export declare const insertAt: <A>(i: number, a: A) => (as: readonly A[]) => O.Option<ReadonlyNonEmptyArray<A>>
 ```
 
-**Example**
+Added in v2.5.0
 
-```ts
-import { append } from 'fp-ts/ReadonlyNonEmptyArray'
-import { pipe } from 'fp-ts/function'
+## ~~prependToAll~~
 
-assert.deepStrictEqual(pipe([1, 2, 3], append(4)), [1, 2, 3, 4])
+Use `prependAll` instead.
+
+**Signature**
+
+```ts
+export declare const prependToAll: <A>(middle: A) => (as: ReadonlyNonEmptyArray<A>) => ReadonlyNonEmptyArray<A>
 ```
 
-Added in v2.10.0
+Added in v2.9.0
+
+# constructors
 
 ## fromArray
 
@@ -816,30 +804,9 @@ export declare const fromReadonlyArray: <A>(as: readonly A[]) => O.Option<Readon
 
 Added in v2.5.0
 
-## prepend
-
-Prepend an element to the front of a `ReadonlyArray`, creating a new `ReadonlyNonEmptyArray`.
-
-**Signature**
-
-```ts
-export declare const prepend: <A>(head: A) => (tail: readonly A[]) => ReadonlyNonEmptyArray<A>
-```
-
-**Example**
-
-```ts
-import { prepend } from 'fp-ts/ReadonlyNonEmptyArray'
-import { pipe } from 'fp-ts/function'
-
-assert.deepStrictEqual(pipe([2, 3, 4], prepend(1)), [1, 2, 3, 4])
-```
-
-Added in v2.10.0
-
 ## ~~cons~~
 
-Use `prepend` instead.
+Use `ReadonlyArray`'s `prepend` instead.
 
 **Signature**
 
@@ -852,7 +819,7 @@ Added in v2.5.0
 
 ## ~~snoc~~
 
-Use `append` instead.
+Use `ReadonlyArray`'s `append` instead.
 
 **Signature**
 
@@ -928,20 +895,6 @@ export declare const unsnoc: <A>(as: ReadonlyNonEmptyArray<A>) => readonly [read
 
 Added in v2.10.0
 
-# guards
-
-## isNonEmpty
-
-Test whether a `ReadonlyArray` is non empty.
-
-**Signature**
-
-```ts
-export declare const isNonEmpty: <A>(as: readonly A[]) => as is ReadonlyNonEmptyArray<A>
-```
-
-Added in v2.10.0
-
 # instances
 
 ## Alt
diff --git a/docs/modules/Record.ts.md b/docs/modules/Record.ts.md
index f3bf1c5b..ec260d95 100644
--- a/docs/modules/Record.ts.md
+++ b/docs/modules/Record.ts.md
@@ -48,7 +48,6 @@ Added in v2.0.0
   - [collect](#collect)
   - [deleteAt](#deleteat)
   - [elem](#elem)
-  - [empty](#empty)
   - [every](#every)
   - [filterMapWithIndex](#filtermapwithindex)
   - [filterWithIndex](#filterwithindex)
@@ -80,6 +79,7 @@ Added in v2.0.0
   - [traverse](#traverse)
   - [traverseWithIndex](#traversewithindex)
   - [updateAt](#updateat)
+  - [~~empty~~](#empty)
   - [~~hasOwnProperty (function)~~](#hasownproperty-function)
   - [~~insertAt~~](#insertat)
 
@@ -431,16 +431,6 @@ export declare const elem: <A>(
 
 Added in v2.0.0
 
-## empty
-
-**Signature**
-
-```ts
-export declare const empty: Record<string, never>
-```
-
-Added in v2.0.0
-
 ## every
 
 **Signature**
@@ -949,6 +939,18 @@ export declare const updateAt: <A>(k: string, a: A) => <K extends string>(r: Rec
 
 Added in v2.0.0
 
+## ~~empty~~
+
+Use a new `{}` instead.
+
+**Signature**
+
+```ts
+export declare const empty: Record<string, never>
+```
+
+Added in v2.0.0
+
 ## ~~hasOwnProperty (function)~~
 
 Use `has` instead.
diff --git a/docs/modules/Tree.ts.md b/docs/modules/Tree.ts.md
index 3412ad2d..dfec6742 100644
--- a/docs/modules/Tree.ts.md
+++ b/docs/modules/Tree.ts.md
@@ -279,7 +279,7 @@ Added in v2.0.0
 **Signature**
 
 ```ts
-export declare function make<A>(value: A, forest: Forest<A> = A.empty): Tree<A>
+export declare function make<A>(value: A, forest: Forest<A> = []): Tree<A>
 ```
 
 Added in v2.0.0
diff --git a/package.json b/package.json
index 55754c3d..ad901555 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "fp-ts",
-  "version": "2.10.0-rc.3",
+  "version": "2.10.0-rc.4",
   "description": "Functional programming in TypeScript",
   "main": "lib/index.js",
   "module": "es6/index.js",
diff --git a/src/Array.ts b/src/Array.ts
index 519bfc3c..9d90328a 100644
--- a/src/Array.ts
+++ b/src/Array.ts
@@ -69,7 +69,8 @@ export const prepend: <A>(head: A) => (tail: Array<A>) => NEA.NonEmptyArray<A> =
 export const append: <A>(end: A) => (init: Array<A>) => NEA.NonEmptyArray<A> = NEA.append
 
 /**
- * Return a list of length `n` with element `i` initialized with `f(i)`
+ * Return an `Array` of length `n` with element `i` initialized with `f(i)`.
+ * If `n` (must be a natural number) is negative return `[]`.
  *
  * @example
  * import { makeBy } from 'fp-ts/Array'
@@ -92,7 +93,7 @@ export const makeBy = <A>(n: number, f: (i: number) => A): Array<A> => {
 }
 
 /**
- * Create an array containing a range of integers, including both endpoints
+ * Create an `Array` containing a range of integers, including both endpoints.
  *
  * @example
  * import { range } from 'fp-ts/Array'
@@ -102,10 +103,11 @@ export const makeBy = <A>(n: number, f: (i: number) => A): Array<A> => {
  * @category constructors
  * @since 2.0.0
  */
-export const range = (start: number, end: number): Array<number> => makeBy(end - start + 1, (i) => start + i)
+export const range = (start: number, end: number): Array<number> =>
+  start <= end ? makeBy(end - start + 1, (i) => start + i) : [start]
 
 /**
- * Create an array containing a value repeated the specified number of times
+ * Create an `Array` containing a value repeated the specified number of times.
  *
  * @example
  * import { replicate } from 'fp-ts/Array'
@@ -333,8 +335,8 @@ export const tail = <A>(as: Array<A>): Option<Array<A>> => (isNonEmpty(as) ? O.s
 export const init = <A>(as: Array<A>): Option<Array<A>> => (isNonEmpty(as) ? O.some(NEA.init(as)) : O.none)
 
 /**
- * Keep only a number of elements from the start of an array, creating a new array.
- * `n` must be a natural number
+ * Keep only a number of elements from the start of an `Array`, creating a new `Array`.
+ * If `n` (must be a natural number) is out of bound a copy of the input is returned.
  *
  * @example
  * import { takeLeft } from 'fp-ts/Array'
@@ -344,11 +346,12 @@ export const init = <A>(as: Array<A>): Option<Array<A>> => (isNonEmpty(as) ? O.s
  * @category combinators
  * @since 2.0.0
  */
-export const takeLeft = (n: number) => <A>(as: Array<A>): Array<A> => (n <= 0 ? [] : as.slice(0, n))
+export const takeLeft = (n: number) => <A>(as: Array<A>): Array<A> =>
+  isOutOfBound(n, as) ? copy(as) : n === 0 ? [] : as.slice(0, n)
 
 /**
- * Keep only a number of elements from the end of an array, creating a new array.
- * `n` must be a natural number
+ * Keep only a number of elements from the end of an `Array`, creating a new `Array`.
+ * If `n` (must be a natural number) is out of bound a copy of the input is returned.
  *
  * @example
  * import { takeRight } from 'fp-ts/Array'
@@ -358,7 +361,8 @@ export const takeLeft = (n: number) => <A>(as: Array<A>): Array<A> => (n <= 0 ?
  * @category combinators
  * @since 2.0.0
  */
-export const takeRight = (n: number) => <A>(as: Array<A>): Array<A> => (n <= 0 ? [] : as.slice(-n))
+export const takeRight = (n: number) => <A>(as: Array<A>): Array<A> =>
+  isOutOfBound(n, as) ? copy(as) : n === 0 ? [] : as.slice(-n)
 
 /**
  * Calculate the longest initial subarray for which all element satisfy the specified predicate, creating a new array
@@ -437,7 +441,8 @@ export function spanLeft<A>(predicate: Predicate<A>): (as: Array<A>) => Spanned<
 }
 
 /**
- * Drop a number of elements from the start of an array, creating a new array
+ * Drop a number of elements from the start of an `Array`, creating a new `Array`.
+ * If `n` (must be a natural number) is negative a copy of the input is returned.
  *
  * @example
  * import { dropLeft } from 'fp-ts/Array'
@@ -447,10 +452,12 @@ export function spanLeft<A>(predicate: Predicate<A>): (as: Array<A>) => Spanned<
  * @category combinators
  * @since 2.0.0
  */
-export const dropLeft = (n: number) => <A>(as: Array<A>): Array<A> => as.slice(n, as.length)
+export const dropLeft = (n: number) => <A>(as: Array<A>): Array<A> =>
+  n <= 0 ? copy(as) : n >= as.length ? [] : as.slice(n, as.length)
 
 /**
- * Drop a number of elements from the end of an array, creating a new array
+ * Drop a number of elements from the end of an `ReadonlyArray`, creating a new `ReadonlyArray`.
+ * If `n` (must be a natural number) is negative a copy of the input is returned.
  *
  * @example
  * import { dropRight } from 'fp-ts/Array'
@@ -460,7 +467,8 @@ export const dropLeft = (n: number) => <A>(as: Array<A>): Array<A> => as.slice(n
  * @category combinators
  * @since 2.0.0
  */
-export const dropRight = (n: number) => <A>(as: Array<A>): Array<A> => as.slice(0, as.length - n)
+export const dropRight = (n: number) => <A>(as: Array<A>): Array<A> =>
+  n <= 0 ? copy(as) : n >= as.length ? [] : as.slice(0, as.length - n)
 
 /**
  * Remove the longest initial subarray for which all element satisfy the specified predicate, creating a new array
@@ -1019,7 +1027,7 @@ export const chunksOf = (n: number): (<A>(as: Array<A>) => Array<NonEmptyArray<A
 }
 
 /**
- * Array comprehension
+ * `Array` comprehension.
  *
  * ```
  * [ f(x, y, ...) | x ← xs, y ← ys, ..., g(x, y, ...) ]
@@ -1061,13 +1069,15 @@ export function comprehension<R>(
   f: (...xs: Array<any>) => R,
   g: (...xs: Array<any>) => boolean = () => true
 ): Array<R> {
-  const go = (scope: Array<any>, input: Array<Array<any>>): Array<R> => {
-    if (input.length === 0) {
-      return g(...scope) ? [f(...scope)] : []
-    } else {
-      return _chain(input[0], (x) => go(pipe(scope, append(x)), input.slice(1)))
-    }
-  }
+  const go = (scope: Array<any>, input: Array<Array<any>>): Array<R> =>
+    isNonEmpty(input)
+      ? pipe(
+          NEA.head(input),
+          chain((x) => go(pipe(scope, append(x)), NEA.tail(input)))
+        )
+      : g(...scope)
+      ? [f(...scope)]
+      : []
   return go([], input)
 }
 
@@ -1170,7 +1180,7 @@ export function difference<A>(E: Eq<A>): (xs: Array<A>, ys?: Array<A>) => Array<
  * @category Pointed
  * @since 2.0.0
  */
-export const of: Pointed1<URI>['of'] = (a) => [a]
+export const of: Pointed1<URI>['of'] = NEA.of
 
 // -------------------------------------------------------------------------------------
 // non-pipeables
@@ -2007,13 +2017,6 @@ export const unsafeDeleteAt = <A>(i: number, as: Array<A>): Array<A> => {
 // utils
 // -------------------------------------------------------------------------------------
 
-/**
- * An empty array
- *
- * @since 2.0.0
- */
-export const empty: Array<never> = []
-
 /**
  * @since 2.9.0
  */
@@ -2064,6 +2067,14 @@ export const apS =
 // deprecated
 // -------------------------------------------------------------------------------------
 
+/**
+ * Use a new `[]` instead.
+ *
+ * @since 2.0.0
+ * @deprecated
+ */
+export const empty: Array<never> = []
+
 /**
  * Use `prepend` instead.
  *
diff --git a/src/Map.ts b/src/Map.ts
index 245842f2..f9d27c1c 100644
--- a/src/Map.ts
+++ b/src/Map.ts
@@ -279,11 +279,6 @@ export const isSubmap: <K, A>(
   (me: Map<K, A>, that: Map<K, A>): boolean
 } = RM.isSubmap
 
-/**
- * @since 2.0.0
- */
-export const empty = new Map<never, never>()
-
 /**
  * @category instances
  * @since 2.0.0
@@ -813,6 +808,14 @@ export const Filterable: Filterable2<URI> = {
 // deprecated
 // -------------------------------------------------------------------------------------
 
+/**
+ * Use a `new Map()` instead.
+ *
+ * @since 2.0.0
+ * @deprecated
+ */
+export const empty = new Map<never, never>()
+
 /**
  * Use `upsertAt` instead.
  *
diff --git a/src/NonEmptyArray.ts b/src/NonEmptyArray.ts
index 63aef66b..d5ee1069 100644
--- a/src/NonEmptyArray.ts
+++ b/src/NonEmptyArray.ts
@@ -1,5 +1,14 @@
 /**
- * Data structure which represents non-empty arrays
+ * Data structure which represents non-empty arrays.
+ *
+ * ```ts
+ * export type NonEmptyArray<A> = Array<A> & {
+ *   0: A
+ * }
+ * ```
+ *
+ * Note that you don't need any conversion, a `NonEmptyArray` is an `Array`,
+ * so all `Array`'s APIs can be used with a `NonEmptyArray` without further ado.
  *
  * @since 2.0.0
  */
@@ -44,49 +53,54 @@ export interface NonEmptyArray<A> extends Array<A> {
 }
 
 // -------------------------------------------------------------------------------------
-// guards
+// internal
 // -------------------------------------------------------------------------------------
 
 /**
- * Test whether a `Array` is non empty.
- *
- * @category guards
- * @since 2.10.0
+ * @internal
  */
 export const isNonEmpty = <A>(as: Array<A>): as is NonEmptyArray<A> => as.length > 0
 
-// -------------------------------------------------------------------------------------
-// constructors
-// -------------------------------------------------------------------------------------
+/**
+ * @internal
+ */
+export const isOutOfBound = <A>(i: number, as: Array<A>): boolean => i < 0 || i >= as.length
 
 /**
- * Prepend an element to the front of a `ReadonlyArray`, creating a new `ReadonlyNonEmptyArray`.
- *
- * @example
- * import { prepend } from 'fp-ts/NonEmptyArray'
- * import { pipe } from 'fp-ts/function'
- *
- * assert.deepStrictEqual(pipe([2, 3, 4], prepend(1)), [1, 2, 3, 4])
- *
- * @category constructors
- * @since 2.10.0
+ * @internal
  */
 export const prepend = <A>(head: A) => (tail: Array<A>): NonEmptyArray<A> => [head, ...tail]
 
 /**
- * Append an element to the end of a `ReadonlyArray`, creating a new `ReadonlyNonEmptyArray`.
- *
- * @example
- * import { append } from 'fp-ts/NonEmptyArray'
- * import { pipe } from 'fp-ts/function'
- *
- * assert.deepStrictEqual(pipe([1, 2, 3], append(4)), [1, 2, 3, 4])
- *
- * @category constructors
- * @since 2.10.0
+ * @internal
  */
 export const append = <A>(end: A) => (init: Array<A>): NonEmptyArray<A> => concat(init, [end])
 
+/**
+ * @internal
+ */
+export const unsafeInsertAt = <A>(i: number, a: A, as: Array<A>): NonEmptyArray<A> => {
+  if (isNonEmpty(as)) {
+    const xs = fromReadonlyNonEmptyArray(as)
+    xs.splice(i, 0, a)
+    return xs
+  }
+  return [a]
+}
+
+/**
+ * @internal
+ */
+export const unsafeUpdateAt = <A>(i: number, a: A, as: NonEmptyArray<A>): NonEmptyArray<A> => {
+  const xs = fromReadonlyNonEmptyArray(as)
+  xs[i] = a
+  return xs
+}
+
+// -------------------------------------------------------------------------------------
+// constructors
+// -------------------------------------------------------------------------------------
+
 /**
  * @category constructors
  * @since 2.10.0
@@ -257,18 +271,6 @@ export const groupBy = <A>(f: (a: A) => string) => (as: Array<A>): Record<string
 export const sort = <B>(O: Ord<B>) => <A extends B>(as: NonEmptyArray<A>): NonEmptyArray<A> =>
   as.length === 1 ? as : (as.slice().sort(O.compare) as any)
 
-/**
- * @internal
- */
-export const unsafeInsertAt = <A>(i: number, a: A, as: Array<A>): NonEmptyArray<A> => {
-  if (isNonEmpty(as)) {
-    const xs = fromReadonlyNonEmptyArray(as)
-    xs.splice(i, 0, a)
-    return xs
-  }
-  return [a]
-}
-
 /**
  * @category combinators
  * @since 2.0.0
@@ -283,24 +285,6 @@ export const insertAt = <A>(i: number, a: A) => (as: Array<A>): Option<NonEmptyA
 export const updateAt = <A>(i: number, a: A): ((as: NonEmptyArray<A>) => Option<NonEmptyArray<A>>) =>
   modifyAt(i, () => a)
 
-/**
- * @internal
- */
-export const isOutOfBound = <A>(i: number, as: Array<A>): boolean => i < 0 || i >= as.length
-
-/**
- * @internal
- */
-export const unsafeUpdateAt = <A>(i: number, a: A, as: NonEmptyArray<A>): NonEmptyArray<A> => {
-  if (as[i] === a) {
-    return as
-  } else {
-    const xs = fromReadonlyNonEmptyArray(as)
-    xs[i] = a
-    return xs
-  }
-}
-
 /**
  * @category combinators
  * @since 2.0.0
@@ -314,24 +298,6 @@ export const modifyAt = <A>(i: number, f: (a: A) => A) => (as: NonEmptyArray<A>)
  */
 export const copy: <A>(as: NonEmptyArray<A>) => NonEmptyArray<A> = fromReadonlyNonEmptyArray
 
-/**
- * @category combinators
- * @since 2.0.0
- */
-export function filter<A, B extends A>(refinement: Refinement<A, B>): (as: NonEmptyArray<A>) => Option<NonEmptyArray<B>>
-export function filter<A>(predicate: Predicate<A>): (as: NonEmptyArray<A>) => Option<NonEmptyArray<A>>
-export function filter<A>(predicate: Predicate<A>): (as: NonEmptyArray<A>) => Option<NonEmptyArray<A>> {
-  return filterWithIndex((_, a) => predicate(a))
-}
-
-/**
- * @category combinators
- * @since 2.0.0
- */
-export const filterWithIndex = <A>(predicate: (i: number, a: A) => boolean) => (
-  as: NonEmptyArray<A>
-): Option<NonEmptyArray<A>> => fromArray(as.filter((a, i) => predicate(i, a)))
-
 /**
  * @category Pointed
  * @since 2.0.0
@@ -1023,6 +989,31 @@ export const concatAll = <A>(S: Semigroup<A>) => (as: NonEmptyArray<A>): A => as
 // deprecated
 // -------------------------------------------------------------------------------------
 
+/**
+ * Use `Array`'s `filter` instead.
+ *
+ * @category combinators
+ * @since 2.0.0
+ * @deprecated
+ */
+export function filter<A, B extends A>(refinement: Refinement<A, B>): (as: NonEmptyArray<A>) => Option<NonEmptyArray<B>>
+export function filter<A>(predicate: Predicate<A>): (as: NonEmptyArray<A>) => Option<NonEmptyArray<A>>
+export function filter<A>(predicate: Predicate<A>): (as: NonEmptyArray<A>) => Option<NonEmptyArray<A>> {
+  // tslint:disable-next-line: deprecation
+  return filterWithIndex((_, a) => predicate(a))
+}
+
+/**
+ * Use `Array`'s `filterWithIndex` instead.
+ *
+ * @category combinators
+ * @since 2.0.0
+ * @deprecated
+ */
+export const filterWithIndex = <A>(predicate: (i: number, a: A) => boolean) => (
+  as: NonEmptyArray<A>
+): Option<NonEmptyArray<A>> => fromArray(as.filter((a, i) => predicate(i, a)))
+
 /**
  * Use `unprepend` instead.
  *
@@ -1042,7 +1033,7 @@ export const uncons: <A>(as: NonEmptyArray<A>) => [A, Array<A>] = unprepend
 export const unsnoc: <A>(as: NonEmptyArray<A>) => [Array<A>, A] = unappend
 
 /**
- * Use `prepend` instead.
+ * Use `Array`'s `prepend` instead.
  *
  * @category constructors
  * @since 2.0.0
@@ -1056,7 +1047,7 @@ export function cons<A>(head: A, tail?: Array<A>): NonEmptyArray<A> | ((tail: Ar
 }
 
 /**
- * Use `append` instead.
+ * Use `Array`'s `append` instead.
  *
  * @category constructors
  * @since 2.0.0
diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts
index 3296ef1a..04a6f4c9 100644
--- a/src/ReadonlyArray.ts
+++ b/src/ReadonlyArray.ts
@@ -76,7 +76,8 @@ export const append = RNEA.append
 export const fromArray = <A>(as: Array<A>): ReadonlyArray<A> => (isEmpty(as) ? empty : as.slice())
 
 /**
- * Return a list of length `n` with element `i` initialized with `f(i)`
+ * Return a `ReadonlyArray` of length `n` with element `i` initialized with `f(i)`.
+ * If `n` (must be a natural number) is negative return `empty`.
  *
  * @example
  * import { makeBy } from 'fp-ts/ReadonlyArray'
@@ -99,7 +100,7 @@ export const makeBy = <A>(n: number, f: (i: number) => A): ReadonlyArray<A> => {
 }
 
 /**
- * Create an array containing a range of integers, including both endpoints
+ * Create a `ReadonlyArray` containing a range of integers, including both endpoints.
  *
  * @example
  * import { range } from 'fp-ts/ReadonlyArray'
@@ -109,10 +110,11 @@ export const makeBy = <A>(n: number, f: (i: number) => A): ReadonlyArray<A> => {
  * @category constructors
  * @since 2.5.0
  */
-export const range = (start: number, end: number): ReadonlyArray<number> => makeBy(end - start + 1, (i) => start + i)
+export const range = (start: number, end: number): ReadonlyArray<number> =>
+  start <= end ? makeBy(end - start + 1, (i) => start + i) : [start]
 
 /**
- * Create an array containing a value repeated the specified number of times
+ * Create a `ReadonlyArray` containing a value repeated the specified number of times.
  *
  * @example
  * import { replicate } from 'fp-ts/ReadonlyArray'
@@ -343,8 +345,8 @@ export const init = <A>(as: ReadonlyArray<A>): Option<ReadonlyArray<A>> =>
   isNonEmpty(as) ? O.some(RNEA.init(as)) : O.none
 
 /**
- * Keep only a number of elements from the start of a `ReadonlyArray`, creating a new `ReadonlyArray`.
- * The input `n` must be a natural number.
+ * Keep only a number of elements from the start of an `ReadonlyArray`, creating a new `ReadonlyArray`.
+ * If `n` (must be a natural number) is out of bound the input is returned.
  *
  * @example
  * import { takeLeft } from 'fp-ts/ReadonlyArray'
@@ -354,11 +356,12 @@ export const init = <A>(as: ReadonlyArray<A>): Option<ReadonlyArray<A>> =>
  * @category combinators
  * @since 2.5.0
  */
-export const takeLeft = (n: number) => <A>(as: ReadonlyArray<A>): ReadonlyArray<A> => (n <= 0 ? empty : as.slice(0, n))
+export const takeLeft = (n: number) => <A>(as: ReadonlyArray<A>): ReadonlyArray<A> =>
+  isOutOfBound(n, as) ? as : n === 0 ? empty : as.slice(0, n)
 
 /**
- * Keep only a number of elements from the end of a `ReadonlyArray`, creating a new `ReadonlyArray`.
- * The input `n` must be a natural number.
+ * Keep only a number of elements from the end of an `ReadonlyArray`, creating a new `ReadonlyArray`.
+ * If `n` (must be a natural number) is out of bound the input is returned.
  *
  * @example
  * import { takeRight } from 'fp-ts/ReadonlyArray'
@@ -367,7 +370,8 @@ export const takeLeft = (n: number) => <A>(as: ReadonlyArray<A>): ReadonlyArray<
  *
  * @since 2.5.0
  */
-export const takeRight = (n: number) => <A>(as: ReadonlyArray<A>): ReadonlyArray<A> => (n <= 0 ? empty : as.slice(-n))
+export const takeRight = (n: number) => <A>(as: ReadonlyArray<A>): ReadonlyArray<A> =>
+  isOutOfBound(n, as) ? as : n === 0 ? empty : as.slice(-n)
 
 /**
  * Calculate the longest initial subarray for which all element satisfy the specified predicate, creating a new array
@@ -443,7 +447,8 @@ export function spanLeft<A>(predicate: Predicate<A>): (as: ReadonlyArray<A>) =>
 }
 
 /**
- * Drop a number of elements from the start of an array, creating a new array
+ * Drop a number of elements from the start of an `ReadonlyArray`, creating a new `ReadonlyArray`.
+ * If `n` (must be a natural number) is negative the input is returned.
  *
  * @example
  * import { dropLeft } from 'fp-ts/ReadonlyArray'
@@ -453,10 +458,12 @@ export function spanLeft<A>(predicate: Predicate<A>): (as: ReadonlyArray<A>) =>
  * @category combinators
  * @since 2.5.0
  */
-export const dropLeft = (n: number) => <A>(as: ReadonlyArray<A>): ReadonlyArray<A> => as.slice(n, as.length)
+export const dropLeft = (n: number) => <A>(as: ReadonlyArray<A>): ReadonlyArray<A> =>
+  n <= 0 ? as : n >= as.length ? empty : as.slice(n, as.length)
 
 /**
- * Drop a number of elements from the end of an array, creating a new array
+ * Drop a number of elements from the end of an `ReadonlyArray`, creating a new `ReadonlyArray`.
+ * If `n` (must be a natural number) is negative the input is returned.
  *
  * @example
  * import { dropRight } from 'fp-ts/ReadonlyArray'
@@ -466,7 +473,8 @@ export const dropLeft = (n: number) => <A>(as: ReadonlyArray<A>): ReadonlyArray<
  * @category combinators
  * @since 2.5.0
  */
-export const dropRight = (n: number) => <A>(as: ReadonlyArray<A>): ReadonlyArray<A> => as.slice(0, as.length - n)
+export const dropRight = (n: number) => <A>(as: ReadonlyArray<A>): ReadonlyArray<A> =>
+  n <= 0 ? as : n >= as.length ? empty : as.slice(0, as.length - n)
 
 /**
  * Remove the longest initial subarray for which all element satisfy the specified predicate, creating a new array
@@ -1088,7 +1096,7 @@ export const chunksOf = (n: number): (<A>(as: ReadonlyArray<A>) => ReadonlyArray
 }
 
 /**
- * Array comprehension
+ * `ReadonlyArray` comprehension.
  *
  * ```
  * [ f(x, y, ...) | x ← xs, y ← ys, ..., g(x, y, ...) ]
@@ -1138,13 +1146,15 @@ export function comprehension<R>(
   f: (...xs: ReadonlyArray<any>) => R,
   g: (...xs: ReadonlyArray<any>) => boolean = () => true
 ): ReadonlyArray<R> {
-  const go = (scope: ReadonlyArray<any>, input: ReadonlyArray<ReadonlyArray<any>>): ReadonlyArray<R> => {
-    if (input.length === 0) {
-      return g(...scope) ? [f(...scope)] : empty
-    } else {
-      return _chain(input[0], (x) => go(pipe(scope, append(x)), input.slice(1)))
-    }
-  }
+  const go = (scope: ReadonlyArray<any>, input: ReadonlyArray<ReadonlyArray<any>>): ReadonlyArray<R> =>
+    isNonEmpty(input)
+      ? pipe(
+          RNEA.head(input),
+          chain((x) => go(pipe(scope, append(x)), RNEA.tail(input)))
+        )
+      : g(...scope)
+      ? [f(...scope)]
+      : empty
   return go(empty, input)
 }
 
@@ -1253,7 +1263,7 @@ export function difference<A>(
  * @category Pointed
  * @since 2.5.0
  */
-export const of: Pointed1<URI>['of'] = (a) => [a]
+export const of: Pointed1<URI>['of'] = RNEA.of
 
 /**
  * @category Alternative
diff --git a/src/ReadonlyMap.ts b/src/ReadonlyMap.ts
index 69cae24a..19a602ee 100644
--- a/src/ReadonlyMap.ts
+++ b/src/ReadonlyMap.ts
@@ -272,8 +272,13 @@ export const modifyAt = <K>(
     if (O.isNone(found)) {
       return O.none
     }
+    const [fk, fv] = found.value
+    const next = f(fv)
+    if (next === fv) {
+      return O.some(m)
+    }
     const r = new Map(m)
-    r.set(found.value[0], f(found.value[1]))
+    r.set(fk, next)
     return O.some(r)
   }
 }
@@ -398,7 +403,9 @@ export function isSubmap<K, A>(
 /**
  * @since 2.5.0
  */
-export const empty: ReadonlyMap<never, never> = new Map<never, never>()
+export const empty: ReadonlyMap<never, never> =
+  // the type annotation here is intended (otherwise it doesn't type-check)
+  new Map<never, never>()
 
 /**
  * @category instances
diff --git a/src/ReadonlyNonEmptyArray.ts b/src/ReadonlyNonEmptyArray.ts
index c256c47f..74ba0697 100644
--- a/src/ReadonlyNonEmptyArray.ts
+++ b/src/ReadonlyNonEmptyArray.ts
@@ -1,5 +1,14 @@
 /**
- * Data structure which represents non-empty arrays
+ * Data structure which represents non-empty readonly arrays.
+ *
+ * ```ts
+ * export type ReadonlyNonEmptyArray<A> = ReadonlyArray<A> & {
+ *   readonly 0: A
+ * }
+ * ```
+ *
+ * Note that you don't need any conversion, a `ReadonlyNonEmptyArray` is a `ReadonlyArray`,
+ * so all `ReadonlyArray`'s APIs can be used with a `ReadonlyNonEmptyArray` without further ado.
  *
  * @since 2.5.0
  */
@@ -42,55 +51,64 @@ export type ReadonlyNonEmptyArray<A> = ReadonlyArray<A> & {
   readonly 0: A
 }
 
+// -------------------------------------------------------------------------------------
+// internal
+// -------------------------------------------------------------------------------------
+
 /**
  * @internal
  */
 export const empty: ReadonlyArray<never> = []
 
-// -------------------------------------------------------------------------------------
-// guards
-// -------------------------------------------------------------------------------------
-
 /**
- * Test whether a `ReadonlyArray` is non empty.
- *
- * @category guards
- * @since 2.10.0
+ * @internal
  */
 export const isNonEmpty = <A>(as: ReadonlyArray<A>): as is ReadonlyNonEmptyArray<A> => as.length > 0
 
-// -------------------------------------------------------------------------------------
-// constructors
-// -------------------------------------------------------------------------------------
+/**
+ * @internal
+ */
+export const isOutOfBound = <A>(i: number, as: ReadonlyArray<A>): boolean => i < 0 || i >= as.length
 
 /**
- * Prepend an element to the front of a `ReadonlyArray`, creating a new `ReadonlyNonEmptyArray`.
- *
- * @example
- * import { prepend } from 'fp-ts/ReadonlyNonEmptyArray'
- * import { pipe } from 'fp-ts/function'
- *
- * assert.deepStrictEqual(pipe([2, 3, 4], prepend(1)), [1, 2, 3, 4])
- *
- * @category constructors
- * @since 2.10.0
+ * @internal
  */
 export const prepend = <A>(head: A) => (tail: ReadonlyArray<A>): ReadonlyNonEmptyArray<A> => [head, ...tail]
 
 /**
- * Append an element to the end of a `ReadonlyArray`, creating a new `ReadonlyNonEmptyArray`.
- *
- * @example
- * import { append } from 'fp-ts/ReadonlyNonEmptyArray'
- * import { pipe } from 'fp-ts/function'
- *
- * assert.deepStrictEqual(pipe([1, 2, 3], append(4)), [1, 2, 3, 4])
- *
- * @category constructors
- * @since 2.10.0
+ * @internal
  */
 export const append = <A>(end: A) => (init: ReadonlyArray<A>): ReadonlyNonEmptyArray<A> => concat(init, [end])
 
+/**
+ * @internal
+ */
+export const unsafeInsertAt = <A>(i: number, a: A, as: ReadonlyArray<A>): ReadonlyNonEmptyArray<A> => {
+  if (isNonEmpty(as)) {
+    const xs = fromReadonlyNonEmptyArray(as)
+    xs.splice(i, 0, a)
+    return xs
+  }
+  return [a]
+}
+
+/**
+ * @internal
+ */
+export const unsafeUpdateAt = <A>(i: number, a: A, as: ReadonlyNonEmptyArray<A>): ReadonlyNonEmptyArray<A> => {
+  if (as[i] === a) {
+    return as
+  } else {
+    const xs = fromReadonlyNonEmptyArray(as)
+    xs[i] = a
+    return xs
+  }
+}
+
+// -------------------------------------------------------------------------------------
+// constructors
+// -------------------------------------------------------------------------------------
+
 /**
  * Return a `ReadonlyNonEmptyArray` from a `ReadonlyArray` returning `none` if the input is empty.
  *
@@ -273,24 +291,6 @@ export const sort = <B>(O: Ord<B>) => <A extends B>(as: ReadonlyNonEmptyArray<A>
 export const updateAt = <A>(i: number, a: A): ((as: ReadonlyNonEmptyArray<A>) => Option<ReadonlyNonEmptyArray<A>>) =>
   modifyAt(i, () => a)
 
-/**
- * @internal
- */
-export const isOutOfBound = <A>(i: number, as: ReadonlyArray<A>): boolean => i < 0 || i >= as.length
-
-/**
- * @internal
- */
-export const unsafeUpdateAt = <A>(i: number, a: A, as: ReadonlyNonEmptyArray<A>): ReadonlyNonEmptyArray<A> => {
-  if (as[i] === a) {
-    return as
-  } else {
-    const xs = fromReadonlyNonEmptyArray(as)
-    xs[i] = a
-    return xs
-  }
-}
-
 /**
  * @category combinators
  * @since 2.5.0
@@ -299,26 +299,6 @@ export const modifyAt = <A>(i: number, f: (a: A) => A) => (
   as: ReadonlyNonEmptyArray<A>
 ): Option<ReadonlyNonEmptyArray<A>> => (isOutOfBound(i, as) ? O.none : O.some(unsafeUpdateAt(i, f(as[i]), as)))
 
-/**
- * @category combinators
- * @since 2.5.0
- */
-export function filter<A, B extends A>(
-  refinement: Refinement<A, B>
-): (as: ReadonlyNonEmptyArray<A>) => Option<ReadonlyNonEmptyArray<B>>
-export function filter<A>(predicate: Predicate<A>): (as: ReadonlyNonEmptyArray<A>) => Option<ReadonlyNonEmptyArray<A>>
-export function filter<A>(predicate: Predicate<A>): (as: ReadonlyNonEmptyArray<A>) => Option<ReadonlyNonEmptyArray<A>> {
-  return filterWithIndex((_, a) => predicate(a))
-}
-
-/**
- * @category combinators
- * @since 2.5.0
- */
-export const filterWithIndex = <A>(predicate: (i: number, a: A) => boolean) => (
-  as: ReadonlyNonEmptyArray<A>
-): Option<ReadonlyNonEmptyArray<A>> => fromReadonlyArray(as.filter((a, i) => predicate(i, a)))
-
 /**
  * @category combinators
  * @since 2.5.1
@@ -1056,6 +1036,33 @@ export const concatAll = <A>(S: Semigroup<A>) => (as: ReadonlyNonEmptyArray<A>):
 // deprecated
 // -------------------------------------------------------------------------------------
 
+/**
+ * Use `ReadonlyArray`'s `filter` instead.
+ *
+ * @category combinators
+ * @since 2.5.0
+ * @deprecated
+ */
+export function filter<A, B extends A>(
+  refinement: Refinement<A, B>
+): (as: ReadonlyNonEmptyArray<A>) => Option<ReadonlyNonEmptyArray<B>>
+export function filter<A>(predicate: Predicate<A>): (as: ReadonlyNonEmptyArray<A>) => Option<ReadonlyNonEmptyArray<A>>
+export function filter<A>(predicate: Predicate<A>): (as: ReadonlyNonEmptyArray<A>) => Option<ReadonlyNonEmptyArray<A>> {
+  // tslint:disable-next-line: deprecation
+  return filterWithIndex((_, a) => predicate(a))
+}
+
+/**
+ * Use `ReadonlyArray`'s `filterWithIndex` instead.
+ *
+ * @category combinators
+ * @since 2.5.0
+ * @deprecated
+ */
+export const filterWithIndex = <A>(predicate: (i: number, a: A) => boolean) => (
+  as: ReadonlyNonEmptyArray<A>
+): Option<ReadonlyNonEmptyArray<A>> => fromReadonlyArray(as.filter((a, i) => predicate(i, a)))
+
 /**
  * Use `unprepend` instead.
  *
@@ -1075,7 +1082,7 @@ export const uncons: <A>(as: ReadonlyNonEmptyArray<A>) => readonly [A, ReadonlyA
 export const unsnoc: <A>(as: ReadonlyNonEmptyArray<A>) => readonly [ReadonlyArray<A>, A] = unappend
 
 /**
- * Use `prepend` instead.
+ * Use `ReadonlyArray`'s `prepend` instead.
  *
  * @category constructors
  * @since 2.5.0
@@ -1092,7 +1099,7 @@ export function cons<A>(
 }
 
 /**
- * Use `append` instead.
+ * Use `ReadonlyArray`'s `append` instead.
  *
  * @category constructors
  * @since 2.5.0
@@ -1100,18 +1107,6 @@ export function cons<A>(
  */
 export const snoc = <A>(init: ReadonlyArray<A>, end: A): ReadonlyNonEmptyArray<A> => concat(init, [end])
 
-/**
- * @internal
- */
-export const unsafeInsertAt = <A>(i: number, a: A, as: ReadonlyArray<A>): ReadonlyNonEmptyArray<A> => {
-  if (isNonEmpty(as)) {
-    const xs = fromReadonlyNonEmptyArray(as)
-    xs.splice(i, 0, a)
-    return xs
-  }
-  return [a]
-}
-
 /**
  * Use `ReadonlyArray`'s `insertAt` instead.
  *
diff --git a/src/ReadonlyRecord.ts b/src/ReadonlyRecord.ts
index 6421f5ec..0190803a 100644
--- a/src/ReadonlyRecord.ts
+++ b/src/ReadonlyRecord.ts
@@ -213,8 +213,12 @@ export const modifyAt = <A>(k: string, f: (a: A) => A) => <K extends string>(
   if (!has(k, r)) {
     return O.none
   }
+  const next = f(r[k])
+  if (next === r[k]) {
+    return O.some(r)
+  }
   const out: Record<K, A> = Object.assign({}, r)
-  out[k] = f(r[k])
+  out[k] = next
   return O.some(out)
 }
 
diff --git a/src/Record.ts b/src/Record.ts
index 53f535c2..4810b923 100644
--- a/src/Record.ts
+++ b/src/Record.ts
@@ -142,13 +142,20 @@ export function deleteAt(k: string): <A>(r: Record<string, A>) => Record<string,
 /**
  * @since 2.0.0
  */
-export const updateAt: <A>(k: string, a: A) => <K extends string>(r: Record<K, A>) => Option<Record<K, A>> = RR.updateAt
+export const updateAt = <A>(k: string, a: A): (<K extends string>(r: Record<K, A>) => Option<Record<K, A>>) =>
+  modifyAt(k, () => a)
 
 /**
  * @since 2.0.0
  */
-export const modifyAt: <A>(k: string, f: (a: A) => A) => <K extends string>(r: Record<K, A>) => Option<Record<K, A>> =
-  RR.modifyAt
+export const modifyAt = <A>(k: string, f: (a: A) => A) => <K extends string>(r: Record<K, A>): Option<Record<K, A>> => {
+  if (!has(k, r)) {
+    return O.none
+  }
+  const out: Record<K, A> = Object.assign({}, r)
+  out[k] = f(r[k])
+  return O.some(out)
+}
 
 /**
  * Delete a key and value from a map, returning the value as well as the subsequent map
@@ -209,11 +216,6 @@ export const lookup: {
   <A>(k: string, r: Record<string, A>): Option<A>
 } = RR.lookup
 
-/**
- * @since 2.0.0
- */
-export const empty: Record<string, never> = {}
-
 /**
  * Map a record passing the keys to the iterating function
  *
@@ -842,6 +844,14 @@ export const Witherable: Witherable1<URI> = {
 // deprecated
 // -------------------------------------------------------------------------------------
 
+/**
+ * Use a new `{}` instead.
+ *
+ * @since 2.0.0
+ * @deprecated
+ */
+export const empty: Record<string, never> = {}
+
 /**
  * Use `upsertAt` instead.
  *
diff --git a/src/Tree.ts b/src/Tree.ts
index 1f808f69..f28a2903 100644
--- a/src/Tree.ts
+++ b/src/Tree.ts
@@ -47,7 +47,7 @@ export interface Tree<A> {
  * @category constructors
  * @since 2.0.0
  */
-export function make<A>(value: A, forest: Forest<A> = A.empty): Tree<A> {
+export function make<A>(value: A, forest: Forest<A> = []): Tree<A> {
   return {
     value,
     forest
@@ -60,7 +60,7 @@ export function make<A>(value: A, forest: Forest<A> = A.empty): Tree<A> {
  */
 export function getShow<A>(S: Show<A>): Show<Tree<A>> {
   const show = (t: Tree<A>): string => {
-    return t.forest === A.empty || t.forest.length === 0
+    return A.isEmpty(t.forest)
       ? `make(${S.show(t.value)})`
       : `make(${S.show(t.value)}, [${t.forest.map(show).join(', ')}])`
   }


Test patch:
diff --git a/test/Array.ts b/test/Array.ts
index b4b1b463..09116e11 100644
--- a/test/Array.ts
+++ b/test/Array.ts
@@ -1,4 +1,3 @@
-import * as assert from 'assert'
 import * as fc from 'fast-check'
 import { isDeepStrictEqual } from 'util'
 import * as E from '../src/Either'
@@ -417,17 +416,27 @@ describe('Array', () => {
   })
 
   it('takeLeft', () => {
-    U.deepStrictEqual(_.takeLeft(2)([]), [])
-    U.deepStrictEqual(_.takeLeft(2)([1, 2, 3]), [1, 2])
-    U.deepStrictEqual(_.takeLeft(0)([1, 2, 3]), [])
+    U.deepStrictEqual(_.takeLeft(0)([]), [])
+    U.deepStrictEqual(_.takeLeft(0)([1, 2]), [])
+    U.deepStrictEqual(_.takeLeft(1)([1, 2]), [1])
+    U.deepStrictEqual(_.takeLeft(2)([1, 2]), [1, 2])
+    // out of bound
+    U.deepStrictEqual(_.takeLeft(1)([]), [])
+    U.deepStrictEqual(_.takeLeft(3)([1, 2]), [1, 2])
+    U.deepStrictEqual(_.takeLeft(-1)([]), [])
+    U.deepStrictEqual(_.takeLeft(-1)([1, 2]), [1, 2])
   })
 
   it('takeRight', () => {
-    U.deepStrictEqual(_.takeRight(2)([1, 2, 3, 4, 5]), [4, 5])
-    U.deepStrictEqual(_.takeRight(0)([1, 2, 3, 4, 5]), [])
-    U.deepStrictEqual(_.takeRight(2)([]), [])
-    U.deepStrictEqual(_.takeRight(5)([1, 2, 3, 4, 5]), [1, 2, 3, 4, 5])
-    U.deepStrictEqual(_.takeRight(10)([1, 2, 3, 4, 5]), [1, 2, 3, 4, 5])
+    U.deepStrictEqual(_.takeRight(0)([]), [])
+    U.deepStrictEqual(_.takeRight(0)([1, 2]), [])
+    U.deepStrictEqual(_.takeRight(1)([1, 2]), [2])
+    U.deepStrictEqual(_.takeRight(2)([1, 2]), [1, 2])
+    // out of bound
+    U.deepStrictEqual(_.takeRight(1)([]), [])
+    U.deepStrictEqual(_.takeRight(3)([1, 2]), [1, 2])
+    U.deepStrictEqual(_.takeRight(-1)([]), [])
+    U.deepStrictEqual(_.takeRight(-1)([1, 2]), [1, 2])
   })
 
   it('spanLeft', () => {
@@ -443,15 +452,25 @@ describe('Array', () => {
   })
 
   it('dropLeft', () => {
-    U.deepStrictEqual(_.dropLeft(2)([1, 2, 3]), [3])
-    U.deepStrictEqual(_.dropLeft(10)([1, 2, 3]), [])
-    U.deepStrictEqual(_.dropLeft(0)([1, 2, 3]), [1, 2, 3])
+    U.deepStrictEqual(_.dropLeft(0)([1, 2]), [1, 2])
+    U.deepStrictEqual(_.dropLeft(1)([1, 2]), [2])
+    U.deepStrictEqual(_.dropLeft(2)([1, 2]), [])
+    // out of bound
+    U.deepStrictEqual(_.dropLeft(1)([]), [])
+    U.deepStrictEqual(_.dropLeft(3)([1, 2]), [])
+    U.deepStrictEqual(_.dropLeft(-1)([]), [])
+    U.deepStrictEqual(_.dropLeft(-1)([1, 2]), [1, 2])
   })
 
   it('dropRight', () => {
-    U.deepStrictEqual(_.dropRight(2)([1, 2, 3, 4, 5]), [1, 2, 3])
-    U.deepStrictEqual(_.dropRight(10)([1, 2, 3, 4, 5]), [])
-    U.deepStrictEqual(_.dropRight(0)([1, 2, 3, 4, 5]), [1, 2, 3, 4, 5])
+    U.deepStrictEqual(_.dropRight(0)([1, 2]), [1, 2])
+    U.deepStrictEqual(_.dropRight(1)([1, 2]), [1])
+    U.deepStrictEqual(_.dropRight(2)([1, 2]), [])
+    // out of bound
+    U.deepStrictEqual(_.dropRight(1)([]), [])
+    U.deepStrictEqual(_.dropRight(3)([1, 2]), [])
+    U.deepStrictEqual(_.dropRight(-1)([]), [])
+    U.deepStrictEqual(_.dropRight(-1)([1, 2]), [1, 2])
   })
 
   it('dropLeftWhile', () => {
@@ -578,12 +597,13 @@ describe('Array', () => {
   })
 
   it('unsafeUpdateAt', () => {
-    // should return the same reference if nothing changed
-    const x = { a: 1 }
-    const as: Array<{ readonly a: number }> = [x]
-    const result = _.unsafeUpdateAt(0, x, as)
-    U.deepStrictEqual(result, as)
-    U.deepStrictEqual(_.unsafeUpdateAt(0, 1, []), _.empty)
+    U.deepStrictEqual(pipe(_.unsafeUpdateAt(1, 2, []), _.isEmpty), true)
+    // should not return the same reference if nothing changed
+    const input: Array<number> = [1, 2, 3]
+    U.deepStrictEqual(
+      pipe(_.unsafeUpdateAt(1, 2, input), (out) => out === input),
+      false
+    )
   })
 
   it('updateAt', () => {
@@ -603,11 +623,21 @@ describe('Array', () => {
     const double = (x: number): number => x * 2
     U.deepStrictEqual(_.modifyAt(1, double)(as), O.some([1, 4, 3]))
     U.deepStrictEqual(_.modifyAt(1, double)([]), O.none)
+    // should not return the same reference if nothing changed
+    const input: Array<number> = [1, 2, 3]
+    U.deepStrictEqual(
+      pipe(
+        input,
+        _.modifyAt(1, identity),
+        O.map((out) => out === input)
+      ),
+      O.some(false)
+    )
   })
 
   it('sort', () => {
     U.deepStrictEqual(_.sort(N.Ord)([3, 2, 1]), [1, 2, 3])
-    assert.strictEqual(_.sort(N.Ord)(_.empty), _.empty)
+    U.deepStrictEqual(_.sort(N.Ord)([]), [])
     const byName = pipe(
       S.Ord,
       Ord.contramap((x: { readonly name: string }) => x.name)
@@ -882,12 +912,20 @@ describe('Array', () => {
   it('makeBy', () => {
     const double = (n: number): number => n * 2
     U.deepStrictEqual(_.makeBy(5, double), [0, 2, 4, 6, 8])
+    U.deepStrictEqual(_.makeBy(0, double), [])
+    U.deepStrictEqual(_.makeBy(-1, double), [])
   })
 
   it('range', () => {
     U.deepStrictEqual(_.range(0, 0), [0])
+    U.deepStrictEqual(_.range(0, 1), [0, 1])
     U.deepStrictEqual(_.range(1, 5), [1, 2, 3, 4, 5])
     U.deepStrictEqual(_.range(10, 15), [10, 11, 12, 13, 14, 15])
+    U.deepStrictEqual(_.range(-1, 0), [-1, 0])
+    U.deepStrictEqual(_.range(-5, -1), [-5, -4, -3, -2, -1])
+    // out of bound
+    U.deepStrictEqual(_.range(2, 1), [2])
+    U.deepStrictEqual(_.range(-1, -2), [-1])
   })
 
   it('replicate', () => {
@@ -979,7 +1017,6 @@ describe('Array', () => {
   })
 
   it('size', () => {
-    U.deepStrictEqual(_.size(_.empty), 0)
     U.deepStrictEqual(_.size([]), 0)
     U.deepStrictEqual(_.size(['a']), 1)
   })
diff --git a/test/Map.ts b/test/Map.ts
index 2814a52e..5029ca5f 100644
--- a/test/Map.ts
+++ b/test/Map.ts
@@ -61,7 +61,6 @@ describe('Map', () => {
     U.deepStrictEqual(_.size(emptyMap), 0)
     U.deepStrictEqual(_.size(a1), 1)
 
-    U.deepStrictEqual(_.size(_.empty), 0)
     U.deepStrictEqual(_.size(new Map()), 0)
     U.deepStrictEqual(_.size(new Map([['a', 1]])), 1)
   })
@@ -72,7 +71,6 @@ describe('Map', () => {
     U.deepStrictEqual(_.isEmpty(emptyMap), true)
     U.deepStrictEqual(_.isEmpty(a1), false)
 
-    U.deepStrictEqual(_.isEmpty(_.empty), true)
     U.deepStrictEqual(_.isEmpty(new Map()), true)
     U.deepStrictEqual(_.isEmpty(new Map([['a', 1]])), false)
   })
@@ -377,7 +375,7 @@ describe('Map', () => {
 
     // tslint:disable-next-line: deprecation
     const insert = _.insertAt(eqKey)
-    U.deepStrictEqual(insert({ id: 1 }, { value: 1 })(_.empty), new Map([[{ id: 1 }, { value: 1 }]]))
+    U.deepStrictEqual(insert({ id: 1 }, { value: 1 })(new Map()), new Map([[{ id: 1 }, { value: 1 }]]))
     const x = insert({ id: 1 }, value1)(repo)
     U.deepStrictEqual(
       x,
@@ -530,11 +528,6 @@ describe('Map', () => {
     U.deepStrictEqual(pipe(new Map([[{ id: 3 }, { value: 3 }]]), isSubmap(repo)), false)
   })
 
-  it('empty', () => {
-    U.deepStrictEqual(_.empty, new Map<string, number>())
-    U.deepStrictEqual(_.isEmpty(_.empty), true)
-  })
-
   it('singleton', () => {
     U.deepStrictEqual(
       _.singleton('k1', 0),
@@ -920,7 +913,7 @@ describe('Map', () => {
     it('wither', async () => {
       const wither = W.wither(T.ApplicativePar)
       const f = (n: number) => T.of(p(n) ? O.some(n + 1) : O.none)
-      U.deepStrictEqual(await wither(_.empty, f)(), _.empty)
+      U.deepStrictEqual(await wither(new Map(), f)(), new Map())
       U.deepStrictEqual(
         await wither(
           new Map([
@@ -936,7 +929,7 @@ describe('Map', () => {
     it('wilt', async () => {
       const wilt = W.wilt(T.ApplicativePar)
       const f = (n: number) => T.of(p(n) ? right(n + 1) : left(n - 1))
-      U.deepStrictEqual(await wilt(_.empty, f)(), separated(_.empty, _.empty))
+      U.deepStrictEqual(await wilt(new Map(), f)(), separated(new Map(), new Map()))
       U.deepStrictEqual(
         await wilt(
           new Map([
@@ -1073,6 +1066,16 @@ describe('Map', () => {
         new Map<User, number>([[{ id: 'a' }, 2]])
       )
     )
+    // should not return the same reference if nothing changed
+    const input: Map<string, number> = new Map([['a', 1]])
+    U.deepStrictEqual(
+      pipe(
+        input,
+        _.modifyAt(S.Eq)('a', identity),
+        O.map((out) => out === input)
+      ),
+      O.some(false)
+    )
   })
 
   it('mapWithIndex', () => {
diff --git a/test/NonEmptyArray.ts b/test/NonEmptyArray.ts
index eb13392c..7c8b8b17 100644
--- a/test/NonEmptyArray.ts
+++ b/test/NonEmptyArray.ts
@@ -245,6 +245,16 @@ describe('NonEmptyArray', () => {
     const double = (n: number): number => n * 2
     U.deepStrictEqual(_.modifyAt(1, double)([1]), O.none)
     U.deepStrictEqual(_.modifyAt(1, double)([1, 2]), O.some([1, 4]))
+    // should not return the same reference if nothing changed
+    const input: _.NonEmptyArray<number> = [1, 2, 3]
+    U.deepStrictEqual(
+      pipe(
+        input,
+        _.modifyAt(1, identity),
+        O.map((out) => out === input)
+      ),
+      O.some(false)
+    )
   })
 
   it('copy', () => {
@@ -288,8 +298,10 @@ describe('NonEmptyArray', () => {
     )
 
     // refinements
+    // tslint:disable-next-line: deprecation
     const actual1 = _.filter(O.isSome)([O.some(3), O.some(2), O.some(1)])
     U.deepStrictEqual(actual1, O.some([O.some(3), O.some(2), O.some(1)]))
+    // tslint:disable-next-line: deprecation
     const actual2 = _.filter(O.isSome)([O.some(3), O.none, O.some(1)])
     U.deepStrictEqual(actual2, O.some([O.some(3), O.some(1)]))
   })
@@ -298,10 +310,12 @@ describe('NonEmptyArray', () => {
     U.deepStrictEqual(
       pipe(
         [1, 2, 3],
+        // tslint:disable-next-line: deprecation
         _.filterWithIndex((i) => i % 2 === 0)
       ),
       O.some([1, 3])
     )
+    // tslint:disable-next-line: deprecation
     U.deepStrictEqual(_.filterWithIndex((i, a: number) => i % 2 === 1 && a > 2)([1, 2, 3]), O.none)
   })
 
diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts
index 1cfa8159..7b7dad14 100644
--- a/test/ReadonlyArray.ts
+++ b/test/ReadonlyArray.ts
@@ -414,22 +414,27 @@ describe('ReadonlyArray', () => {
   })
 
   it('takeLeft', () => {
-    U.deepStrictEqual(_.takeLeft(2)([1, 2, 3]), [1, 2])
-    U.deepStrictEqual(_.takeLeft(0)([1, 2, 3]), [])
+    U.deepStrictEqual(_.takeLeft(0)([]), [])
+    U.deepStrictEqual(_.takeLeft(0)([1, 2]), [])
+    U.deepStrictEqual(_.takeLeft(1)([1, 2]), [1])
+    U.deepStrictEqual(_.takeLeft(2)([1, 2]), [1, 2])
     // out of bound
-    U.deepStrictEqual(_.takeLeft(2)([]), [])
-    U.deepStrictEqual(_.takeLeft(5)([1, 2, 3]), [1, 2, 3])
-    U.deepStrictEqual(_.takeLeft(-1)([1, 2, 3]), [])
+    U.deepStrictEqual(_.takeLeft(1)([]), [])
+    U.deepStrictEqual(_.takeLeft(3)([1, 2]), [1, 2])
+    U.deepStrictEqual(_.takeLeft(-1)([]), [])
+    U.deepStrictEqual(_.takeLeft(-1)([1, 2]), [1, 2])
   })
 
   it('takeRight', () => {
-    U.deepStrictEqual(_.takeRight(2)([1, 2, 3, 4, 5]), [4, 5])
-    U.deepStrictEqual(_.takeRight(0)([1, 2, 3, 4, 5]), [])
-    U.deepStrictEqual(_.takeRight(5)([1, 2, 3, 4, 5]), [1, 2, 3, 4, 5])
+    U.deepStrictEqual(_.takeRight(0)([]), [])
+    U.deepStrictEqual(_.takeRight(0)([1, 2]), [])
+    U.deepStrictEqual(_.takeRight(1)([1, 2]), [2])
+    U.deepStrictEqual(_.takeRight(2)([1, 2]), [1, 2])
     // out of bound
-    U.deepStrictEqual(_.takeRight(2)([]), [])
-    U.deepStrictEqual(_.takeRight(10)([1, 2, 3, 4, 5]), [1, 2, 3, 4, 5])
-    U.deepStrictEqual(_.takeLeft(-1)([1, 2, 3]), [])
+    U.deepStrictEqual(_.takeRight(1)([]), [])
+    U.deepStrictEqual(_.takeRight(3)([1, 2]), [1, 2])
+    U.deepStrictEqual(_.takeRight(-1)([]), [])
+    U.deepStrictEqual(_.takeRight(-1)([1, 2]), [1, 2])
   })
 
   it('spanLeft', () => {
@@ -445,15 +450,25 @@ describe('ReadonlyArray', () => {
   })
 
   it('dropLeft', () => {
-    U.deepStrictEqual(_.dropLeft(2)([1, 2, 3]), [3])
-    U.deepStrictEqual(_.dropLeft(10)([1, 2, 3]), [])
-    U.deepStrictEqual(_.dropLeft(0)([1, 2, 3]), [1, 2, 3])
+    U.deepStrictEqual(_.dropLeft(0)([1, 2]), [1, 2])
+    U.deepStrictEqual(_.dropLeft(1)([1, 2]), [2])
+    U.deepStrictEqual(_.dropLeft(2)([1, 2]), [])
+    // out of bound
+    U.deepStrictEqual(_.dropLeft(1)([]), [])
+    U.deepStrictEqual(_.dropLeft(3)([1, 2]), [])
+    U.deepStrictEqual(_.dropLeft(-1)([]), [])
+    U.deepStrictEqual(_.dropLeft(-1)([1, 2]), [1, 2])
   })
 
   it('dropRight', () => {
-    U.deepStrictEqual(_.dropRight(2)([1, 2, 3, 4, 5]), [1, 2, 3])
-    U.deepStrictEqual(_.dropRight(10)([1, 2, 3, 4, 5]), [])
-    U.deepStrictEqual(_.dropRight(0)([1, 2, 3, 4, 5]), [1, 2, 3, 4, 5])
+    U.deepStrictEqual(_.dropRight(0)([1, 2]), [1, 2])
+    U.deepStrictEqual(_.dropRight(1)([1, 2]), [1])
+    U.deepStrictEqual(_.dropRight(2)([1, 2]), [])
+    // out of bound
+    U.deepStrictEqual(_.dropRight(1)([]), [])
+    U.deepStrictEqual(_.dropRight(3)([1, 2]), [])
+    U.deepStrictEqual(_.dropRight(-1)([]), [])
+    U.deepStrictEqual(_.dropRight(-1)([1, 2]), [1, 2])
   })
 
   it('dropLeftWhile', () => {
@@ -580,12 +595,13 @@ describe('ReadonlyArray', () => {
   })
 
   it('unsafeUpdateAt', () => {
+    assert.strictEqual(_.unsafeUpdateAt(1, 2, []), _.empty)
     // should return the same reference if nothing changed
-    const x = { a: 1 }
-    const as: ReadonlyArray<{ readonly a: number }> = [x]
-    const result = _.unsafeUpdateAt(0, x, as)
-    U.deepStrictEqual(result, as)
-    U.deepStrictEqual(_.unsafeUpdateAt(0, 1, []), _.empty)
+    const input: ReadonlyArray<number> = [1, 2, 3]
+    U.deepStrictEqual(
+      pipe(_.unsafeUpdateAt(1, 2, input), (out) => out === input),
+      true
+    )
   })
 
   it('updateAt', () => {
@@ -601,10 +617,19 @@ describe('ReadonlyArray', () => {
   })
 
   it('modifyAt', () => {
-    const as: ReadonlyArray<number> = [1, 2, 3]
     const double = (x: number): number => x * 2
-    U.deepStrictEqual(_.modifyAt(1, double)(as), O.some([1, 4, 3]))
+    U.deepStrictEqual(_.modifyAt(1, double)([1, 2, 3]), O.some([1, 4, 3]))
     U.deepStrictEqual(_.modifyAt(1, double)([]), O.none)
+    // should return the same reference if nothing changed
+    const input: ReadonlyArray<number> = [1, 2, 3]
+    U.deepStrictEqual(
+      pipe(
+        input,
+        _.modifyAt(1, identity),
+        O.map((out) => out === input)
+      ),
+      O.some(true)
+    )
   })
 
   it('sort', () => {
@@ -907,12 +932,20 @@ describe('ReadonlyArray', () => {
   it('makeBy', () => {
     const double = (n: number): number => n * 2
     U.deepStrictEqual(_.makeBy(5, double), [0, 2, 4, 6, 8])
+    U.deepStrictEqual(_.makeBy(0, double), [])
+    U.deepStrictEqual(_.makeBy(-1, double), [])
   })
 
   it('range', () => {
     U.deepStrictEqual(_.range(0, 0), [0])
+    U.deepStrictEqual(_.range(0, 1), [0, 1])
     U.deepStrictEqual(_.range(1, 5), [1, 2, 3, 4, 5])
     U.deepStrictEqual(_.range(10, 15), [10, 11, 12, 13, 14, 15])
+    U.deepStrictEqual(_.range(-1, 0), [-1, 0])
+    U.deepStrictEqual(_.range(-5, -1), [-5, -4, -3, -2, -1])
+    // out of bound
+    U.deepStrictEqual(_.range(2, 1), [2])
+    U.deepStrictEqual(_.range(-1, -2), [-1])
   })
 
   it('replicate', () => {
diff --git a/test/ReadonlyMap.ts b/test/ReadonlyMap.ts
index 3e2897cd..35ae9e94 100644
--- a/test/ReadonlyMap.ts
+++ b/test/ReadonlyMap.ts
@@ -1109,6 +1109,16 @@ describe('ReadonlyMap', () => {
         new Map<User, number>([[{ id: 'a' }, 2]])
       )
     )
+    // should return the same reference if nothing changed
+    const input: ReadonlyMap<string, number> = new Map([['a', 1]])
+    U.deepStrictEqual(
+      pipe(
+        input,
+        _.modifyAt(S.Eq)('a', identity),
+        O.map((out) => out === input)
+      ),
+      O.some(true)
+    )
   })
 
   it('fromMap', () => {
diff --git a/test/ReadonlyNonEmptyArray.ts b/test/ReadonlyNonEmptyArray.ts
index 65e1f763..9a341ded 100644
--- a/test/ReadonlyNonEmptyArray.ts
+++ b/test/ReadonlyNonEmptyArray.ts
@@ -256,6 +256,16 @@ describe('ReadonlyNonEmptyArray', () => {
     const double = (n: number): number => n * 2
     U.deepStrictEqual(_.modifyAt(1, double)([1]), O.none)
     U.deepStrictEqual(_.modifyAt(1, double)([1, 2]), O.some([1, 4] as const))
+    // should return the same reference if nothing changed
+    const input: _.ReadonlyNonEmptyArray<number> = [1, 2, 3]
+    U.deepStrictEqual(
+      pipe(
+        input,
+        _.modifyAt(1, identity),
+        O.map((out) => out === input)
+      ),
+      O.some(true)
+    )
   })
 
   it('filter', () => {
@@ -274,14 +284,18 @@ describe('ReadonlyNonEmptyArray', () => {
     assert.deepStrictEqual(_.filter(({ x }) => x !== 10)([a1, a2, a3]), O.some([a1, a2, a3]))
 
     // refinements
+    // tslint:disable-next-line: deprecation
     const actual1 = _.filter(O.isSome)([O.some(3), O.some(2), O.some(1)])
     assert.deepStrictEqual(actual1, O.some([O.some(3), O.some(2), O.some(1)]))
+    // tslint:disable-next-line: deprecation
     const actual2 = _.filter(O.isSome)([O.some(3), O.none, O.some(1)])
     assert.deepStrictEqual(actual2, O.some([O.some(3), O.some(1)]))
   })
 
   it('filterWithIndex', () => {
+    // tslint:disable-next-line: deprecation
     assert.deepStrictEqual(_.filterWithIndex((i) => i % 2 === 0)([1, 2, 3]), O.some([1, 3]))
+    // tslint:disable-next-line: deprecation
     U.deepStrictEqual(_.filterWithIndex((i, a: number) => i % 2 === 1 && a > 2)([1, 2, 3]), O.none)
   })
 
diff --git a/test/ReadonlyRecord.ts b/test/ReadonlyRecord.ts
index c4cb8862..fce8841c 100644
--- a/test/ReadonlyRecord.ts
+++ b/test/ReadonlyRecord.ts
@@ -421,6 +421,16 @@ describe('ReadonlyRecord', () => {
     const x: _.ReadonlyRecord<string, number> = { a: 1 }
     U.deepStrictEqual(_.modifyAt('b', (n: number) => n * 2)(x), O.none)
     U.deepStrictEqual(_.modifyAt('a', (n: number) => n * 2)(x), O.some({ a: 2 }))
+    // should return the same reference if nothing changed
+    const input: _.ReadonlyRecord<string, number> = { a: 1 }
+    U.deepStrictEqual(
+      pipe(
+        input,
+        _.modifyAt('a', identity),
+        O.map((out) => out === input)
+      ),
+      O.some(true)
+    )
   })
 
   it('fromRecord', () => {
diff --git a/test/Record.ts b/test/Record.ts
index c57f6c6b..0d970566 100644
--- a/test/Record.ts
+++ b/test/Record.ts
@@ -415,5 +415,15 @@ describe('Record', () => {
     const x: Record<string, number> = { a: 1 }
     U.deepStrictEqual(_.modifyAt('b', (n: number) => n * 2)(x), O.none)
     U.deepStrictEqual(_.modifyAt('a', (n: number) => n * 2)(x), O.some({ a: 2 }))
+    // should not return the same reference if nothing changed
+    const input: Record<string, number> = { a: 1 }
+    U.deepStrictEqual(
+      pipe(
+        input,
+        _.modifyAt('a', identity),
+        O.map((out) => out === input)
+      ),
+      O.some(false)
+    )
   })
 })