Repository: gcanti/fp-ts
Pull request #1496 (2021-04-25 17:03:09)

Add flattenW (less strict version of flatten) to all types that also have chainW
Eg. for `Either`, allows:

```
function a(input: E.Either<E1, E.Either<E2, A>>): E.Either<E1 | E2, A> {
  return E.flattenW(input)
}
```

instead of:

```
function a(input: E.Either<E1, E.Either<E2, A>>): E.Either<E1 | E2, A> {
  return pipe(input, E.chainW(id => id))
}
```

i.e. allows the Left type to widen.

jussisaurio: Would probably make sense to add TaskEither.flattenW to the same PR, right?
gcanti: @jussisaurio anywhere there's a `chainW` I guess.

p.s.
I'll change the PR target to the `2.11` branch (which is dedicated to the next release)
gcanti: @jussisaurio nice, thank you! could you please update the changelog too?

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index d266251e..b5475d6c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -89,6 +89,7 @@ high state of flux, you're at risk of it changing without notice.
     - add `isBoolean`
   - `Either`
     - add `chainOptionK`
+    - add `flattenW`
   - `EitherT`
     - add `orElseFirst`
     - add `orLeft`
@@ -98,6 +99,7 @@ high state of flux, you're at risk of it changing without notice.
   - `IOEither`
     - add `orElseFirst` / `orElseFirstW`
     - add `orLeft`
+    - add `flattenW`
   - `Magma`
     - add `reverse`
     - add `filterFirst`
@@ -138,16 +140,19 @@ high state of flux, you're at risk of it changing without notice.
     - add `equals`
   - `Reader`
     - add `asksEW`, `asksW`
+    - add `flattenW`
   - `ReaderEither`
     - add `asksEW`, `asksW`
     - add `orElseFirst` / `orElseFirstW`
     - add `orLeft`
     - add `chainReaderKW`
     - add `chainFirstReaderK`, `chainFirstReaderKW`
+    - add `flattenW`
   - `ReaderTask`
     - add `asksEW`, `asksW`
     - add `chainReaderKW`
     - add `chainFirstReaderK`, `chainFirstReaderKW`
+    - add `flattenW`
   - `ReaderTaskEither`
     - add `asksEW`, `asksW`
     - add `orElseFirst` / `orElseFirstW`
@@ -160,6 +165,7 @@ high state of flux, you're at risk of it changing without notice.
     - add `chainFirstReaderTaskK`, `chainFirstReaderTaskKW`
     - add `chainReaderEitherK`, `chainReaderEitherKW`
     - add `chainFirstReaderEitherK`, `chainFirstReaderEitherKW`
+    - add `flattenW`
   - `ReadonlyArray`
     - add `prependW`, `appendW` (@thewilkybarkid)
     - add `filterE`
@@ -229,6 +235,7 @@ high state of flux, you're at risk of it changing without notice.
     - add `asksEW`, `asksW`
     - add `chainReaderKW`
     - add `chainFirstReaderK`, `chainFirstReaderKW`
+    - add `flattenW`
   - `string`
     - add `toUpperCase`
   - `struct`
@@ -239,6 +246,7 @@ high state of flux, you're at risk of it changing without notice.
     - add `chainTaskOptionK`
     - add `orElseFirst` / `orElseFirstW`
     - add `orLeft`
+    - add `flattenW`
   - `TaskOption`
     - add `fromTaskEither` (@thewilkybarkid)
   - `Witherable`
