Repository: gcanti/fp-ts
Pull request #1463 (2021-04-01 10:52:44)

Backport EitherT orElseFirst and orLeft
Refs https://github.com/gcanti/fp-ts/pull/1445#issuecomment-808323185

thewilkybarkid: Haven't committed doc changes as saw failures on `E.fromPredicate` examples, which is unrelated...
thewilkybarkid: Just noticed the `2.11` doesn't have builds, https://github.com/gcanti/fp-ts/commit/6898b03722cddd8ed9c12469059d0f3007b8d7a4 introduced broken examples. /cc @gcanti 
gcanti: @thewilkybarkid thanks, fixed
thewilkybarkid: Covered the 4 categories that have it in `3.0.0` (`Either` itself and `StateReaderTaskEither` don't).

Patch:
diff --git a/docs/modules/EitherT.ts.md b/docs/modules/EitherT.ts.md
index f64dd008..e1fcfcf1 100644
--- a/docs/modules/EitherT.ts.md
+++ b/docs/modules/EitherT.ts.md
@@ -30,6 +30,8 @@ Added in v2.0.0
   - [match](#match)
   - [matchE](#matche)
   - [orElse](#orelse)
+  - [orElseFirst](#orelsefirst)
+  - [orLeft](#orleft)
   - [right](#right)
   - [rightF](#rightf)
   - [swap](#swap)
@@ -477,6 +479,74 @@ export declare function orElse<M>(
 
 Added in v2.10.0
 
+## orElseFirst
+
+**Signature**
+
+```ts
+export declare function orElseFirst<M extends URIS3>(
+  M: Monad3<M>
+): <E, R, ME, B>(
+  onLeft: (e: E) => Kind3<M, R, ME, Either<E, B>>
+) => <A>(ma: Kind3<M, R, ME, Either<E, A>>) => Kind3<M, R, ME, Either<E, A>>
+export declare function orElseFirst<M extends URIS3, ME>(
+  M: Monad3C<M, ME>
+): <E, R, B>(
+  onLeft: (e: E) => Kind3<M, R, ME, Either<E, B>>
+) => <A>(ma: Kind3<M, R, ME, Either<E, A>>) => Kind3<M, R, ME, Either<E, A>>
+export declare function orElseFirst<M extends URIS2>(
+  M: Monad2<M>
+): <E, ME, B>(
+  onLeft: (e: E) => Kind2<M, ME, Either<E, B>>
+) => <A>(ma: Kind2<M, ME, Either<E, A>>) => Kind2<M, ME, Either<E, A>>
+export declare function orElseFirst<M extends URIS2, ME>(
+  M: Monad2C<M, ME>
+): <E, B>(
+  onLeft: (e: E) => Kind2<M, ME, Either<E, B>>
+) => <A>(ma: Kind2<M, ME, Either<E, A>>) => Kind2<M, ME, Either<E, A>>
+export declare function orElseFirst<M extends URIS>(
+  M: Monad1<M>
+): <E, B>(onLeft: (e: E) => Kind<M, Either<E, B>>) => <A>(ma: Kind<M, Either<E, A>>) => Kind<M, Either<E, A>>
+export declare function orElseFirst<M>(
+  M: Monad<M>
+): <E, B>(onLeft: (e: E) => HKT<M, Either<E, B>>) => <A>(ma: HKT<M, Either<E, A>>) => HKT<M, Either<E, A>>
+```
+
+Added in v2.11.0
+
+## orLeft
+
+**Signature**
+
+```ts
+export declare function orLeft<M extends URIS3>(
+  M: Monad3<M>
+): <E1, R, ME, E2>(
+  onLeft: (e: E1) => Kind3<M, R, ME, E2>
+) => <A>(fa: Kind3<M, R, ME, Either<E1, A>>) => Kind3<M, R, ME, Either<E2, A>>
+export declare function orLeft<M extends URIS3, ME>(
+  M: Monad3C<M, ME>
+): <E1, R, E2>(
+  onLeft: (e: E1) => Kind3<M, R, ME, E2>
+) => <A>(fa: Kind3<M, R, ME, Either<E1, A>>) => Kind3<M, R, ME, Either<E2, A>>
+export declare function orLeft<M extends URIS2>(
+  M: Monad2<M>
+): <E1, ME, E2>(
+  onLeft: (e: E1) => Kind2<M, ME, E2>
+) => <A>(fa: Kind2<M, ME, Either<E1, A>>) => Kind2<M, ME, Either<E2, A>>
+export declare function orLeft<M extends URIS2, ME>(
+  M: Monad2C<M, ME>
+): <E1, E2>(onLeft: (e: E1) => Kind2<M, ME, E2>) => <A>(fa: Kind2<M, ME, Either<E1, A>>) => Kind2<M, ME, Either<E2, A>>
+export declare function orLeft<M extends URIS>(
+  M: Monad1<M>
+): <E1, E2>(onLeft: (e: E1) => Kind<M, E2>) => <A>(fa: Kind<M, Either<E1, A>>) => Kind<M, Either<E2, A>>
+export declare function orLeft<M>(
+  M: Monad<M>
+): <E1, E2>(onLeft: (e: E1) => HKT<M, E2>) => <A>(fa: HKT<M, Either<E1, A>>) => HKT<M, Either<E2, A>>
+```
+
+Added in v2.11.0
+
 ## right
 
 **Signature**
diff --git a/docs/modules/IOEither.ts.md b/docs/modules/IOEither.ts.md
index 792fcfe9..527f8751 100644
--- a/docs/modules/IOEither.ts.md
+++ b/docs/modules/IOEither.ts.md
@@ -51,7 +51,10 @@ Added in v2.0.0
   - [fromIOK](#fromiok)
   - [fromOptionK](#fromoptionk)
   - [orElse](#orelse)
+  - [orElseFirst](#orelsefirst)
+  - [orElseFirstW](#orelsefirstw)
   - [orElseW](#orelsew)
+  - [orLeft](#orleft)
   - [swap](#swap)
 - [constructors](#constructors)
   - [fromEither](#fromeither)
@@ -483,6 +486,28 @@ export declare const orElse: <E1, A, E2>(onLeft: (e: E1) => IOEither<E2, A>) =>
 
 Added in v2.0.0
 
+## orElseFirst
+
+**Signature**
+
+```ts
+export declare const orElseFirst: <E, B>(onLeft: (e: E) => IOEither<E, B>) => <A>(ma: IOEither<E, A>) => IOEither<E, A>
+```
+
+Added in v2.11.0
+
+## orElseFirstW
+
+**Signature**
+
+```ts
+export declare const orElseFirstW: <E1, E2, B>(
+  onLeft: (e: E1) => IOEither<E2, B>
+) => <A>(ma: IOEither<E1, A>) => IOEither<E1 | E2, A>
+```
+
+Added in v2.11.0
+
 ## orElseW
 
 Less strict version of [`orElse`](#orElse).
@@ -497,6 +522,16 @@ export declare const orElseW: <E1, E2, B>(
 
 Added in v2.10.0
 
+## orLeft
+
+**Signature**
+
+```ts
+export declare const orLeft: <E1, E2>(onLeft: (e: E1) => I.IO<E2>) => <A>(fa: IOEither<E1, A>) => IOEither<E2, A>
+```
+
+Added in v2.11.0
+
 ## swap
 
 **Signature**
diff --git a/docs/modules/ReaderEither.ts.md b/docs/modules/ReaderEither.ts.md
index 4c52e8c8..3e611a4f 100644
--- a/docs/modules/ReaderEither.ts.md
+++ b/docs/modules/ReaderEither.ts.md
@@ -47,7 +47,10 @@ Added in v2.0.0
   - [fromOptionK](#fromoptionk)
   - [fromReaderK](#fromreaderk)
   - [orElse](#orelse)
+  - [orElseFirst](#orelsefirst)
+  - [orElseFirstW](#orelsefirstw)
   - [orElseW](#orelsew)
+  - [orLeft](#orleft)
   - [swap](#swap)
   - [~~local~~](#local)
 - [constructors](#constructors)
@@ -488,6 +491,30 @@ export declare const orElse: <E1, R, E2, A>(
 
 Added in v2.0.0
 
+## orElseFirst
+
+**Signature**
+
+```ts
+export declare const orElseFirst: <E, R, B>(
+  onLeft: (e: E) => ReaderEither<R, E, B>
+) => <A>(ma: ReaderEither<R, E, A>) => ReaderEither<R, E, A>
+```
+
+Added in v2.11.0
+
+## orElseFirstW
+
+**Signature**
+
+```ts
+export declare const orElseFirstW: <E1, R, E2, B>(
+  onLeft: (e: E1) => ReaderEither<R, E2, B>
+) => <A>(ma: ReaderEither<R, E1, A>) => ReaderEither<R, E1 | E2, A>
+```
+
+Added in v2.11.0
+
 ## orElseW
 
 Less strict version of [`orElse`](#orElse).
@@ -502,6 +529,18 @@ export declare const orElseW: <E1, R1, E2, B>(
 
 Added in v2.10.0
 
+## orLeft
+
+**Signature**
+
+```ts
+export declare const orLeft: <E1, R, E2>(
+  onLeft: (e: E1) => R.Reader<R, E2>
+) => <A>(fa: ReaderEither<R, E1, A>) => ReaderEither<R, E2, A>
+```
+
+Added in v2.11.0
+
 ## swap
 
 **Signature**
diff --git a/docs/modules/ReaderTaskEither.ts.md b/docs/modules/ReaderTaskEither.ts.md
index 9e22c2eb..73f8ed73 100644
--- a/docs/modules/ReaderTaskEither.ts.md
+++ b/docs/modules/ReaderTaskEither.ts.md
@@ -59,7 +59,10 @@ Added in v2.0.0
   - [fromTaskEitherK](#fromtaskeitherk)
   - [fromTaskK](#fromtaskk)
   - [orElse](#orelse)
+  - [orElseFirst](#orelsefirst)
+  - [orElseFirstW](#orelsefirstw)
   - [orElseW](#orelsew)
+  - [orLeft](#orleft)
   - [swap](#swap)
   - [~~local~~](#local)
 - [constructors](#constructors)
@@ -673,6 +676,30 @@ export declare const orElse: <R, E1, A, E2>(
 
 Added in v2.0.0
 
+## orElseFirst
+
+**Signature**
+
+```ts
+export declare const orElseFirst: <E, R, B>(
+  onLeft: (e: E) => ReaderTaskEither<R, E, B>
+) => <A>(ma: ReaderTaskEither<R, E, A>) => ReaderTaskEither<R, E, A>
+```
+
+Added in v2.11.0
+
+## orElseFirstW
+
+**Signature**
+
+```ts
+export declare const orElseFirstW: <E1, R, E2, B>(
+  onLeft: (e: E1) => ReaderTaskEither<R, E2, B>
+) => <A>(ma: ReaderTaskEither<R, E1, A>) => ReaderTaskEither<R, E1 | E2, A>
+```
+
+Added in v2.11.0
+
 ## orElseW
 
 Less strict version of [`orElse`](#orElse).
@@ -687,6 +714,18 @@ export declare const orElseW: <E1, R1, E2, B>(
 
 Added in v2.10.0
 
+## orLeft
+
+**Signature**
+
+```ts
+export declare const orLeft: <E1, R, E2>(
+  onLeft: (e: E1) => RT.ReaderTask<R, E2>
+) => <A>(fa: ReaderTaskEither<R, E1, A>) => ReaderTaskEither<R, E2, A>
+```
+
+Added in v2.11.0
+
 ## swap
 
 **Signature**
diff --git a/docs/modules/TaskEither.ts.md b/docs/modules/TaskEither.ts.md
index c91b3f84..a3242780 100644
--- a/docs/modules/TaskEither.ts.md
+++ b/docs/modules/TaskEither.ts.md
@@ -61,7 +61,10 @@ Added in v2.0.0
   - [fromOptionK](#fromoptionk)
   - [fromTaskK](#fromtaskk)
   - [orElse](#orelse)
+  - [orElseFirst](#orelsefirst)
+  - [orElseFirstW](#orelsefirstw)
   - [orElseW](#orelsew)
+  - [orLeft](#orleft)
   - [swap](#swap)
 - [constructors](#constructors)
   - [fromEither](#fromeither)
@@ -630,6 +633,30 @@ test()
 
 Added in v2.0.0
 
+## orElseFirst
+
+**Signature**
+
+```ts
+export declare const orElseFirst: <E, B>(
+  onLeft: (e: E) => TaskEither<E, B>
+) => <A>(ma: TaskEither<E, A>) => TaskEither<E, A>
+```
+
+Added in v2.11.0
+
+## orElseFirstW
+
+**Signature**
+
+```ts
+export declare const orElseFirstW: <E1, E2, B>(
+  onLeft: (e: E1) => TaskEither<E2, B>
+) => <A>(ma: TaskEither<E1, A>) => TaskEither<E1 | E2, A>
+```
+
+Added in v2.11.0
+
 ## orElseW
 
 Less strict version of [`orElse`](#orElse).
@@ -644,6 +671,16 @@ export declare const orElseW: <E1, E2, B>(
 
 Added in v2.10.0
 
+## orLeft
+
+**Signature**
+
+```ts
+export declare const orLeft: <E1, E2>(onLeft: (e: E1) => T.Task<E2>) => <A>(fa: TaskEither<E1, A>) => TaskEither<E2, A>
+```
+
+Added in v2.11.0
+
 ## swap
 
 **Signature**
diff --git a/dtslint/ts3.5/IOEither.ts b/dtslint/ts3.5/IOEither.ts
index 88825ebf..8831b526 100644
--- a/dtslint/ts3.5/IOEither.ts
+++ b/dtslint/ts3.5/IOEither.ts
@@ -13,6 +13,56 @@ pipe(
   _.getOrElseW(() => IO.of(null))
 )
 
+//
+// orElse
+//
+
+// $ExpectType IOEither<number, never>
+pipe(
+  _.left('a'),
+  _.orElse((a) => _.left(a.length))
+)
+
+//
+// orElseW
+//
+
+// $ExpectType IOEither<never, string | number>
+pipe(
+  _.left<string, string>('a'),
+  _.orElseW((a) => _.right(a.length))
+)
+
+//
+// orElseFirst
+//
+
+// $ExpectType IOEither<string, never>
+pipe(
+  _.left('a'),
+  _.orElseFirst((a) => _.right(a.length))
+)
+
+//
+// orElseFirstW
+//
+
+// $ExpectType IOEither<string | number, never>
+pipe(
+  _.left('a'),
+  _.orElseFirstW((a) => _.left(a.length))
+)
+
+//
+// orLeft
+//
+
+// $ExpectType IOEither<number, never>
+pipe(
+  _.left('a'),
+  _.orLeft((a) => IO.of(a.length))
+)
+
 //
 // chainW
 //
diff --git a/dtslint/ts3.5/ReaderEither.ts b/dtslint/ts3.5/ReaderEither.ts
index 88760a70..c00e7a01 100644
--- a/dtslint/ts3.5/ReaderEither.ts
+++ b/dtslint/ts3.5/ReaderEither.ts
@@ -13,6 +13,56 @@ pipe(
   _.getOrElseW(() => R.of<{ b: number }, null>(null))
 )
 
+//
+// orElse
+//
+
+// $ExpectType ReaderEither<{ a: string; }, number, never>
+pipe(
+  _.left<{ a: string }, string, never>('a'),
+  _.orElse((a) => _.left(a.length))
+)
+
+//
+// orElseW
+//
+
+// $ExpectType ReaderEither<{ b: number; } & { a: string; }, never, string | number>
+pipe(
+  _.left<{ a: string }, string, string>('a'),
+  _.orElseW((a) => _.right<{ b: number }, never, string | number>(a.length))
+)
+
+//
+// orElseFirst
+//
+
+// $ExpectType ReaderEither<{ a: string; }, string, never>
+pipe(
+  _.left<{ a: string }, string, never>('a'),
+  _.orElseFirst((a) => _.right(a.length))
+)
+
+//
+// orElseFirstW
+//
+
+// $ExpectType ReaderEither<{ a: string; }, string | number, never>
+pipe(
+  _.left<{ a: string }, string, never>('a'),
+  _.orElseFirstW((a) => _.left(a.length))
+)
+
+//
+// orLeft
+//
+
+// $ExpectType ReaderEither<{ a: string; }, number, never>
+pipe(
+  _.left<{ a: string }, string, never>('a'),
+  _.orLeft((a) => R.of(a.length))
+)
+
 //
 // chainW
 //
diff --git a/dtslint/ts3.5/ReaderTaskEither.ts b/dtslint/ts3.5/ReaderTaskEither.ts
index 17877cd2..3d47ab58 100644
--- a/dtslint/ts3.5/ReaderTaskEither.ts
+++ b/dtslint/ts3.5/ReaderTaskEither.ts
@@ -15,6 +15,56 @@ pipe(
   _.getOrElseW(() => RT.of<{ b: number }, null>(null))
 )
 
+//
+// orElse
+//
+
+// $ExpectType ReaderTaskEither<{ a: string; }, number, never>
+pipe(
+  _.left<{ a: string }, string, never>('a'),
+  _.orElse((a) => _.left(a.length))
+)
+
+//
+// orElseW
+//
+
+// $ExpectType ReaderTaskEither<{ b: number; } & { a: string; }, never, string | number>
+pipe(
+  _.left<{ a: string }, string, string>('a'),
+  _.orElseW((a) => _.right<{ b: number }, never, string | number>(a.length))
+)
+
+//
+// orElseFirst
+//
+
+// $ExpectType ReaderTaskEither<{ a: string; }, string, never>
+pipe(
+  _.left<{ a: string }, string, never>('a'),
+  _.orElseFirst((a) => _.right(a.length))
+)
+
+//
+// orElseFirstW
+//
+
+// $ExpectType ReaderTaskEither<{ a: string; }, string | number, never>
+pipe(
+  _.left<{ a: string }, string, never>('a'),
+  _.orElseFirstW((a) => _.left(a.length))
+)
+
+//
+// orLeft
+//
+
+// $ExpectType ReaderTaskEither<{ a: string; }, number, never>
+pipe(
+  _.left<{ a: string }, string, never>('a'),
+  _.orLeft((a) => RT.of(a.length))
+)
+
 //
 // chainW
 //
diff --git a/dtslint/ts3.5/TaskEither.ts b/dtslint/ts3.5/TaskEither.ts
index b1678307..84d37534 100644
--- a/dtslint/ts3.5/TaskEither.ts
+++ b/dtslint/ts3.5/TaskEither.ts
@@ -15,6 +15,56 @@ pipe(
   _.getOrElseW(() => T.of(null))
 )
 
+//
+// orElse
+//
+
+// $ExpectType TaskEither<number, never>
+pipe(
+  _.left('a'),
+  _.orElse((a) => _.left(a.length))
+)
+
+//
+// orElseW
+//
+
+// $ExpectType TaskEither<never, string | number>
+pipe(
+  _.left<string, string>('a'),
+  _.orElseW((a) => _.right(a.length))
+)
+
+//
+// orElseFirst
+//
+
+// $ExpectType TaskEither<string, never>
+pipe(
+  _.left('a'),
+  _.orElseFirst((a) => _.right(a.length))
+)
+
+//
+// orElseFirstW
+//
+
+// $ExpectType TaskEither<string | number, never>
+pipe(
+  _.left('a'),
+  _.orElseFirstW((a) => _.left(a.length))
+)
+
+//
+// orLeft
+//
+
+// $ExpectType TaskEither<number, never>
+pipe(
+  _.left('a'),
+  _.orLeft((a) => T.of(a.length))
+)
+
 //
 // chainW
 //
diff --git a/src/EitherT.ts b/src/EitherT.ts
index 72b82a6c..ee50350e 100644
--- a/src/EitherT.ts
+++ b/src/EitherT.ts
@@ -466,6 +466,82 @@ export function orElse<M>(
   return (onLeft) => (ma) => M.chain(ma, (e) => (E.isLeft(e) ? onLeft(e.left) : M.of(e)))
 }
 
+/**
+ * @since 2.11.0
+ */
+export function orElseFirst<M extends URIS3>(
+  M: Monad3<M>
+): <E, R, ME, B>(
+  onLeft: (e: E) => Kind3<M, R, ME, Either<E, B>>
+) => <A>(ma: Kind3<M, R, ME, Either<E, A>>) => Kind3<M, R, ME, Either<E, A>>
+export function orElseFirst<M extends URIS3, ME>(
+  M: Monad3C<M, ME>
+): <E, R, B>(
+  onLeft: (e: E) => Kind3<M, R, ME, Either<E, B>>
+) => <A>(ma: Kind3<M, R, ME, Either<E, A>>) => Kind3<M, R, ME, Either<E, A>>
+export function orElseFirst<M extends URIS2>(
+  M: Monad2<M>
+): <E, ME, B>(
+  onLeft: (e: E) => Kind2<M, ME, Either<E, B>>
+) => <A>(ma: Kind2<M, ME, Either<E, A>>) => Kind2<M, ME, Either<E, A>>
+export function orElseFirst<M extends URIS2, ME>(
+  M: Monad2C<M, ME>
+): <E, B>(
+  onLeft: (e: E) => Kind2<M, ME, Either<E, B>>
+) => <A>(ma: Kind2<M, ME, Either<E, A>>) => Kind2<M, ME, Either<E, A>>
+export function orElseFirst<M extends URIS>(
+  M: Monad1<M>
+): <E, B>(onLeft: (e: E) => Kind<M, Either<E, B>>) => <A>(ma: Kind<M, Either<E, A>>) => Kind<M, Either<E, A>>
+export function orElseFirst<M>(
+  M: Monad<M>
+): <E, B>(onLeft: (e: E) => HKT<M, Either<E, B>>) => <A>(ma: HKT<M, Either<E, A>>) => HKT<M, Either<E, A>>
+export function orElseFirst<M>(
+  M: Monad<M>
+): <E, B>(onLeft: (e: E) => HKT<M, Either<E, B>>) => <A>(ma: HKT<M, Either<E, A>>) => HKT<M, Either<E, A>> {
+  const orElseM = orElse(M)
+  return (onLeft) => orElseM((e) => M.map(onLeft(e), (eb) => (E.isLeft(eb) ? eb : E.left(e))))
+}
+
+/**
+ * @since 2.11.0
+ */
+export function orLeft<M extends URIS3>(
+  M: Monad3<M>
+): <E1, R, ME, E2>(
+  onLeft: (e: E1) => Kind3<M, R, ME, E2>
+) => <A>(fa: Kind3<M, R, ME, Either<E1, A>>) => Kind3<M, R, ME, Either<E2, A>>
+export function orLeft<M extends URIS3, ME>(
+  M: Monad3C<M, ME>
+): <E1, R, E2>(
+  onLeft: (e: E1) => Kind3<M, R, ME, E2>
+) => <A>(fa: Kind3<M, R, ME, Either<E1, A>>) => Kind3<M, R, ME, Either<E2, A>>
+export function orLeft<M extends URIS2>(
+  M: Monad2<M>
+): <E1, ME, E2>(
+  onLeft: (e: E1) => Kind2<M, ME, E2>
+) => <A>(fa: Kind2<M, ME, Either<E1, A>>) => Kind2<M, ME, Either<E2, A>>
+export function orLeft<M extends URIS2, ME>(
+  M: Monad2C<M, ME>
+): <E1, E2>(onLeft: (e: E1) => Kind2<M, ME, E2>) => <A>(fa: Kind2<M, ME, Either<E1, A>>) => Kind2<M, ME, Either<E2, A>>
+export function orLeft<M extends URIS>(
+  M: Monad1<M>
+): <E1, E2>(onLeft: (e: E1) => Kind<M, E2>) => <A>(fa: Kind<M, Either<E1, A>>) => Kind<M, Either<E2, A>>
+export function orLeft<M>(
+  M: Monad<M>
+): <E1, E2>(onLeft: (e: E1) => HKT<M, E2>) => <A>(fa: HKT<M, Either<E1, A>>) => HKT<M, Either<E2, A>>
+export function orLeft<M>(
+  M: Monad<M>
+): <E1, E2>(onLeft: (e: E1) => HKT<M, E2>) => <A>(fa: HKT<M, Either<E1, A>>) => HKT<M, Either<E2, A>> {
+  return (onLeft) => (ma) =>
+    M.chain(
+      ma,
+      E.match(
+        (e) => M.map(onLeft(e), E.left),
+        (a) => M.of(E.right(a))
+      )
+    )
+}
+
 // -------------------------------------------------------------------------------------
 // utils
 // -------------------------------------------------------------------------------------
diff --git a/src/IOEither.ts b/src/IOEither.ts
index 7d6c4a76..f0a6b80a 100644
--- a/src/IOEither.ts
+++ b/src/IOEither.ts
@@ -241,6 +241,30 @@ export const orElseW: <E1, E2, B>(
   onLeft: (e: E1) => IOEither<E2, B>
 ) => <A>(ma: IOEither<E1, A>) => IOEither<E2, A | B> = orElse as any
 
+/**
+ * @category combinators
+ * @since 2.11.0
+ */
+export const orElseFirst: <E, B>(onLeft: (e: E) => IOEither<E, B>) => <A>(ma: IOEither<E, A>) => IOEither<E, A> =
+  /*#__PURE__*/
+  ET.orElseFirst(I.Monad)
+
+/**
+ * @category combinators
+ * @since 2.11.0
+ */
+export const orElseFirstW: <E1, E2, B>(
+  onLeft: (e: E1) => IOEither<E2, B>
+) => <A>(ma: IOEither<E1, A>) => IOEither<E1 | E2, A> = orElseFirst as any
+
+/**
+ * @category combinators
+ * @since 2.11.0
+ */
+export const orLeft: <E1, E2>(onLeft: (e: E1) => IO<E2>) => <A>(fa: IOEither<E1, A>) => IOEither<E2, A> =
+  /*#__PURE__*/
+  ET.orLeft(I.Monad)
+
 /**
  * @category combinators
  * @since 2.0.0
diff --git a/src/ReaderEither.ts b/src/ReaderEither.ts
index 6d88cd23..2f17074d 100644
--- a/src/ReaderEither.ts
+++ b/src/ReaderEither.ts
@@ -231,6 +231,34 @@ export const orElseW: <E1, R1, E2, B>(
   onLeft: (e: E1) => ReaderEither<R1, E2, B>
 ) => <R2, A>(ma: ReaderEither<R2, E1, A>) => ReaderEither<R1 & R2, E2, A | B> = orElse as any
 
+/**
+ * @category combinators
+ * @since 2.11.0
+ */
+export const orElseFirst: <E, R, B>(
+  onLeft: (e: E) => ReaderEither<R, E, B>
+) => <A>(ma: ReaderEither<R, E, A>) => ReaderEither<R, E, A> =
+  /*#__PURE__*/
+  ET.orElseFirst(R.Monad)
+
+/**
+ * @category combinators
+ * @since 2.11.0
+ */
+export const orElseFirstW: <E1, R, E2, B>(
+  onLeft: (e: E1) => ReaderEither<R, E2, B>
+) => <A>(ma: ReaderEither<R, E1, A>) => ReaderEither<R, E1 | E2, A> = orElseFirst as any
+
+/**
+ * @category combinators
+ * @since 2.11.0
+ */
+export const orLeft: <E1, R, E2>(
+  onLeft: (e: E1) => Reader<R, E2>
+) => <A>(fa: ReaderEither<R, E1, A>) => ReaderEither<R, E2, A> =
+  /*#__PURE__*/
+  ET.orLeft(R.Monad)
+
 /**
  * @category combinators
  * @since 2.0.0
diff --git a/src/ReaderTaskEither.ts b/src/ReaderTaskEither.ts
index 5dadee89..24a73590 100644
--- a/src/ReaderTaskEither.ts
+++ b/src/ReaderTaskEither.ts
@@ -335,6 +335,34 @@ export const orElseW: <E1, R1, E2, B>(
   onLeft: (e: E1) => ReaderTaskEither<R1, E2, B>
 ) => <R2, A>(ma: ReaderTaskEither<R2, E1, A>) => ReaderTaskEither<R1 & R2, E2, A | B> = orElse as any
 
+/**
+ * @category combinators
+ * @since 2.11.0
+ */
+export const orElseFirst: <E, R, B>(
+  onLeft: (e: E) => ReaderTaskEither<R, E, B>
+) => <A>(ma: ReaderTaskEither<R, E, A>) => ReaderTaskEither<R, E, A> =
+  /*#__PURE__*/
+  ET.orElseFirst(RT.Monad)
+
+/**
+ * @category combinators
+ * @since 2.11.0
+ */
+export const orElseFirstW: <E1, R, E2, B>(
+  onLeft: (e: E1) => ReaderTaskEither<R, E2, B>
+) => <A>(ma: ReaderTaskEither<R, E1, A>) => ReaderTaskEither<R, E1 | E2, A> = orElseFirst as any
+
+/**
+ * @category combinators
+ * @since 2.11.0
+ */
+export const orLeft: <E1, R, E2>(
+  onLeft: (e: E1) => ReaderTask<R, E2>
+) => <A>(fa: ReaderTaskEither<R, E1, A>) => ReaderTaskEither<R, E2, A> =
+  /*#__PURE__*/
+  ET.orLeft(RT.Monad)
+
 /**
  * @category combinators
  * @since 2.0.0
diff --git a/src/TaskEither.ts b/src/TaskEither.ts
index 2d30d64d..27276bf5 100644
--- a/src/TaskEither.ts
+++ b/src/TaskEither.ts
@@ -319,6 +319,30 @@ export const orElseW: <E1, E2, B>(
   onLeft: (e: E1) => TaskEither<E2, B>
 ) => <A>(ma: TaskEither<E1, A>) => TaskEither<E2, A | B> = orElse as any
 
+/**
+ * @category combinators
+ * @since 2.11.0
+ */
+export const orElseFirst: <E, B>(onLeft: (e: E) => TaskEither<E, B>) => <A>(ma: TaskEither<E, A>) => TaskEither<E, A> =
+  /*#__PURE__*/
+  ET.orElseFirst(T.Monad)
+
+/**
+ * @category combinators
+ * @since 2.11.0
+ */
+export const orElseFirstW: <E1, E2, B>(
+  onLeft: (e: E1) => TaskEither<E2, B>
+) => <A>(ma: TaskEither<E1, A>) => TaskEither<E1 | E2, A> = orElseFirst as any
+
+/**
+ * @category combinators
+ * @since 2.11.0
+ */
+export const orLeft: <E1, E2>(onLeft: (e: E1) => Task<E2>) => <A>(fa: TaskEither<E1, A>) => TaskEither<E2, A> =
+  /*#__PURE__*/
+  ET.orLeft(T.Monad)
+
 /**
  * @category combinators
  * @since 2.0.0


Test patch:
diff --git a/test/IOEither.ts b/test/IOEither.ts
index 2ab221ad..0211c4ca 100644
--- a/test/IOEither.ts
+++ b/test/IOEither.ts
@@ -251,6 +251,30 @@ describe('IOEither', () => {
     U.deepStrictEqual(_.orElse(() => _.right(2))(_.right(1))(), E.right(1))
   })
 
+  it('orElseW', () => {
+    U.deepStrictEqual(_.orElseW(() => _.right(2))(_.right(1))(), E.right(1))
+  })
+
+  it('orElseFirst', () => {
+    const f = _.orElseFirst((e: string) => (e.length <= 1 ? _.right(true) : _.left(e + '!')))
+    U.deepStrictEqual(pipe(_.right(1), f)(), E.right(1))
+    U.deepStrictEqual(pipe(_.left('a'), f)(), E.left('a'))
+    U.deepStrictEqual(pipe(_.left('aa'), f)(), E.left('aa!'))
+  })
+
+  it('orElseFirstW', () => {
+    const f = _.orElseFirstW((e: string) => (e.length <= 1 ? _.right(true) : _.left(e + '!')))
+    U.deepStrictEqual(pipe(_.right(1), f)(), E.right(1))
+    U.deepStrictEqual(pipe(_.left('a'), f)(), E.left('a'))
+    U.deepStrictEqual(pipe(_.left('aa'), f)(), E.left('aa!'))
+  })
+
+  it('orLeft', () => {
+    const f = _.orLeft((e: string) => I.of(e + '!'))
+    U.deepStrictEqual(pipe(_.right(1), f)(), E.right(1))
+    U.deepStrictEqual(pipe(_.left('a'), f)(), E.left('a!'))
+  })
+
   it('tryCatch', () => {
     U.deepStrictEqual(_.tryCatch(() => 1, E.toError)(), E.right(1))
     U.deepStrictEqual(
diff --git a/test/ReaderEither.ts b/test/ReaderEither.ts
index edb0853b..fe882ba6 100644
--- a/test/ReaderEither.ts
+++ b/test/ReaderEither.ts
@@ -134,6 +134,31 @@ describe('ReaderEither', () => {
     U.deepStrictEqual(orElse(_.right(1))({}), E.right(1))
   })
 
+  it('orElseW', () => {
+    const orElse = _.orElseW((s: string) => (s.length > 2 ? _.right(1) : _.left(2)))
+    U.deepStrictEqual(orElse(_.right(1))({}), E.right(1))
+  })
+
+  it('orElseFirst', () => {
+    const f = _.orElseFirst((s: string) => (s.length <= 1 ? _.right(true) : _.left(s + '!')))
+    U.deepStrictEqual(pipe(_.right(1), f)({}), E.right(1))
+    U.deepStrictEqual(pipe(_.left('a'), f)({}), E.left('a'))
+    U.deepStrictEqual(pipe(_.left('aa'), f)({}), E.left('aa!'))
+  })
+
+  it('orElseFirstW', () => {
+    const f = _.orElseFirstW((s: string) => (s.length <= 1 ? _.right(true) : _.left(s + '!')))
+    U.deepStrictEqual(pipe(_.right(1), f)({}), E.right(1))
+    U.deepStrictEqual(pipe(_.left('a'), f)({}), E.left('a'))
+    U.deepStrictEqual(pipe(_.left('aa'), f)({}), E.left('aa!'))
+  })
+
+  it('orLeft', () => {
+    const f = _.orLeft((s: string) => R.of(s + '!'))
+    U.deepStrictEqual(pipe(_.right(1), f)({}), E.right(1))
+    U.deepStrictEqual(pipe(_.left('a'), f)({}), E.left('a!'))
+  })
+
   describe('getSemigroup', () => {
     it('concat', () => {
       // tslint:disable-next-line: deprecation
diff --git a/test/ReaderTaskEither.ts b/test/ReaderTaskEither.ts
index 1267bf98..b99ea133 100644
--- a/test/ReaderTaskEither.ts
+++ b/test/ReaderTaskEither.ts
@@ -250,6 +250,43 @@ describe('ReaderTaskEither', () => {
     )
   })
 
+  it('orElseW', async () => {
+    U.deepStrictEqual(
+      await pipe(
+        _.right(1),
+        _.orElseW((s: string) => _.right(s.length))
+      )({})(),
+      E.right(1)
+    )
+    U.deepStrictEqual(
+      await pipe(
+        _.left('error'),
+        _.orElseW((s) => _.right(s.length))
+      )({})(),
+      E.right(5)
+    )
+  })
+
+  it('orElseFirst', async () => {
+    const f = _.orElseFirst((s: string) => (s.length <= 1 ? _.right(true) : _.left(s + '!')))
+    U.deepStrictEqual(await pipe(_.right(1), f)({})(), E.right(1))
+    U.deepStrictEqual(await pipe(_.left('a'), f)({})(), E.left('a'))
+    U.deepStrictEqual(await pipe(_.left('aa'), f)({})(), E.left('aa!'))
+  })
+
+  it('orElseFirstW', async () => {
+    const f = _.orElseFirstW((s: string) => (s.length <= 1 ? _.right(true) : _.left(s + '!')))
+    U.deepStrictEqual(await pipe(_.right(1), f)({})(), E.right(1))
+    U.deepStrictEqual(await pipe(_.left('a'), f)({})(), E.left('a'))
+    U.deepStrictEqual(await pipe(_.left('aa'), f)({})(), E.left('aa!'))
+  })
+
+  it('orLeft', async () => {
+    const f = _.orLeft((s: string) => RT.of(s + '!'))
+    U.deepStrictEqual(await pipe(_.right(1), f)({})(), E.right(1))
+    U.deepStrictEqual(await pipe(_.left('a'), f)({})(), E.left('a!'))
+  })
+
   describe('MonadIO', () => {
     it('fromIO', async () => {
       U.deepStrictEqual(await _.fromIO(() => 1)({})(), E.right(1))
diff --git a/test/TaskEither.ts b/test/TaskEither.ts
index 9a619f7a..73079aeb 100644
--- a/test/TaskEither.ts
+++ b/test/TaskEither.ts
@@ -392,6 +392,43 @@ describe('TaskEither', () => {
     )
   })
 
+  it('orElseW', async () => {
+    U.deepStrictEqual(
+      await pipe(
+        _.left('foo'),
+        _.orElseW((l) => _.right(l.length))
+      )(),
+      E.right(3)
+    )
+    U.deepStrictEqual(
+      await pipe(
+        _.right(1),
+        _.orElseW(() => _.right(2))
+      )(),
+      E.right(1)
+    )
+  })
+
+  it('orElseFirst', async () => {
+    const f = _.orElseFirst((e: string) => (e.length <= 1 ? _.right(true) : _.left(e + '!')))
+    U.deepStrictEqual(await pipe(_.right(1), f)(), E.right(1))
+    U.deepStrictEqual(await pipe(_.left('a'), f)(), E.left('a'))
+    U.deepStrictEqual(await pipe(_.left('aa'), f)(), E.left('aa!'))
+  })
+
+  it('orElseFirstW', async () => {
+    const f = _.orElseFirstW((e: string) => (e.length <= 1 ? _.right(true) : _.left(e + '!')))
+    U.deepStrictEqual(await pipe(_.right(1), f)(), E.right(1))
+    U.deepStrictEqual(await pipe(_.left('a'), f)(), E.left('a'))
+    U.deepStrictEqual(await pipe(_.left('aa'), f)(), E.left('aa!'))
+  })
+
+  it('orLeft', async () => {
+    const f = _.orLeft((e: string) => T.of(e + '!'))
+    U.deepStrictEqual(await pipe(_.right(1), f)(), E.right(1))
+    U.deepStrictEqual(await pipe(_.left('a'), f)(), E.left('a!'))
+  })
+
   it('swap', async () => {
     U.deepStrictEqual(await _.swap(_.right(1))(), E.left(1))
     U.deepStrictEqual(await _.swap(_.left('a'))(), E.right('a'))