Repository: gcanti/fp-ts
Pull request #1436 (2021-03-22 09:40:46)

Add appendW, prependW and concatW to arrays
Adds `appendW` and `prependW` to `Array` and `ReadonlyArray`, and `concatW` to `NonEmptyArray` and `ReadonlyNonEmptyArray`.


Patch:
diff --git a/docs/modules/Array.ts.md b/docs/modules/Array.ts.md
index c3eed388..0ec5b1cc 100644
--- a/docs/modules/Array.ts.md
+++ b/docs/modules/Array.ts.md
@@ -98,8 +98,10 @@ Added in v2.0.0
   - [~~prependToAll~~](#prependtoall)
 - [constructors](#constructors)
   - [append](#append)
+  - [appendW](#appendw)
   - [makeBy](#makeby)
   - [prepend](#prepend)
+  - [prependW](#prependw)
   - [range](#range)
   - [replicate](#replicate)
   - [~~cons~~](#cons)
@@ -1340,6 +1342,18 @@ assert.deepStrictEqual(pipe([1, 2, 3], append(4)), [1, 2, 3, 4])
 
 Added in v2.10.0
 
+## appendW
+
+Less strict version of [`append`](#append).
+
+**Signature**
+
+```ts
+export declare const appendW: <A, B>(end: B) => (init: A[]) => NEA.NonEmptyArray<A | B>
+```
+
+Added in v2.11.0
+
 ## makeBy
 
 Return a `Array` of length `n` with element `i` initialized with `f(i)`.
@@ -1384,6 +1398,18 @@ assert.deepStrictEqual(pipe([2, 3, 4], prepend(1)), [1, 2, 3, 4])
 
 Added in v2.10.0
 
+## prependW
+
+Less strict version of [`prepend`](#prepend).
+
+**Signature**
+
+```ts
+export declare const prependW: <A, B>(head: B) => (tail: A[]) => NEA.NonEmptyArray<A | B>
+```
+
+Added in v2.11.0
+
 ## range
 
 Create an `Array` containing a range of integers, including both endpoints.
diff --git a/docs/modules/ReadonlyArray.ts.md b/docs/modules/ReadonlyArray.ts.md
index c078b092..d4b67277 100644
--- a/docs/modules/ReadonlyArray.ts.md
+++ b/docs/modules/ReadonlyArray.ts.md
@@ -96,8 +96,10 @@ Added in v2.5.0
   - [~~prependToAll~~](#prependtoall)
 - [constructors](#constructors)
   - [append](#append)
+  - [appendW](#appendw)
   - [makeBy](#makeby)
   - [prepend](#prepend)
+  - [prependW](#prependw)
   - [range](#range)
   - [replicate](#replicate)
   - [~~cons~~](#cons)
@@ -1350,6 +1352,18 @@ assert.deepStrictEqual(pipe([1, 2, 3], append(4)), [1, 2, 3, 4])
 
 Added in v2.10.0
 
+## appendW
+
+Less strict version of [`append`](#append).
+
+**Signature**
+
+```ts
+export declare const appendW: <B>(end: B) => <A>(init: readonly A[]) => RNEA.ReadonlyNonEmptyArray<B | A>
+```
+
+Added in v2.11.0
+
 ## makeBy
 
 Return a `ReadonlyArray` of length `n` with element `i` initialized with `f(i)`.
@@ -1394,6 +1408,18 @@ assert.deepStrictEqual(pipe([2, 3, 4], prepend(1)), [1, 2, 3, 4])
 
 Added in v2.10.0
 
+## prependW
+
+Less strict version of [`prepend`](#prepend).
+
+**Signature**
+
+```ts
+export declare const prependW: <B>(head: B) => <A>(tail: readonly A[]) => RNEA.ReadonlyNonEmptyArray<B | A>
+```
+
+Added in v2.11.0
+
 ## range
 
 Create a `ReadonlyArray` containing a range of integers, including both endpoints.
diff --git a/docs/modules/TaskEither.ts.md b/docs/modules/TaskEither.ts.md
index 91a9346b..12c09c51 100644
--- a/docs/modules/TaskEither.ts.md
+++ b/docs/modules/TaskEither.ts.md
@@ -70,6 +70,7 @@ Added in v2.0.0
   - [fromOption](#fromoption)
   - [fromPredicate](#frompredicate)
   - [fromTask](#fromtask)
+  - [fromTaskOption](#fromtaskoption)
   - [left](#left)
   - [leftIO](#leftio)
   - [leftTask](#lefttask)
@@ -716,6 +717,16 @@ export declare const fromTask: <E, A>(fa: T.Task<A>) => TaskEither<E, A>
 
 Added in v2.7.0
 
+## fromTaskOption
+
+**Signature**
+
+```ts
+export declare const fromTaskOption: <E>(onNone: Lazy<E>) => <A>(e: TaskOption<A>) => TaskEither<E, A>
+```
+
+Added in v2.11.0
+
 ## left
 
 **Signature**
diff --git a/docs/modules/TaskOption.ts.md b/docs/modules/TaskOption.ts.md
index fad6f6b6..8d23d8b6 100644
--- a/docs/modules/TaskOption.ts.md
+++ b/docs/modules/TaskOption.ts.md
@@ -53,6 +53,7 @@ Added in v2.10.0
   - [fromOption](#fromoption)
   - [fromPredicate](#frompredicate)
   - [fromTask](#fromtask)
+  - [fromTaskEither](#fromtaskeither)
   - [none](#none)
   - [some](#some)
 - [destructors](#destructors)
@@ -463,6 +464,16 @@ export declare const fromTask: <A>(fa: T.Task<A>) => TaskOption<A>
 
 Added in v2.10.0
 
+## fromTaskEither
+
+**Signature**
+
+```ts
+export declare const fromTaskEither: <A>(e: TaskEither<unknown, A>) => TaskOption<A>
+```
+
+Added in v2.11.0
+
 ## none
 
 **Signature**
diff --git a/dtslint/ts3.5/Array.ts b/dtslint/ts3.5/Array.ts
index 8d448c98..c2cba40f 100644
--- a/dtslint/ts3.5/Array.ts
+++ b/dtslint/ts3.5/Array.ts
@@ -9,6 +9,16 @@ declare const ns: Array<number>
 declare const ss: Array<string>
 declare const tns: Array<[number, string]>
 
+// prepend
+
+pipe(ss, _.prepend('a')) // $ExpectType NonEmptyArray<string>
+pipe(ss, _.prependW(1)) // $ExpectType NonEmptyArray<string | number>
+
+// append
+
+pipe(ss, _.append('a')) // $ExpectType NonEmptyArray<string>
+pipe(ss, _.appendW(1)) // $ExpectType NonEmptyArray<string | number>
+
 //
 // zip
 //
diff --git a/dtslint/ts3.5/ReadonlyArray.ts b/dtslint/ts3.5/ReadonlyArray.ts
index 7d4c1ffc..45b5edde 100644
--- a/dtslint/ts3.5/ReadonlyArray.ts
+++ b/dtslint/ts3.5/ReadonlyArray.ts
@@ -9,6 +9,16 @@ declare const rns: ReadonlyArray<number>
 declare const rss: ReadonlyArray<string>
 declare const rtns: ReadonlyArray<readonly [number, string]>
 
+// prepend
+
+pipe(rss, _.prepend('a')) // $ExpectType ReadonlyNonEmptyArray<string>
+pipe(rss, _.prependW(1)) // $ExpectType ReadonlyNonEmptyArray<string | number>
+
+// append
+
+pipe(rss, _.append('a')) // $ExpectType ReadonlyNonEmptyArray<string>
+pipe(rss, _.appendW(1)) // $ExpectType ReadonlyNonEmptyArray<string | number>
+
 //
 // zip
 //
diff --git a/dtslint/ts3.5/TaskEither.ts b/dtslint/ts3.5/TaskEither.ts
index 5a08e02a..b1678307 100644
--- a/dtslint/ts3.5/TaskEither.ts
+++ b/dtslint/ts3.5/TaskEither.ts
@@ -1,6 +1,7 @@
 import * as _ from '../../src/TaskEither'
 import * as T from '../../src/Task'
 import * as E from '../../src/Either'
+import * as TO from '../../src/TaskOption'
 import * as IOE from '../../src/IOEither'
 import { pipe } from '../../src/function'
 
@@ -44,6 +45,16 @@ pipe(
   _.chainIOEitherKW(() => IOE.right<number, number>(1))
 )
 
+//
+// fromTaskOption
+//
+
+// $ExpectType TaskEither<string, number>
+pipe(
+  TO.some(1),
+  _.fromTaskOption(() => 'a')
+)
+
 //
 // taskify
 //
diff --git a/dtslint/ts3.5/TaskOption.ts b/dtslint/ts3.5/TaskOption.ts
new file mode 100644
index 00000000..7026f1ba
--- /dev/null
+++ b/dtslint/ts3.5/TaskOption.ts
@@ -0,0 +1,11 @@
+import * as _ from '../../src/TaskOption'
+import * as TE from '../../src/TaskEither'
+import { pipe } from '../../src/function'
+
+declare const tesn: TE.TaskEither<string, number>
+
+//
+// fromTaskEither
+//
+
+pipe(tesn, _.fromTaskEither) // $ExpectType TaskOption<number>
diff --git a/src/Array.ts b/src/Array.ts
index cd714335..f95d490a 100644
--- a/src/Array.ts
+++ b/src/Array.ts
@@ -54,6 +54,14 @@ import Option = O.Option
  */
 export const prepend: <A>(head: A) => (tail: Array<A>) => NEA.NonEmptyArray<A> = NEA.prepend
 
+/**
+ * Less strict version of [`prepend`](#prepend).
+ *
+ * @category constructors
+ * @since 2.11.0
+ */
+export const prependW: <A, B>(head: B) => (tail: Array<A>) => NEA.NonEmptyArray<A | B> = NEA.prependW
+
 /**
  * Append an element to the end of a `Array`, creating a new `NonEmptyArray`.
  *
@@ -68,6 +76,14 @@ 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
 
+/**
+ * Less strict version of [`append`](#append).
+ *
+ * @category constructors
+ * @since 2.11.0
+ */
+export const appendW: <A, B>(end: B) => (init: Array<A>) => NEA.NonEmptyArray<A | B> = NEA.appendW
+
 /**
  * Return a `Array` of length `n` with element `i` initialized with `f(i)`.
  *
diff --git a/src/NonEmptyArray.ts b/src/NonEmptyArray.ts
index 46557882..e35c59a2 100644
--- a/src/NonEmptyArray.ts
+++ b/src/NonEmptyArray.ts
@@ -69,12 +69,22 @@ export const isOutOfBound = <A>(i: number, as: Array<A>): boolean => i < 0 || i
 /**
  * @internal
  */
-export const prepend = <A>(head: A) => (tail: Array<A>): NonEmptyArray<A> => [head, ...tail]
+export const prependW = <B>(head: B) => <A>(tail: Array<A>): NonEmptyArray<A | B> => [head, ...tail]
 
 /**
  * @internal
  */
-export const append = <A>(end: A) => (init: Array<A>): NonEmptyArray<A> => concat(init, [end])
+export const prepend: <A>(head: A) => (tail: Array<A>) => NonEmptyArray<A> = prependW
+
+/**
+ * @internal
+ */
+export const appendW = <B>(end: B) => <A>(init: Array<A>): NonEmptyArray<A | B> => [...init, end] as any
+
+/**
+ * @internal
+ */
+export const append: <A>(end: A) => (init: Array<A>) => NonEmptyArray<A> = appendW
 
 /**
  * @internal
diff --git a/src/ReadonlyArray.ts b/src/ReadonlyArray.ts
index ed4eb954..14955c19 100644
--- a/src/ReadonlyArray.ts
+++ b/src/ReadonlyArray.ts
@@ -55,6 +55,14 @@ import ReadonlyNonEmptyArray = RNEA.ReadonlyNonEmptyArray
  */
 export const prepend = RNEA.prepend
 
+/**
+ * Less strict version of [`prepend`](#prepend).
+ *
+ * @category constructors
+ * @since 2.11.0
+ */
+export const prependW = RNEA.prependW
+
 /**
  * Append an element to the end of a `ReadonlyArray`, creating a new `ReadonlyNonEmptyArray`.
  *
@@ -69,6 +77,14 @@ export const prepend = RNEA.prepend
  */
 export const append = RNEA.append
 
+/**
+ * Less strict version of [`append`](#append).
+ *
+ * @category constructors
+ * @since 2.11.0
+ */
+export const appendW = RNEA.appendW
+
 /**
  * Return a `ReadonlyArray` of length `n` with element `i` initialized with `f(i)`.
  *
diff --git a/src/ReadonlyNonEmptyArray.ts b/src/ReadonlyNonEmptyArray.ts
index 5c0877fa..e8159ba5 100644
--- a/src/ReadonlyNonEmptyArray.ts
+++ b/src/ReadonlyNonEmptyArray.ts
@@ -73,12 +73,22 @@ export const isOutOfBound = <A>(i: number, as: ReadonlyArray<A>): boolean => i <
 /**
  * @internal
  */
-export const prepend = <A>(head: A) => (tail: ReadonlyArray<A>): ReadonlyNonEmptyArray<A> => [head, ...tail]
+export const prependW = <B>(head: B) => <A>(tail: ReadonlyArray<A>): ReadonlyNonEmptyArray<A | B> => [head, ...tail]
 
 /**
  * @internal
  */
-export const append = <A>(end: A) => (init: ReadonlyArray<A>): ReadonlyNonEmptyArray<A> => concat(init, [end])
+export const prepend: <A>(head: A) => (tail: ReadonlyArray<A>) => ReadonlyNonEmptyArray<A> = prependW
+
+/**
+ * @internal
+ */
+export const appendW = <B>(end: B) => <A>(init: ReadonlyArray<A>): ReadonlyNonEmptyArray<A | B> => [...init, end] as any
+
+/**
+ * @internal
+ */
+export const append: <A>(end: A) => (init: ReadonlyArray<A>) => ReadonlyNonEmptyArray<A> = appendW
 
 /**
  * @internal
diff --git a/src/TaskEither.ts b/src/TaskEither.ts
index f8c642fb..77fc2aea 100644
--- a/src/TaskEither.ts
+++ b/src/TaskEither.ts
@@ -60,6 +60,7 @@ import { Monoid } from './Monoid'
 import { Pointed2 } from './Pointed'
 import { Semigroup } from './Semigroup'
 import * as T from './Task'
+import { TaskOption } from './TaskOption'
 
 // -------------------------------------------------------------------------------------
 // model
@@ -828,6 +829,13 @@ export const chainOptionK =
   /*#__PURE__*/
   chainOptionK_(FromEither, Chain)
 
+/**
+ * @category constructors
+ * @since 2.11.0
+ */
+export const fromTaskOption: <E>(onNone: Lazy<E>) => <A>(e: TaskOption<A>) => TaskEither<E, A> = (onNone) =>
+  T.map(E.fromOption(onNone))
+
 /**
  * @category combinators
  * @since 2.4.0
diff --git a/src/TaskOption.ts b/src/TaskOption.ts
index a57ba65e..ef065068 100644
--- a/src/TaskOption.ts
+++ b/src/TaskOption.ts
@@ -32,6 +32,7 @@ import * as OT from './OptionT'
 import { Pointed1 } from './Pointed'
 import { Separated } from './Separated'
 import * as T from './Task'
+import { TaskEither } from './TaskEither'
 
 import Task = T.Task
 import Option = O.Option
@@ -97,6 +98,14 @@ export const fromTask: FromTask1<URI>['fromTask'] =
   /*#__PURE__*/
   OT.fromF(T.Functor)
 
+/**
+ * @category constructors
+ * @since 2.11.0
+ */
+export const fromTaskEither: <A>(e: TaskEither<unknown, A>) => TaskOption<A> =
+  /*#__PURE__*/
+  T.map(O.fromEither)
+
 // -------------------------------------------------------------------------------------
 // destructors
 // -------------------------------------------------------------------------------------


Test patch:
diff --git a/test/Array.ts b/test/Array.ts
index dc4f8abf..1f5a74f0 100644
--- a/test/Array.ts
+++ b/test/Array.ts
@@ -973,6 +973,16 @@ describe('Array', () => {
     })
   })
 
+  it('prepend', () => {
+    U.deepStrictEqual(pipe(['a', 'b'], _.prepend('c')), ['c', 'a', 'b'])
+    U.deepStrictEqual(pipe(['a', 'b'], _.prependW(3)), [3, 'a', 'b'])
+  })
+
+  it('append', () => {
+    U.deepStrictEqual(pipe(['a', 'b'], _.append('c')), ['a', 'b', 'c'])
+    U.deepStrictEqual(pipe(['a', 'b'], _.appendW(3)), ['a', 'b', 3])
+  })
+
   it('makeBy', () => {
     U.deepStrictEqual(_.makeBy(5, U.double), [0, 2, 4, 6, 8])
     U.deepStrictEqual(_.makeBy(0, U.double), [])
diff --git a/test/ReadonlyArray.ts b/test/ReadonlyArray.ts
index a96be591..b2ee1feb 100644
--- a/test/ReadonlyArray.ts
+++ b/test/ReadonlyArray.ts
@@ -1040,6 +1040,16 @@ describe('ReadonlyArray', () => {
     })
   })
 
+  it('prepend', () => {
+    U.deepStrictEqual(pipe(['a', 'b'], _.prepend('c')), ['c', 'a', 'b'])
+    U.deepStrictEqual(pipe(['a', 'b'], _.prependW(3)), [3, 'a', 'b'])
+  })
+
+  it('append', () => {
+    U.deepStrictEqual(pipe(['a', 'b'], _.append('c')), ['a', 'b', 'c'])
+    U.deepStrictEqual(pipe(['a', 'b'], _.appendW(3)), ['a', 'b', 3])
+  })
+
   it('makeBy', () => {
     U.deepStrictEqual(_.makeBy(5, U.double), [0, 2, 4, 6, 8])
     U.strictEqual(_.makeBy(0, U.double), _.empty)
diff --git a/test/TaskEither.ts b/test/TaskEither.ts
index 316eeb4d..171aea14 100644
--- a/test/TaskEither.ts
+++ b/test/TaskEither.ts
@@ -10,6 +10,7 @@ import { none, some } from '../src/Option'
 import { pipeable } from '../src/pipeable'
 import * as N from '../src/number'
 import * as T from '../src/Task'
+import * as TO from '../src/TaskOption'
 import * as _ from '../src/TaskEither'
 import * as S from '../src/string'
 import { left, right } from '../src/Separated'
@@ -471,6 +472,23 @@ describe('TaskEither', () => {
     )
   })
 
+  it('fromTaskOption', async () => {
+    U.deepStrictEqual(
+      await pipe(
+        TO.none,
+        _.fromTaskOption(() => 'none')
+      )(),
+      E.left('none')
+    )
+    U.deepStrictEqual(
+      await pipe(
+        TO.some(1),
+        _.fromTaskOption(() => 'none')
+      )(),
+      E.right(1)
+    )
+  })
+
   it('fromPredicate', async () => {
     const gt2 = _.fromPredicate(
       (n: number) => n >= 2,
diff --git a/test/TaskOption.ts b/test/TaskOption.ts
index 43ef5201..7736f3d4 100644
--- a/test/TaskOption.ts
+++ b/test/TaskOption.ts
@@ -2,6 +2,7 @@ import * as U from './util'
 import { pipe } from '../src/function'
 import * as O from '../src/Option'
 import * as T from '../src/Task'
+import * as TE from '../src/TaskEither'
 import * as _ from '../src/TaskOption'
 
 describe('TaskOption', () => {
@@ -118,6 +119,15 @@ describe('TaskOption', () => {
     U.deepStrictEqual(await f(3)(), O.some(3))
   })
 
+  it('fromTaskEither', async () => {
+    const pl = TE.left('a')
+    const pr = TE.right('a')
+    const fl = _.fromTaskEither(pl)
+    const fr = _.fromTaskEither(pr)
+    U.deepStrictEqual(await fl(), O.none)
+    U.deepStrictEqual(await fr(), O.some('a'))
+  })
+
   // -------------------------------------------------------------------------------------
   // destructors
   // -------------------------------------------------------------------------------------