diff --git a/docs/modules/Either.ts.md b/docs/modules/Either.ts.md
index 273c4b37..35eee7cf 100644
--- a/docs/modules/Either.ts.md
+++ b/docs/modules/Either.ts.md
@@ -62,6 +62,7 @@ Added in v2.0.0
   - [filterOrElseW](#filterorelsew)
   - [flap](#flap)
   - [flatten](#flatten)
+  - [flattenW](#flattenw)
   - [fromOptionK](#fromoptionk)
   - [orElse](#orelse)
   - [orElseW](#orelsew)
@@ -620,6 +621,18 @@ assert.deepStrictEqual(E.flatten(E.left('e')), E.left('e'))
 
 Added in v2.0.0
 
+## flattenW
+
+Less strict version of [`flatten`](#flatten).
+
+**Signature**
+
+```ts
+export declare const flattenW: <E1, E2, A>(mma: Either<E1, Either<E2, A>>) => Either<E1 | E2, A>
+```
+
+Added in v2.11.0
+
 ## fromOptionK
 
 **Signature**
diff --git a/docs/modules/IOEither.ts.md b/docs/modules/IOEither.ts.md
index 52153536..3c3047ef 100644
--- a/docs/modules/IOEither.ts.md
+++ b/docs/modules/IOEither.ts.md
@@ -47,6 +47,7 @@ Added in v2.0.0
   - [filterOrElseW](#filterorelsew)
   - [flap](#flap)
   - [flatten](#flatten)
+  - [flattenW](#flattenw)
   - [fromEitherK](#fromeitherk)
   - [fromIOK](#fromiok)
   - [fromOptionK](#fromoptionk)
@@ -444,6 +445,18 @@ export declare const flatten: <E, A>(mma: IOEither<E, IOEither<E, A>>) => IOEith
 
 Added in v2.0.0
 
+## flattenW
+
+Less strict version of [`flatten`](#flatten).
+
+**Signature**
+
+```ts
+export declare const flattenW: <E1, E2, A>(mma: IOEither<E1, IOEither<E2, A>>) => IOEither<E1 | E2, A>
+```
+
+Added in v2.11.0
+
 ## fromEitherK
 
 **Signature**
diff --git a/docs/modules/Reader.ts.md b/docs/modules/Reader.ts.md
index d4ec2af1..ef75ec05 100644
--- a/docs/modules/Reader.ts.md
+++ b/docs/modules/Reader.ts.md
@@ -43,6 +43,7 @@ Added in v2.0.0
   - [chainFirstW](#chainfirstw)
   - [flap](#flap)
   - [flatten](#flatten)
+  - [flattenW](#flattenw)
   - [local](#local)
 - [constructors](#constructors)
   - [ask](#ask)
@@ -346,6 +347,18 @@ export declare const flatten: <R, A>(mma: Reader<R, Reader<R, A>>) => Reader<R,
 
 Added in v2.0.0
 
+## flattenW
+
+Less strict version of [`flatten`](#flatten).
+
+**Signature**
+
+```ts
+export declare const flattenW: <R1, R2, A>(mma: Reader<R1, Reader<R2, A>>) => Reader<R1 & R2, A>
+```
+
+Added in v2.11.0
+
 ## local
 
 Changes the value of the local context during the execution of the action `ma` (similar to `Contravariant`'s
diff --git a/docs/modules/ReaderEither.ts.md b/docs/modules/ReaderEither.ts.md
index 86fd864e..c15fd3b5 100644
--- a/docs/modules/ReaderEither.ts.md
+++ b/docs/modules/ReaderEither.ts.md
@@ -48,6 +48,7 @@ Added in v2.0.0
   - [filterOrElseW](#filterorelsew)
   - [flap](#flap)
   - [flatten](#flatten)
+  - [flattenW](#flattenw)
   - [fromEitherK](#fromeitherk)
   - [fromOptionK](#fromoptionk)
   - [fromReaderK](#fromreaderk)
@@ -518,6 +519,20 @@ export declare const flatten: <R, E, A>(mma: ReaderEither<R, E, ReaderEither<R,
 
 Added in v2.0.0
 
+## flattenW
+
+Less strict version of [`flatten`](#flatten).
+
+**Signature**
+
+```ts
+export declare const flattenW: <R1, R2, E1, E2, A>(
+  mma: ReaderEither<R1, E1, ReaderEither<R2, E2, A>>
+) => ReaderEither<R1 & R2, E1 | E2, A>
+```
+
+Added in v2.11.0
+
 ## fromEitherK
 
 **Signature**
diff --git a/docs/modules/ReaderTask.ts.md b/docs/modules/ReaderTask.ts.md
index 1c266bc4..ae5b6446 100644
--- a/docs/modules/ReaderTask.ts.md
+++ b/docs/modules/ReaderTask.ts.md
@@ -39,6 +39,7 @@ Added in v2.3.0
   - [chainTaskK](#chaintaskk)
   - [flap](#flap)
   - [flatten](#flatten)
+  - [flattenW](#flattenw)
   - [fromIOK](#fromiok)
   - [fromReaderK](#fromreaderk)
   - [fromTaskK](#fromtaskk)
@@ -372,6 +373,18 @@ export declare const flatten: <R, A>(mma: ReaderTask<R, ReaderTask<R, A>>) => Re
 
 Added in v2.3.0
 
+## flattenW
+
+Less strict version of [`flatten`](#flatten).
+
+**Signature**
+
+```ts
+export declare const flattenW: <R1, R2, A>(mma: ReaderTask<R1, ReaderTask<R2, A>>) => ReaderTask<R1 & R2, A>
+```
+
+Added in v2.11.0
+
 ## fromIOK
 
 **Signature**
diff --git a/docs/modules/ReaderTaskEither.ts.md b/docs/modules/ReaderTaskEither.ts.md
index b3185592..8cf34ffa 100644
--- a/docs/modules/ReaderTaskEither.ts.md
+++ b/docs/modules/ReaderTaskEither.ts.md
@@ -64,6 +64,7 @@ Added in v2.0.0
   - [filterOrElseW](#filterorelsew)
   - [flap](#flap)
   - [flatten](#flatten)
+  - [flattenW](#flattenw)
   - [fromEitherK](#fromeitherk)
   - [fromIOEitherK](#fromioeitherk)
   - [fromIOK](#fromiok)
@@ -773,6 +774,20 @@ export declare const flatten: <R, E, A>(
 
 Added in v2.0.0
 
+## flattenW
+
+Less strict version of [`flatten`](#flatten).
+
+**Signature**
+
+```ts
+export declare const flattenW: <R1, E1, R2, E2, A>(
+  mma: ReaderTaskEither<R1, E1, ReaderTaskEither<R2, E2, A>>
+) => ReaderTaskEither<R1 & R2, E1 | E2, A>
+```
+
+Added in v2.11.0
+
 ## fromEitherK
 
 **Signature**
diff --git a/docs/modules/StateReaderTaskEither.ts.md b/docs/modules/StateReaderTaskEither.ts.md
index 5d7f0292..2e25ef7f 100644
--- a/docs/modules/StateReaderTaskEither.ts.md
+++ b/docs/modules/StateReaderTaskEither.ts.md
@@ -59,6 +59,7 @@ Added in v2.0.0
   - [filterOrElseW](#filterorelsew)
   - [flap](#flap)
   - [flatten](#flatten)
+  - [flattenW](#flattenw)
   - [fromEitherK](#fromeitherk)
   - [fromIOEitherK](#fromioeitherk)
   - [fromIOK](#fromiok)
@@ -689,6 +690,20 @@ export declare const flatten: <S, R, E, A>(
 
 Added in v2.0.0
 
+## flattenW
+
+Less strict version of [`flatten`](#flatten).
+
+**Signature**
+
+```ts
+export declare const flattenW: <S, R1, E1, R2, E2, A>(
+  mma: StateReaderTaskEither<S, R1, E1, StateReaderTaskEither<S, R2, E2, A>>
+) => StateReaderTaskEither<S, R1 & R2, E1 | E2, A>
+```
+
+Added in v2.11.0
+
 ## fromEitherK
 
 **Signature**
diff --git a/docs/modules/TaskEither.ts.md b/docs/modules/TaskEither.ts.md
index 93384d6c..566ad14a 100644
--- a/docs/modules/TaskEither.ts.md
+++ b/docs/modules/TaskEither.ts.md
@@ -56,6 +56,7 @@ Added in v2.0.0
   - [filterOrElseW](#filterorelsew)
   - [flap](#flap)
   - [flatten](#flatten)
+  - [flattenW](#flattenw)
   - [fromEitherK](#fromeitherk)
   - [fromIOEitherK](#fromioeitherk)
   - [fromIOK](#fromiok)
@@ -563,6 +564,18 @@ export declare const flatten: <E, A>(mma: TaskEither<E, TaskEither<E, A>>) => Ta
 
 Added in v2.0.0
 
+## flattenW
+
+Less strict version of [`flatten`](#flatten).
+
+**Signature**
+
+```ts
+export declare const flattenW: <E1, E2, A>(mma: TaskEither<E1, TaskEither<E2, A>>) => TaskEither<E1 | E2, A>
+```
+
+Added in v2.11.0
+
 ## fromEitherK
 
 **Signature**
diff --git a/src/Either.ts b/src/Either.ts
index fdd5219c..cb1b703f 100644
--- a/src/Either.ts
+++ b/src/Either.ts
@@ -482,6 +482,16 @@ export const flatten: <E, A>(mma: Either<E, Either<E, A>>) => Either<E, A> =
   /*#__PURE__*/
   chain(identity)
 
+/**
+ * Less strict version of [`flatten`](#flatten).
+ *
+ * @category combinators
+ * @since 2.11.0
+ */
+export const flattenW: <E1, E2, A>(mma: Either<E1, Either<E2, A>>) => Either<E1 | E2, A> =
+  /*#__PURE__*/
+  chainW(identity)
+
 /**
  * Less strict version of [`alt`](#alt).
  *
diff --git a/src/IOEither.ts b/src/IOEither.ts
index 2886ea0c..b120bcd7 100644
--- a/src/IOEither.ts
+++ b/src/IOEither.ts
@@ -387,6 +387,16 @@ export const flatten: <E, A>(mma: IOEither<E, IOEither<E, A>>) => IOEither<E, A>
   /*#__PURE__*/
   chain(identity)
 
+/**
+ * Less strict version of [`flatten`](#flatten).
+ *
+ * @category combinators
+ * @since 2.11.0
+ */
+export const flattenW: <E1, E2, A>(mma: IOEither<E1, IOEither<E2, A>>) => IOEither<E1 | E2, A> =
+  /*#__PURE__*/
+  chainW(identity)
+
 /**
  * Identifies an associative operation on a type constructor. It is similar to `Semigroup`, except that it applies to
  * types of kind `* -> *`.
diff --git a/src/Reader.ts b/src/Reader.ts
index ea2c2a80..03aacf45 100644
--- a/src/Reader.ts
+++ b/src/Reader.ts
@@ -157,6 +157,16 @@ export const flatten: <R, A>(mma: Reader<R, Reader<R, A>>) => Reader<R, A> =
   /*#__PURE__*/
   chain(identity)
 
+/**
+ * Less strict version of [`flatten`](#flatten).
+ *
+ * @category combinators
+ * @since 2.11.0
+ */
+export const flattenW: <R1, R2, A>(mma: Reader<R1, Reader<R2, A>>) => Reader<R1 & R2, A> =
+  /*#__PURE__*/
+  chainW(identity)
+
 /**
  * @category Semigroupoid
  * @since 2.0.0
diff --git a/src/ReaderEither.ts b/src/ReaderEither.ts
index 59a1e721..2552528f 100644
--- a/src/ReaderEither.ts
+++ b/src/ReaderEither.ts
@@ -410,6 +410,18 @@ export const flatten: <R, E, A>(mma: ReaderEither<R, E, ReaderEither<R, E, A>>)
   /*#__PURE__*/
   chain(identity)
 
+/**
+ * Less strict version of [`flatten`](#flatten).
+ *
+ * @category combinators
+ * @since 2.11.0
+ */
+export const flattenW: <R1, R2, E1, E2, A>(
+  mma: ReaderEither<R1, E1, ReaderEither<R2, E2, A>>
+) => ReaderEither<R1 & R2, E1 | E2, A> =
+  /*#__PURE__*/
+  chainW(identity)
+
 /**
  * Identifies an associative operation on a type constructor. It is similar to `Semigroup`, except that it applies to
  * types of kind `* -> *`.
diff --git a/src/ReaderTask.ts b/src/ReaderTask.ts
index 8d0ca046..5d801ef2 100644
--- a/src/ReaderTask.ts
+++ b/src/ReaderTask.ts
@@ -192,6 +192,16 @@ export const flatten: <R, A>(mma: ReaderTask<R, ReaderTask<R, A>>) => ReaderTask
   /*#__PURE__*/
   chain(identity)
 
+/**
+ * Less strict version of [`flatten`](#flatten).
+ *
+ * @category combinators
+ * @since 2.11.0
+ */
+export const flattenW: <R1, R2, A>(mma: ReaderTask<R1, ReaderTask<R2, A>>) => ReaderTask<R1 & R2, A> =
+  /*#__PURE__*/
+  chainW(identity)
+
 // -------------------------------------------------------------------------------------
 // instances
 // -------------------------------------------------------------------------------------
diff --git a/src/ReaderTaskEither.ts b/src/ReaderTaskEither.ts
index 2a86e492..d403a289 100644
--- a/src/ReaderTaskEither.ts
+++ b/src/ReaderTaskEither.ts
@@ -617,6 +617,18 @@ export const flatten: <R, E, A>(mma: ReaderTaskEither<R, E, ReaderTaskEither<R,
   /*#__PURE__*/
   chain(identity)
 
+/**
+ * Less strict version of [`flatten`](#flatten).
+ *
+ * @category combinators
+ * @since 2.11.0
+ */
+export const flattenW: <R1, E1, R2, E2, A>(
+  mma: ReaderTaskEither<R1, E1, ReaderTaskEither<R2, E2, A>>
+) => ReaderTaskEither<R1 & R2, E1 | E2, A> =
+  /*#__PURE__*/
+  chainW(identity)
+
 /**
  * Identifies an associative operation on a type constructor. It is similar to `Semigroup`, except that it applies to
  * types of kind `* -> *`.
diff --git a/src/StateReaderTaskEither.ts b/src/StateReaderTaskEither.ts
index 2b58be5d..3976470a 100644
--- a/src/StateReaderTaskEither.ts
+++ b/src/StateReaderTaskEither.ts
@@ -478,6 +478,18 @@ export const flatten: <S, R, E, A>(
   /*#__PURE__*/
   chain(identity)
 
+/**
+ * Less strict version of [`flatten`](#flatten).
+ *
+ * @category combinators
+ * @since 2.11.0
+ */
+export const flattenW: <S, R1, E1, R2, E2, A>(
+  mma: StateReaderTaskEither<S, R1, E1, StateReaderTaskEither<S, R2, E2, A>>
+) => StateReaderTaskEither<S, R1 & R2, E1 | E2, A> =
+  /*#__PURE__*/
+  chainW(identity)
+
 /**
  * Less strict version of [`alt`](#alt).
  *
diff --git a/src/TaskEither.ts b/src/TaskEither.ts
index ce183bf5..910c421a 100644
--- a/src/TaskEither.ts
+++ b/src/TaskEither.ts
@@ -503,6 +503,16 @@ export const flatten: <E, A>(mma: TaskEither<E, TaskEither<E, A>>) => TaskEither
   /*#__PURE__*/
   chain(identity)
 
+/**
+ * Less strict version of [`flatten`](#flatten).
+ *
+ * @category combinators
+ * @since 2.11.0
+ */
+export const flattenW: <E1, E2, A>(mma: TaskEither<E1, TaskEither<E2, A>>) => TaskEither<E1 | E2, A> =
+  /*#__PURE__*/
+  chainW(identity)
+
 /**
  * Identifies an associative operation on a type constructor. It is similar to `Semigroup`, except that it applies to
  * types of kind `* -> *`.


Test patch:
diff --git a/test/Either.ts b/test/Either.ts
index 215ddab2..08f51bb9 100644
--- a/test/Either.ts
+++ b/test/Either.ts
@@ -114,6 +114,10 @@ describe('Either', () => {
       U.deepStrictEqual(pipe(_.right(_.right('a')), _.flatten), _.right('a'))
     })
 
+    it('flattenW', () => {
+      U.deepStrictEqual(pipe(_.right<'left1', _.Either<'left2', 'a'>>(_.right('a')), _.flattenW), _.right('a'))
+    })
+
     it('bimap', () => {
       const f = (s: string): number => s.length
       const g = (n: number): boolean => n > 2
diff --git a/test/IOEither.ts b/test/IOEither.ts
index 0211c4ca..c84f54e4 100644
--- a/test/IOEither.ts
+++ b/test/IOEither.ts
@@ -117,6 +117,10 @@ describe('IOEither', () => {
       U.deepStrictEqual(pipe(_.right(_.right('a')), _.flatten)(), E.right('a'))
     })
 
+    it('flattenW', () => {
+      U.deepStrictEqual(pipe(_.right<'left1', _.IOEither<'left2', 'a'>>(_.right('a')), _.flattenW)(), E.right('a'))
+    })
+
     it('bimap', () => {
       const f = (s: string): number => s.length
       const g = (n: number): boolean => n > 2
diff --git a/test/Reader.ts b/test/Reader.ts
index 948ef8b2..c7e6eb0c 100644
--- a/test/Reader.ts
+++ b/test/Reader.ts
@@ -41,10 +41,17 @@ describe('Reader', () => {
       U.deepStrictEqual(pipe(_.of<object, string>('foo'), _.chainFirstW(f))({}), 'foo')
     })
 
-    it('chain', () => {
+    it('flatten', () => {
       U.deepStrictEqual(pipe(_.of(_.of('a')), _.flatten)({}), 'a')
     })
 
+    type R1 = { readonly env1: unknown }
+    type R2 = { readonly env2: unknown }
+
+    it('flattenW', () => {
+      U.deepStrictEqual(pipe(_.of<R1, _.Reader<R2, 'a'>>(_.of('a')), _.flattenW)({ env1: '', env2: '' }), 'a')
+    })
+
     it('compose', () => {
       U.deepStrictEqual(pipe(U.double, _.compose(S.size))('aaa'), 6)
     })
diff --git a/test/ReaderEither.ts b/test/ReaderEither.ts
index 8e1311f9..1dde92a5 100644
--- a/test/ReaderEither.ts
+++ b/test/ReaderEither.ts
@@ -58,6 +58,18 @@ describe('ReaderEither', () => {
       U.deepStrictEqual(pipe(_.right(_.right('a')), _.flatten)({}), E.right('a'))
     })
 
+    type R1 = { readonly env1: unknown }
+    type R2 = { readonly env2: unknown }
+    type E1 = { readonly left1: unknown }
+    type E2 = { readonly left2: unknown }
+
+    it('flattenW', () => {
+      U.deepStrictEqual(
+        pipe(_.right<R1, E1, _.ReaderEither<R2, E2, 'a'>>(_.right('a')), _.flattenW)({ env1: '', env2: '' }),
+        E.right('a')
+      )
+    })
+
     it('mapLeft', () => {
       U.deepStrictEqual(pipe(_.right(1), _.mapLeft(S.size))({}), E.right(1))
       U.deepStrictEqual(pipe(_.left('aa'), _.mapLeft(S.size))({}), E.left(2))
diff --git a/test/ReaderTask.ts b/test/ReaderTask.ts
index 0248ebb1..eec36e75 100644
--- a/test/ReaderTask.ts
+++ b/test/ReaderTask.ts
@@ -50,6 +50,13 @@ describe('ReaderTask', () => {
     U.deepStrictEqual(await pipe(_.of(_.of('a')), _.flatten)({})(), 'a')
   })
 
+  type R1 = { readonly env1: unknown }
+  type R2 = { readonly env2: unknown }
+
+  it('flattenW', async () => {
+    U.deepStrictEqual(await pipe(_.of<R1, _.ReaderTask<R2, 'a'>>(_.of('a')), _.flattenW)({ env1: '', env2: '' })(), 'a')
+  })
+
   it('of', async () => {
     U.deepStrictEqual(await _.fromReader(R.of(1))({})(), 1)
   })
diff --git a/test/ReaderTaskEither.ts b/test/ReaderTaskEither.ts
index 14b21d61..31b3ee0c 100644
--- a/test/ReaderTaskEither.ts
+++ b/test/ReaderTaskEither.ts
@@ -53,6 +53,21 @@ describe('ReaderTaskEither', () => {
       U.deepStrictEqual(await pipe(_.right(_.right('a')), _.flatten)({})(), E.right('a'))
     })
 
+    type R1 = { readonly env1: unknown }
+    type R2 = { readonly env2: unknown }
+    type E1 = { readonly left1: unknown }
+    type E2 = { readonly left2: unknown }
+
+    it('flattenW', async () => {
+      U.deepStrictEqual(
+        await pipe(
+          _.right<R1, E1, _.ReaderTaskEither<R2, E2, 'a'>>(_.right('a')),
+          _.flattenW
+        )({ env1: '', env2: '' })(),
+        E.right('a')
+      )
+    })
+
     it('bimap', async () => {
       const f = (s: string): number => s.length
       const g = (n: number): boolean => n > 2
diff --git a/test/StateReaderTaskEither.ts b/test/StateReaderTaskEither.ts
index 4febdaf9..8017e3f0 100644
--- a/test/StateReaderTaskEither.ts
+++ b/test/StateReaderTaskEither.ts
@@ -76,11 +76,26 @@ describe('StateReaderTaskEither', () => {
       U.deepStrictEqual(e, E.right('aaa'))
     })
 
-    it('chainFirst', async () => {
+    it('flatten', async () => {
       const e = await pipe(_.right(_.right('a')), _.flatten, _.evaluate(state))({})()
       U.deepStrictEqual(e, E.right('a'))
     })
 
+    type S = unknown
+    type R1 = { readonly env1: unknown }
+    type R2 = { readonly env2: unknown }
+    type E1 = { readonly left1: unknown }
+    type E2 = { readonly left2: unknown }
+
+    it('flattenW', async () => {
+      const e = await pipe(
+        _.right<S, R1, E1, _.StateReaderTaskEither<S, R2, E2, 'a'>>(_.right('a')),
+        _.flattenW,
+        _.evaluate(state)
+      )({ env1: '', env2: '' })()
+      U.deepStrictEqual(e, E.right('a'))
+    })
+
     it('bimap', async () => {
       const gt2 = (n: number): boolean => n > 2
       const e1 = await pipe(_.right('aaa'), _.bimap(gt2, S.size), _.evaluate(state))({})()
diff --git a/test/TaskEither.ts b/test/TaskEither.ts
index ef024e71..f56cd19f 100644
--- a/test/TaskEither.ts
+++ b/test/TaskEither.ts
@@ -87,6 +87,13 @@ describe('TaskEither', () => {
     U.deepStrictEqual(await pipe(_.right(_.right('a')), _.flatten)(), E.right('a'))
   })
 
+  it('flattenW', async () => {
+    U.deepStrictEqual(
+      await pipe(_.right<'left1', _.TaskEither<'left2', 'a'>>(_.right('a')), _.flattenW)(),
+      E.right('a')
+    )
+  })
+
   it('bimap', async () => {
     const f = (s: string): number => s.length
     const g = (n: number): boolean => n > 2