From 85a05337cbd9ddf5d75764c6dca4d802e7545a55 Mon Sep 17 00:00:00 2001 From: Milan Suk Date: Mon, 15 May 2023 19:48:26 +0200 Subject: [PATCH] feat: tapEither --- docs/modules/FromEither.ts.md | 119 +++++++++++++++-------- docs/modules/IOEither.ts.md | 90 ++++++++++++----- docs/modules/IOOption.ts.md | 59 ++++++++--- docs/modules/Option.ts.md | 82 +++++++++++----- docs/modules/ReaderEither.ts.md | 98 +++++++++++++------ docs/modules/ReaderTaskEither.ts.md | 104 ++++++++++++++------ docs/modules/StateReaderTaskEither.ts.md | 88 +++++++++++------ docs/modules/TaskEither.ts.md | 102 +++++++++++++------ docs/modules/TaskOption.ts.md | 63 +++++++++--- src/FromEither.ts | 45 ++++++++- src/IOEither.ts | 59 ++++++++--- src/IOOption.ts | 55 ++++++++--- src/Option.ts | 40 ++++++-- src/ReaderEither.ts | 63 +++++++++--- src/ReaderTaskEither.ts | 68 +++++++++---- src/StateReaderTaskEither.ts | 55 +++++++---- src/TaskEither.ts | 63 +++++++++--- src/TaskOption.ts | 59 ++++++++--- test/IOEither.ts | 7 ++ test/IOOption.ts | 9 ++ test/Option.ts | 7 ++ test/ReaderEither.ts | 7 ++ test/ReaderTaskEither.ts | 5 + test/StateReaderTaskEither.ts | 7 ++ test/TaskEither.ts | 7 ++ test/TaskOption.ts | 9 ++ 26 files changed, 1027 insertions(+), 343 deletions(-) diff --git a/docs/modules/FromEither.ts.md b/docs/modules/FromEither.ts.md index 2e67ab5f0..b9c8213cd 100644 --- a/docs/modules/FromEither.ts.md +++ b/docs/modules/FromEither.ts.md @@ -16,6 +16,8 @@ Added in v2.10.0 - [constructors](#constructors) - [fromOption](#fromoption) +- [legacy](#legacy) + - [chainFirstEitherK](#chainfirsteitherk) - [lifting](#lifting) - [fromPredicate](#frompredicate) - [model](#model) @@ -28,11 +30,11 @@ Added in v2.10.0 - [FromEither4 (interface)](#fromeither4-interface) - [utils](#utils) - [chainEitherK](#chaineitherk) - - [chainFirstEitherK](#chainfirsteitherk) - [chainOptionK](#chainoptionk) - [filterOrElse](#filterorelse) - [fromEitherK](#fromeitherk) - [fromOptionK](#fromoptionk) + - [tapEither](#tapeither) --- @@ -63,6 +65,47 @@ export declare function fromOption(F: FromEither): (onNone: LazyArg) Added in v2.10.0 +# legacy + +## chainFirstEitherK + +Alias of `tapEither`. + +**Signature** + +```ts +export declare function chainFirstEitherK( + F: FromEither4, + M: Chain4 +): (f: (a: A) => Either) => (ma: Kind4) => Kind4 +export declare function chainFirstEitherK( + F: FromEither3, + M: Chain3 +): (f: (a: A) => Either) => (ma: Kind3) => Kind3 +export declare function chainFirstEitherK( + F: FromEither3C, + M: Chain3C +): (f: (a: A) => Either) => (ma: Kind3) => Kind3 +export declare function chainFirstEitherK( + F: FromEither2, + M: Chain2 +): (f: (a: A) => Either) => (ma: Kind2) => Kind2 +export declare function chainFirstEitherK( + F: FromEither2C, + M: Chain2C +): (f: (a: A) => Either) => (ma: Kind2) => Kind2 +export declare function chainFirstEitherK( + F: FromEither1, + M: Chain1 +): (f: (a: A) => Either) => (ma: Kind) => Kind +export declare function chainFirstEitherK( + F: FromEither, + M: Chain +): (f: (a: A) => Either) => (ma: HKT2) => HKT2 +``` + +Added in v2.12.0 + # lifting ## fromPredicate @@ -248,43 +291,6 @@ export declare function chainEitherK( Added in v2.10.0 -## chainFirstEitherK - -**Signature** - -```ts -export declare function chainFirstEitherK( - F: FromEither4, - M: Chain4 -): (f: (a: A) => Either) => (ma: Kind4) => Kind4 -export declare function chainFirstEitherK( - F: FromEither3, - M: Chain3 -): (f: (a: A) => Either) => (ma: Kind3) => Kind3 -export declare function chainFirstEitherK( - F: FromEither3C, - M: Chain3C -): (f: (a: A) => Either) => (ma: Kind3) => Kind3 -export declare function chainFirstEitherK( - F: FromEither2, - M: Chain2 -): (f: (a: A) => Either) => (ma: Kind2) => Kind2 -export declare function chainFirstEitherK( - F: FromEither2C, - M: Chain2C -): (f: (a: A) => Either) => (ma: Kind2) => Kind2 -export declare function chainFirstEitherK( - F: FromEither1, - M: Chain1 -): (f: (a: A) => Either) => (ma: Kind) => Kind -export declare function chainFirstEitherK( - F: FromEither, - M: Chain -): (f: (a: A) => Either) => (ma: HKT2) => HKT2 -``` - -Added in v2.12.0 - ## chainOptionK **Signature** @@ -449,3 +455,40 @@ export declare function fromOptionK( ``` Added in v2.10.0 + +## tapEither + +**Signature** + +```ts +export declare function tapEither( + F: FromEither4, + M: Chain4 +): (self: Kind4, f: (a: A) => Either) => Kind4 +export declare function tapEither( + F: FromEither3, + M: Chain3 +): (self: Kind3, f: (a: A) => Either) => Kind3 +export declare function tapEither( + F: FromEither3C, + M: Chain3C +): (self: Kind3, f: (a: A) => Either) => Kind3 +export declare function tapEither( + F: FromEither2, + M: Chain2 +): (self: Kind2, f: (a: A) => Either) => Kind2 +export declare function tapEither( + F: FromEither2C, + M: Chain2C +): (self: Kind2, f: (a: A) => Either) => Kind2 +export declare function tapEither( + F: FromEither1, + M: Chain1 +): (self: Kind, f: (a: A) => Either) => Kind +export declare function tapEither( + F: FromEither, + M: Chain +): (self: HKT2, f: (a: A) => Either) => HKT2 +``` + +Added in v2.16.0 diff --git a/docs/modules/IOEither.ts.md b/docs/modules/IOEither.ts.md index b409acfe7..c2efa1067 100644 --- a/docs/modules/IOEither.ts.md +++ b/docs/modules/IOEither.ts.md @@ -20,6 +20,7 @@ Added in v2.0.0 - [combinators](#combinators) - [tap](#tap) + - [tapEither](#tapeither) - [constructors](#constructors) - [left](#left) - [leftIO](#leftio) @@ -79,6 +80,8 @@ Added in v2.0.0 - [chainEitherK](#chaineitherk) - [chainEitherKW](#chaineitherkw) - [chainFirst](#chainfirst) + - [chainFirstEitherK](#chainfirsteitherk) + - [chainFirstEitherKW](#chainfirsteitherkw) - [chainFirstW](#chainfirstw) - [chainOptionK](#chainoptionk) - [chainOptionKW](#chainoptionkw) @@ -106,8 +109,6 @@ Added in v2.0.0 - [matchEW](#matchew) - [matchW](#matchw) - [sequencing](#sequencing) - - [chainFirstEitherK](#chainfirsteitherk) - - [chainFirstEitherKW](#chainfirsteitherkw) - [chainFirstIOK](#chainfirstiok) - [chainIOK](#chainiok) - [flatMap](#flatmap) @@ -170,6 +171,39 @@ export declare const tap: { Added in v2.15.0 +## tapEither + +Composes computations in sequence, using the return value of one computation to determine the next computation and +keeping only the result of the first. + +**Signature** + +```ts +export declare const tapEither: { + (self: IOEither, f: (a: A) => E.Either): IOEither + (f: (a: A) => E.Either): (self: IOEither) => IOEither +} +``` + +**Example** + +```ts +import { pipe } from 'fp-ts/function' +import * as IOE from 'fp-ts/IOEither' +import * as E from 'fp-ts/Either' + +const compute = (value: string) => + pipe( + IOE.of(value), + IOE.tapEither(() => (value.length > 0 ? E.right('ok') : E.left('error'))) + ) + +assert.deepStrictEqual(compute('')(), E.left('error')) +assert.deepStrictEqual(compute('fp-ts')(), E.right('fp-ts')) +``` + +Added in v2.16.0 + # constructors ## left @@ -794,6 +828,34 @@ export declare const chainFirst: (f: (a: A) => IOEither) => (ma: Added in v2.0.0 +## chainFirstEitherK + +Alias of `tapEither`. + +**Signature** + +```ts +export declare const chainFirstEitherK: (f: (a: A) => E.Either) => (ma: IOEither) => IOEither +``` + +Added in v2.12.0 + +## chainFirstEitherKW + +Alias of `tapEither`. + +The `W` suffix (short for **W**idening) means that the error types will be merged. + +**Signature** + +```ts +export declare const chainFirstEitherKW: ( + f: (a: A) => E.Either +) => (ma: IOEither) => IOEither +``` + +Added in v2.12.0 + ## chainFirstW Alias of `tap`. @@ -1098,30 +1160,6 @@ Added in v2.10.0 # sequencing -## chainFirstEitherK - -**Signature** - -```ts -export declare const chainFirstEitherK: (f: (a: A) => E.Either) => (ma: IOEither) => IOEither -``` - -Added in v2.12.0 - -## chainFirstEitherKW - -The `W` suffix (short for **W**idening) means that the error types will be merged. - -**Signature** - -```ts -export declare const chainFirstEitherKW: ( - f: (a: A) => E.Either -) => (ma: IOEither) => IOEither -``` - -Added in v2.12.0 - ## chainFirstIOK **Signature** diff --git a/docs/modules/IOOption.ts.md b/docs/modules/IOOption.ts.md index 33270e0e8..7566d50d8 100644 --- a/docs/modules/IOOption.ts.md +++ b/docs/modules/IOOption.ts.md @@ -19,6 +19,7 @@ Added in v2.12.0 - [combinators](#combinators) - [tap](#tap) + - [tapEither](#tapeither) - [constructors](#constructors) - [none](#none) - [of](#of) @@ -68,6 +69,7 @@ Added in v2.12.0 - [legacy](#legacy) - [chain](#chain) - [chainFirst](#chainfirst) + - [chainFirstEitherK](#chainfirsteitherk) - [lifting](#lifting) - [fromEitherK](#fromeitherk) - [fromIOK](#fromiok) @@ -87,7 +89,6 @@ Added in v2.12.0 - [matchW](#matchw) - [sequencing](#sequencing) - [chainEitherK](#chaineitherk) - - [chainFirstEitherK](#chainfirsteitherk) - [chainFirstIOK](#chainfirstiok) - [chainIOK](#chainiok) - [chainNullableK](#chainnullablek) @@ -127,6 +128,40 @@ export declare const tap: { Added in v2.15.0 +## tapEither + +Composes computations in sequence, using the return value of one computation to determine the next computation and +keeping only the result of the first. + +**Signature** + +```ts +export declare const tapEither: { + (self: IOOption, f: (a: A) => Either): IOOption + (f: (a: A) => Either): (self: IOOption) => IOOption +} +``` + +**Example** + +```ts +import { pipe } from 'fp-ts/function' +import * as IOO from 'fp-ts/IOOption' +import * as O from 'fp-ts/Option' +import * as E from 'fp-ts/Either' + +const compute = (value: number) => + pipe( + IOO.of(value), + IOO.tapEither((value) => (value > 0 ? E.right('ok') : E.left('error'))) + ) + +assert.deepStrictEqual(compute(1)(), O.of(1)) +assert.deepStrictEqual(compute(-1)(), O.none) +``` + +Added in v2.15.0 + # constructors ## none @@ -592,6 +627,18 @@ export declare const chainFirst: (f: (a: A) => IOOption) => (first: IOO Added in v2.12.0 +## chainFirstEitherK + +Alias of `tapEither`. + +**Signature** + +```ts +export declare const chainFirstEitherK: (f: (a: A) => Either) => (ma: IOOption) => IOOption +``` + +Added in v2.12.0 + # lifting ## fromEitherK @@ -770,16 +817,6 @@ export declare const chainEitherK: (f: (a: A) => Either) => (ma: Added in v2.12.0 -## chainFirstEitherK - -**Signature** - -```ts -export declare const chainFirstEitherK: (f: (a: A) => Either) => (ma: IOOption) => IOOption -``` - -Added in v2.12.0 - ## chainFirstIOK **Signature** diff --git a/docs/modules/Option.ts.md b/docs/modules/Option.ts.md index a1d9f0e63..a483df5b1 100644 --- a/docs/modules/Option.ts.md +++ b/docs/modules/Option.ts.md @@ -73,6 +73,7 @@ Added in v2.0.0 - [combinators](#combinators) - [tap](#tap) + - [tapEither](#tapeither) - [constructors](#constructors) - [getLeft](#getleft) - [getRight](#getright) @@ -136,7 +137,9 @@ Added in v2.0.0 - [tryCatchK](#trycatchk) - [legacy](#legacy) - [chain](#chain) + - [chainEitherK](#chaineitherk) - [chainFirst](#chainfirst) + - [chainFirstEitherK](#chainfirsteitherk) - [lifting](#lifting) - [fromEitherK](#fromeitherk) - [fromNullableK](#fromnullablek) @@ -157,8 +160,6 @@ Added in v2.0.0 - [isNone](#isnone) - [isSome](#issome) - [sequencing](#sequencing) - - [chainEitherK](#chaineitherk) - - [chainFirstEitherK](#chainfirsteitherk) - [chainNullableK](#chainnullablek) - [flatMap](#flatmap) - [flatten](#flatten) @@ -213,6 +214,39 @@ export declare const tap: { Added in v2.15.0 +## tapEither + +Composes computations in sequence, using the return value of one computation to determine the next computation and +keeping only the result of the first. + +**Signature** + +```ts +export declare const tapEither: { + (self: Option, f: (a: A) => Either): Option + (f: (a: A) => Either): (self: Option) => Option +} +``` + +**Example** + +```ts +import { pipe } from 'fp-ts/function' +import * as O from 'fp-ts/Option' +import * as E from 'fp-ts/Either' + +const compute = (value: number) => + pipe( + O.of(value), + O.tapEither((value) => (value > 0 ? E.right('ok') : E.left('error'))) + ) + +assert.deepStrictEqual(compute(1), O.of(1)) +assert.deepStrictEqual(compute(-42), O.none) +``` + +Added in v2.15.0 + # constructors ## getLeft @@ -1022,6 +1056,18 @@ export declare const chain: (f: (a: A) => Option) => (ma: Option) => Added in v2.0.0 +## chainEitherK + +Alias of `tapEither`. + +**Signature** + +```ts +export declare const chainEitherK: (f: (a: A) => Either) => (ma: Option) => Option +``` + +Added in v2.11.0 + ## chainFirst Alias of `tap`. @@ -1034,6 +1080,18 @@ export declare const chainFirst: (f: (a: A) => Option) => (first: Optio Added in v2.0.0 +## chainFirstEitherK + +Alias of `tapEither`. + +**Signature** + +```ts +export declare const chainFirstEitherK: (f: (a: A) => Either) => (ma: Option) => Option +``` + +Added in v2.12.0 + # lifting ## fromEitherK @@ -1290,26 +1348,6 @@ Added in v2.0.0 # sequencing -## chainEitherK - -**Signature** - -```ts -export declare const chainEitherK: (f: (a: A) => Either) => (ma: Option) => Option -``` - -Added in v2.11.0 - -## chainFirstEitherK - -**Signature** - -```ts -export declare const chainFirstEitherK: (f: (a: A) => Either) => (ma: Option) => Option -``` - -Added in v2.12.0 - ## chainNullableK This is `chain` + `fromNullable`, useful when working with optional values. diff --git a/docs/modules/ReaderEither.ts.md b/docs/modules/ReaderEither.ts.md index 4ab2e0a28..6ba5de902 100644 --- a/docs/modules/ReaderEither.ts.md +++ b/docs/modules/ReaderEither.ts.md @@ -14,6 +14,7 @@ Added in v2.0.0 - [combinators](#combinators) - [tap](#tap) + - [tapEither](#tapeither) - [constructors](#constructors) - [ask](#ask) - [asks](#asks) @@ -71,6 +72,8 @@ Added in v2.0.0 - [chainEitherK](#chaineitherk) - [chainEitherKW](#chaineitherkw) - [chainFirst](#chainfirst) + - [chainFirstEitherK](#chainfirsteitherk) + - [chainFirstEitherKW](#chainfirsteitherkw) - [chainFirstW](#chainfirstw) - [chainOptionK](#chainoptionk) - [chainOptionKW](#chainoptionkw) @@ -98,8 +101,6 @@ Added in v2.0.0 - [matchEW](#matchew) - [matchW](#matchw) - [sequencing](#sequencing) - - [chainFirstEitherK](#chainfirsteitherk) - - [chainFirstEitherKW](#chainfirsteitherkw) - [chainFirstReaderK](#chainfirstreaderk) - [chainFirstReaderKW](#chainfirstreaderkw) - [chainReaderK](#chainreaderk) @@ -163,6 +164,39 @@ export declare const tap: { Added in v2.15.0 +## tapEither + +Composes computations in sequence, using the return value of one computation to determine the next computation and +keeping only the result of the first. + +**Signature** + +```ts +export declare const tapEither: { + (self: ReaderEither, f: (a: A) => E.Either): ReaderEither + (f: (a: A) => E.Either): (self: ReaderEither) => ReaderEither +} +``` + +**Example** + +```ts +import * as E from 'fp-ts/Either' +import { pipe } from 'fp-ts/function' +import * as RE from 'fp-ts/ReaderEither' + +const checkString = (value: string) => + pipe( + RE.ask(), + RE.tapEither((minLength) => (value.length > minLength ? E.right('ok') : E.left('error'))) + ) + +assert.deepStrictEqual(checkString('')(1), E.left('error')) +assert.deepStrictEqual(checkString('fp-ts')(2), E.right(2)) +``` + +Added in v2.16.0 + # constructors ## ask @@ -808,6 +842,38 @@ export declare const chainFirst: ( Added in v2.0.0 +## chainFirstEitherK + +Alias of `tapEither`. + +**Signature** + +```ts +export declare const chainFirstEitherK: ( + f: (a: A) => E.Either +) => (ma: ReaderEither) => ReaderEither +``` + +Added in v2.12.0 + +## chainFirstEitherKW + +Alias of `tapEither`. + +Less strict version of [`chainFirstEitherK`](#chainfirsteitherk). + +The `W` suffix (short for **W**idening) means that the environment types will be merged. + +**Signature** + +```ts +export declare const chainFirstEitherKW: ( + f: (a: A) => E.Either +) => (ma: ReaderEither) => ReaderEither +``` + +Added in v2.12.0 + ## chainFirstW Alias of `tap`. @@ -1120,34 +1186,6 @@ Added in v2.10.0 # sequencing -## chainFirstEitherK - -**Signature** - -```ts -export declare const chainFirstEitherK: ( - f: (a: A) => E.Either -) => (ma: ReaderEither) => ReaderEither -``` - -Added in v2.12.0 - -## chainFirstEitherKW - -Less strict version of [`chainFirstEitherK`](#chainfirsteitherk). - -The `W` suffix (short for **W**idening) means that the environment types will be merged. - -**Signature** - -```ts -export declare const chainFirstEitherKW: ( - f: (a: A) => E.Either -) => (ma: ReaderEither) => ReaderEither -``` - -Added in v2.12.0 - ## chainFirstReaderK **Signature** diff --git a/docs/modules/ReaderTaskEither.ts.md b/docs/modules/ReaderTaskEither.ts.md index 8c7e7ad6f..3d0ec5a99 100644 --- a/docs/modules/ReaderTaskEither.ts.md +++ b/docs/modules/ReaderTaskEither.ts.md @@ -14,6 +14,7 @@ Added in v2.0.0 - [combinators](#combinators) - [tap](#tap) + - [tapEither](#tapeither) - [constructors](#constructors) - [ask](#ask) - [asks](#asks) @@ -92,6 +93,8 @@ Added in v2.0.0 - [chainEitherK](#chaineitherk) - [chainEitherKW](#chaineitherkw) - [chainFirst](#chainfirst) + - [chainFirstEitherK](#chainfirsteitherk) + - [chainFirstEitherKW](#chainfirsteitherkw) - [chainFirstW](#chainfirstw) - [chainNullableK](#chainnullablek) - [chainOptionK](#chainoptionk) @@ -128,8 +131,6 @@ Added in v2.0.0 - [matchEW](#matchew) - [matchW](#matchw) - [sequencing](#sequencing) - - [chainFirstEitherK](#chainfirsteitherk) - - [chainFirstEitherKW](#chainfirsteitherkw) - [chainFirstIOK](#chainfirstiok) - [chainFirstReaderEitherK](#chainfirstreadereitherk) - [chainFirstReaderEitherKW](#chainfirstreadereitherkw) @@ -223,6 +224,45 @@ export declare const tap: { Added in v2.15.0 +## tapEither + +Composes computations in sequence, using the return value of one computation to determine the next computation and +keeping only the result of the first. + +**Signature** + +```ts +export declare const tapEither: { + (self: ReaderTaskEither, f: (a: A) => E.Either): ReaderTaskEither + (f: (a: A) => E.Either): ( + self: ReaderTaskEither + ) => ReaderTaskEither +} +``` + +**Example** + +```ts +import * as E from 'fp-ts/Either' +import { pipe } from 'fp-ts/function' +import * as RTE from 'fp-ts/ReaderTaskEither' + +const checkString = (value: string) => + pipe( + RTE.ask(), + RTE.tapEither((minLength) => (value.length > minLength ? E.right('ok') : E.left('error'))) + ) + +async function test() { + assert.deepStrictEqual(await checkString('')(2)(), E.left('error')) + assert.deepStrictEqual(await checkString('fp-ts')(2)(), E.right(2)) +} + +test() +``` + +Added in v2.16.0 + # constructors ## ask @@ -1097,6 +1137,38 @@ export declare const chainFirst: ( Added in v2.0.0 +## chainFirstEitherK + +Alias of `tapEither`. + +**Signature** + +```ts +export declare const chainFirstEitherK: ( + f: (a: A) => E.Either +) => (ma: ReaderTaskEither) => ReaderTaskEither +``` + +Added in v2.12.0 + +## chainFirstEitherKW + +Alias of `tapEither`. + +Less strict version of [`chainFirstEitherK`](#chainfirsteitherk). + +The `W` suffix (short for **W**idening) means that the environment types and the error types will be merged. + +**Signature** + +```ts +export declare const chainFirstEitherKW: ( + f: (a: A) => E.Either +) => (ma: ReaderTaskEither) => ReaderTaskEither +``` + +Added in v2.12.0 + ## chainFirstW Alias of `tap`. @@ -1531,34 +1603,6 @@ Added in v2.10.0 # sequencing -## chainFirstEitherK - -**Signature** - -```ts -export declare const chainFirstEitherK: ( - f: (a: A) => E.Either -) => (ma: ReaderTaskEither) => ReaderTaskEither -``` - -Added in v2.12.0 - -## chainFirstEitherKW - -Less strict version of [`chainFirstEitherK`](#chainfirsteitherk). - -The `W` suffix (short for **W**idening) means that the environment types and the error types will be merged. - -**Signature** - -```ts -export declare const chainFirstEitherKW: ( - f: (a: A) => E.Either -) => (ma: ReaderTaskEither) => ReaderTaskEither -``` - -Added in v2.12.0 - ## chainFirstIOK **Signature** diff --git a/docs/modules/StateReaderTaskEither.ts.md b/docs/modules/StateReaderTaskEither.ts.md index c70b7b6f8..1b864481a 100644 --- a/docs/modules/StateReaderTaskEither.ts.md +++ b/docs/modules/StateReaderTaskEither.ts.md @@ -14,6 +14,7 @@ Added in v2.0.0 - [combinators](#combinators) - [tap](#tap) + - [tapEither](#tapeither) - [constructors](#constructors) - [ask](#ask) - [asks](#asks) @@ -74,6 +75,8 @@ Added in v2.0.0 - [legacy](#legacy) - [chain](#chain) - [chainFirst](#chainfirst) + - [chainFirstEitherK](#chainfirsteitherk) + - [chainFirstEitherKW](#chainfirsteitherkw) - [chainFirstW](#chainfirstw) - [chainW](#chainw) - [lifting](#lifting) @@ -96,8 +99,6 @@ Added in v2.0.0 - [sequencing](#sequencing) - [chainEitherK](#chaineitherk) - [chainEitherKW](#chaineitherkw) - - [chainFirstEitherK](#chainfirsteitherk) - - [chainFirstEitherKW](#chainfirsteitherkw) - [chainFirstIOK](#chainfirstiok) - [chainFirstReaderK](#chainfirstreaderk) - [chainFirstReaderKW](#chainfirstreaderkw) @@ -175,6 +176,29 @@ export declare const tap: { Added in v2.15.0 +## tapEither + +Composes computations in sequence, using the return value of one computation to determine the next computation and +keeping only the result of the first. + +**Signature** + +```ts +export declare const tapEither: { + (self: StateReaderTaskEither, f: (a: A) => E.Either): StateReaderTaskEither< + S, + R1, + E1 | E2, + A + > + (f: (a: A) => E.Either): ( + self: StateReaderTaskEither + ) => StateReaderTaskEither +} +``` + +Added in v2.16.0 + # constructors ## ask @@ -799,6 +823,38 @@ export declare const chainFirst: ( Added in v2.0.0 +## chainFirstEitherK + +Alias of `tapEither`. + +**Signature** + +```ts +export declare const chainFirstEitherK: ( + f: (a: A) => E.Either +) => (ma: StateReaderTaskEither) => StateReaderTaskEither +``` + +Added in v2.12.0 + +## chainFirstEitherKW + +Alias of `tapEither`. + +Less strict version of [`chainFirstEitherK`](#chainfirsteitherk). + +The `W` suffix (short for **W**idening) means that the environment types and the error types will be merged. + +**Signature** + +```ts +export declare const chainFirstEitherKW: ( + f: (a: A) => E.Either +) => (ma: StateReaderTaskEither) => StateReaderTaskEither +``` + +Added in v2.12.0 + ## chainFirstW Alias of `tap`. @@ -1047,34 +1103,6 @@ export declare const chainEitherKW: ( Added in v2.6.1 -## chainFirstEitherK - -**Signature** - -```ts -export declare const chainFirstEitherK: ( - f: (a: A) => E.Either -) => (ma: StateReaderTaskEither) => StateReaderTaskEither -``` - -Added in v2.12.0 - -## chainFirstEitherKW - -Less strict version of [`chainFirstEitherK`](#chainfirsteitherk). - -The `W` suffix (short for **W**idening) means that the environment types and the error types will be merged. - -**Signature** - -```ts -export declare const chainFirstEitherKW: ( - f: (a: A) => E.Either -) => (ma: StateReaderTaskEither) => StateReaderTaskEither -``` - -Added in v2.12.0 - ## chainFirstIOK **Signature** diff --git a/docs/modules/TaskEither.ts.md b/docs/modules/TaskEither.ts.md index edc68a25e..e51764e58 100644 --- a/docs/modules/TaskEither.ts.md +++ b/docs/modules/TaskEither.ts.md @@ -21,6 +21,7 @@ Added in v2.0.0 - [combinators](#combinators) - [tap](#tap) + - [tapEither](#tapeither) - [constructors](#constructors) - [left](#left) - [leftIO](#leftio) @@ -91,6 +92,8 @@ Added in v2.0.0 - [chainEitherK](#chaineitherk) - [chainEitherKW](#chaineitherkw) - [chainFirst](#chainfirst) + - [chainFirstEitherK](#chainfirsteitherk) + - [chainFirstEitherKW](#chainfirsteitherkw) - [chainFirstW](#chainfirstw) - [chainNullableK](#chainnullablek) - [chainOptionK](#chainoptionk) @@ -123,8 +126,6 @@ Added in v2.0.0 - [matchEW](#matchew) - [matchW](#matchw) - [sequencing](#sequencing) - - [chainFirstEitherK](#chainfirsteitherk) - - [chainFirstEitherKW](#chainfirsteitherkw) - [chainFirstIOK](#chainfirstiok) - [chainFirstTaskK](#chainfirsttaskk) - [chainIOEitherK](#chainioeitherk) @@ -193,6 +194,43 @@ export declare const tap: { Added in v2.15.0 +## tapEither + +Composes computations in sequence, using the return value of one computation to determine the next computation and +keeping only the result of the first. + +**Signature** + +```ts +export declare const tapEither: { + (self: TaskEither, f: (a: A) => E.Either): TaskEither + (f: (a: A) => E.Either): (self: TaskEither) => TaskEither +} +``` + +**Example** + +```ts +import * as E from 'fp-ts/Either' +import { pipe } from 'fp-ts/function' +import * as TE from 'fp-ts/TaskEither' + +const checkString = (value: string) => + pipe( + TE.of(value), + TE.tapEither(() => (value.length > 0 ? E.right('ok') : E.left('error'))) + ) + +async function test() { + assert.deepStrictEqual(await checkString('')(), E.left('error')) + assert.deepStrictEqual(await checkString('fp-ts')(), E.right('fp-ts')) +} + +test() +``` + +Added in v2.16.0 + # constructors ## left @@ -1091,6 +1129,38 @@ export declare const chainFirst: (f: (a: A) => TaskEither) => (ma Added in v2.0.0 +## chainFirstEitherK + +Alias of `tapEither`. + +**Signature** + +```ts +export declare const chainFirstEitherK: ( + f: (a: A) => E.Either +) => (ma: TaskEither) => TaskEither +``` + +Added in v2.12.0 + +## chainFirstEitherKW + +Alias of `tapEither`. + +Less strict version of [`chainFirstEitherK`](#chainfirsteitherk). + +The `W` suffix (short for **W**idening) means that the error types will be merged. + +**Signature** + +```ts +export declare const chainFirstEitherKW: ( + f: (a: A) => E.Either +) => (ma: TaskEither) => TaskEither +``` + +Added in v2.12.0 + ## chainFirstW Alias of `tap`. @@ -1463,34 +1533,6 @@ Added in v2.10.0 # sequencing -## chainFirstEitherK - -**Signature** - -```ts -export declare const chainFirstEitherK: ( - f: (a: A) => E.Either -) => (ma: TaskEither) => TaskEither -``` - -Added in v2.12.0 - -## chainFirstEitherKW - -Less strict version of [`chainFirstEitherK`](#chainfirsteitherk). - -The `W` suffix (short for **W**idening) means that the error types will be merged. - -**Signature** - -```ts -export declare const chainFirstEitherKW: ( - f: (a: A) => E.Either -) => (ma: TaskEither) => TaskEither -``` - -Added in v2.12.0 - ## chainFirstIOK **Signature** diff --git a/docs/modules/TaskOption.ts.md b/docs/modules/TaskOption.ts.md index 1da9ee9bd..da2e95cf2 100644 --- a/docs/modules/TaskOption.ts.md +++ b/docs/modules/TaskOption.ts.md @@ -14,6 +14,7 @@ Added in v2.10.0 - [combinators](#combinators) - [tap](#tap) + - [tapEither](#tapeither) - [constructors](#constructors) - [none](#none) - [of](#of) @@ -69,6 +70,7 @@ Added in v2.10.0 - [legacy](#legacy) - [chain](#chain) - [chainFirst](#chainfirst) + - [chainFirstEitherK](#chainfirsteitherk) - [lifting](#lifting) - [fromEitherK](#fromeitherk) - [fromIOK](#fromiok) @@ -90,7 +92,6 @@ Added in v2.10.0 - [matchW](#matchw) - [sequencing](#sequencing) - [chainEitherK](#chaineitherk) - - [chainFirstEitherK](#chainfirsteitherk) - [chainFirstIOK](#chainfirstiok) - [chainFirstTaskK](#chainfirsttaskk) - [chainIOK](#chainiok) @@ -140,6 +141,44 @@ export declare const tap: { Added in v2.15.0 +## tapEither + +Composes computations in sequence, using the return value of one computation to determine the next computation and +keeping only the result of the first. + +**Signature** + +```ts +export declare const tapEither: { + (self: TaskOption, f: (a: A) => Either): TaskOption + (f: (a: A) => Either): (self: TaskOption) => TaskOption +} +``` + +**Example** + +```ts +import { pipe } from 'fp-ts/function' +import * as TO from 'fp-ts/TaskOption' +import * as O from 'fp-ts/Option' +import * as E from 'fp-ts/Either' + +const compute = (value: number) => + pipe( + TO.of(value), + TO.tapEither((value) => (value > 0 ? E.right('ok') : E.left('error'))) + ) + +async function test() { + assert.deepStrictEqual(await compute(1)(), O.of(1)) + assert.deepStrictEqual(await compute(-1)(), O.none) +} + +test() +``` + +Added in v2.16.0 + # constructors ## none @@ -673,6 +712,18 @@ export declare const chainFirst: (f: (a: A) => TaskOption) => (first: T Added in v2.10.0 +## chainFirstEitherK + +Alias of `tapEither`. + +**Signature** + +```ts +export declare const chainFirstEitherK: (f: (a: A) => Either) => (ma: TaskOption) => TaskOption +``` + +Added in v2.12.0 + # lifting ## fromEitherK @@ -884,16 +935,6 @@ export declare const chainEitherK: (f: (a: A) => Either) => (ma: Added in v2.12.0 -## chainFirstEitherK - -**Signature** - -```ts -export declare const chainFirstEitherK: (f: (a: A) => Either) => (ma: TaskOption) => TaskOption -``` - -Added in v2.12.0 - ## chainFirstIOK **Signature** diff --git a/src/FromEither.ts b/src/FromEither.ts index 1bd51b1a5..c1388019c 100644 --- a/src/FromEither.ts +++ b/src/FromEither.ts @@ -4,7 +4,7 @@ * @since 2.10.0 */ -import { Chain, Chain1, Chain2, Chain2C, Chain3, Chain3C, Chain4, chainFirst } from './Chain' +import { Chain, Chain1, Chain2, Chain2C, Chain3, Chain3C, Chain4, chainFirst, tap } from './Chain' import { Either } from './Either' import { flow, LazyArg } from './function' import { HKT2, Kind, Kind2, Kind3, Kind4, URIS, URIS2, URIS3, URIS4 } from './HKT' @@ -321,6 +321,9 @@ export function chainEitherK( } /** + * Alias of `tapEither`. + * + * @category legacy * @since 2.12.0 */ export function chainFirstEitherK( @@ -427,3 +430,43 @@ export function filterOrElse( (ma: Kind2): Kind2 => M.chain(ma, (a) => F.fromEither(predicate(a) ? _.right(a) : _.left(onFalse(a)))) } + +/** + * @since 2.16.0 + */ +export function tapEither( + F: FromEither4, + M: Chain4 +): (self: Kind4, f: (a: A) => Either) => Kind4 +export function tapEither( + F: FromEither3, + M: Chain3 +): (self: Kind3, f: (a: A) => Either) => Kind3 +export function tapEither( + F: FromEither3C, + M: Chain3C +): (self: Kind3, f: (a: A) => Either) => Kind3 +export function tapEither( + F: FromEither2, + M: Chain2 +): (self: Kind2, f: (a: A) => Either) => Kind2 +export function tapEither( + F: FromEither2C, + M: Chain2C +): (self: Kind2, f: (a: A) => Either) => Kind2 +export function tapEither( + F: FromEither1, + M: Chain1 +): (self: Kind, f: (a: A) => Either) => Kind +export function tapEither( + F: FromEither, + M: Chain +): (self: HKT2, f: (a: A) => Either) => HKT2 +export function tapEither( + F: FromEither2, + M: Chain2 +): (self: Kind2, f: (a: A) => Either) => Kind2 { + const fromEither = fromEitherK(F) + const tapM = tap(M) + return (self, f) => tapM(self, fromEither(f)) +} diff --git a/src/IOEither.ts b/src/IOEither.ts index 62a6b3ef5..a5a232d49 100644 --- a/src/IOEither.ts +++ b/src/IOEither.ts @@ -30,14 +30,14 @@ import { partitionMap as partitionMap_ } from './Filterable' import { - chainFirstEitherK as chainFirstEitherK_, chainOptionK as chainOptionK_, filterOrElse as filterOrElse_, FromEither2, fromEitherK as fromEitherK_, fromOption as fromOption_, fromOptionK as fromOptionK_, - fromPredicate as fromPredicate_ + fromPredicate as fromPredicate_, + tapEither as tapEither_ } from './FromEither' import { chainFirstIOK as chainFirstIOK_, chainIOK as chainIOK_, FromIO2, fromIOK as fromIOK_ } from './FromIO' import { dual, flow, identity, LazyArg, pipe, SK } from './function' @@ -613,6 +613,15 @@ export const Monad: Monad2 = { chain: flatMap } +/** + * @category instances + * @since 2.10.0 + */ +export const FromEither: FromEither2 = { + URI, + fromEither +} + /** * Composes computations in sequence, using the return value of one computation to determine the next computation and * keeping only the result of the first. @@ -625,6 +634,31 @@ export const tap: { (f: (a: A) => IOEither): (self: IOEither) => IOEither } = /*#__PURE__*/ dual(2, chainable.tap(Chain)) +/** + * Composes computations in sequence, using the return value of one computation to determine the next computation and + * keeping only the result of the first. + * + * @example + * import { pipe } from 'fp-ts/function' + * import * as IOE from 'fp-ts/IOEither' + * import * as E from 'fp-ts/Either' + * + * const compute = (value: string) => pipe( + * IOE.of(value), + * IOE.tapEither(() => value.length > 0 ? E.right('ok') : E.left('error')), + * ) + * + * assert.deepStrictEqual(compute('')(), E.left('error')) + * assert.deepStrictEqual(compute('fp-ts')(), E.right('fp-ts')) + * + * @category combinators + * @since 2.16.0 + */ +export const tapEither: { + (self: IOEither, f: (a: A) => Either): IOEither + (f: (a: A) => Either): (self: IOEither) => IOEither +} = /*#__PURE__*/ dual(2, tapEither_(FromEither, Chain)) + /** * @category instances * @since 2.7.0 @@ -692,15 +726,6 @@ export const chainIOK: (f: (a: A) => I.IO) => (first: IOEither export const chainFirstIOK: (f: (a: A) => I.IO) => (first: IOEither) => IOEither = /*#__PURE__*/ chainFirstIOK_(FromIO, Chain) -/** - * @category instances - * @since 2.10.0 - */ -export const FromEither: FromEither2 = { - URI, - fromEither -} - /** * @category conversions * @since 2.0.0 @@ -828,21 +853,25 @@ export const chainEitherKW: ( ) => (ma: IOEither) => IOEither = flatMapEither /** - * @category sequencing + * Alias of `tapEither`. + * + * @category legacy * @since 2.12.0 */ export const chainFirstEitherK: (f: (a: A) => E.Either) => (ma: IOEither) => IOEither = - /*#__PURE__*/ chainFirstEitherK_(FromEither, Chain) + tapEither /** + * Alias of `tapEither`. + * * The `W` suffix (short for **W**idening) means that the error types will be merged. * - * @category sequencing + * @category legacy * @since 2.12.0 */ export const chainFirstEitherKW: ( f: (a: A) => E.Either -) => (ma: IOEither) => IOEither = chainFirstEitherK as any +) => (ma: IOEither) => IOEither = tapEither /** * @category lifting diff --git a/src/IOOption.ts b/src/IOOption.ts index fe1f9e385..542652984 100644 --- a/src/IOOption.ts +++ b/src/IOOption.ts @@ -22,9 +22,9 @@ import { } from './Filterable' import { chainEitherK as chainEitherK_, - chainFirstEitherK as chainFirstEitherK_, FromEither1, - fromEitherK as fromEitherK_ + fromEitherK as fromEitherK_, + tapEither as tapEither_ } from './FromEither' import { chainFirstIOK as chainFirstIOK_, chainIOK as chainIOK_, FromIO1, fromIOK as fromIOK_ } from './FromIO' import { dual, flow, identity, LazyArg, pipe, SK } from './function' @@ -438,6 +438,15 @@ export const Chain: chainable.Chain1 = { chain: flatMap } +/** + * @category instances + * @since 2.12.0 + */ +export const FromEither: FromEither1 = { + URI, + fromEither +} + /** * Composes computations in sequence, using the return value of one computation to determine the next computation and * keeping only the result of the first. @@ -450,6 +459,32 @@ export const tap: { (f: (a: A) => IOOption<_>): (self: IOOption) => IOOption } = /*#__PURE__*/ dual(2, chainable.tap(Chain)) +/** + * Composes computations in sequence, using the return value of one computation to determine the next computation and + * keeping only the result of the first. + * + * @example + * import { pipe } from 'fp-ts/function' + * import * as IOO from 'fp-ts/IOOption' + * import * as O from 'fp-ts/Option' + * import * as E from 'fp-ts/Either' + * + * const compute = (value: number) => pipe( + * IOO.of(value), + * IOO.tapEither((value) => value > 0 ? E.right('ok') : E.left('error')), + * ) + * + * assert.deepStrictEqual(compute(1)(), O.of(1)) + * assert.deepStrictEqual(compute(-1)(), O.none) + * + * @category combinators + * @since 2.15.0 + */ +export const tapEither: { + (self: IOOption, f: (a: A) => Either): IOOption + (f: (a: A) => Either): (self: IOOption) => IOOption +} = /*#__PURE__*/ dual(2, tapEither_(FromEither, Chain)) + /** * @category instances * @since 2.12.0 @@ -570,15 +605,6 @@ export const chainIOK: (f: (a: A) => I.IO) => (first: IOOption) => I export const chainFirstIOK: (f: (a: A) => I.IO) => (first: IOOption) => IOOption = /*#__PURE__*/ chainFirstIOK_(FromIO, Chain) -/** - * @category instances - * @since 2.12.0 - */ -export const FromEither: FromEither1 = { - URI, - fromEither -} - /** * @category lifting * @since 2.12.0 @@ -595,11 +621,12 @@ export const chainEitherK: (f: (a: A) => Either) => (ma: IOOption /*#__PURE__*/ chainEitherK_(FromEither, Chain) /** - * @category sequencing + * Alias of `tapEither`. + * + * @category legacy * @since 2.12.0 */ -export const chainFirstEitherK: (f: (a: A) => Either) => (ma: IOOption) => IOOption = - /*#__PURE__*/ chainFirstEitherK_(FromEither, Chain) +export const chainFirstEitherK: (f: (a: A) => Either) => (ma: IOOption) => IOOption = tapEither // ------------------------------------------------------------------------------------- // do notation diff --git a/src/Option.ts b/src/Option.ts index a51004095..dc38a7f83 100644 --- a/src/Option.ts +++ b/src/Option.ts @@ -79,9 +79,9 @@ import { Filterable1 } from './Filterable' import { Foldable1 } from './Foldable' import { chainEitherK as chainEitherK_, - chainFirstEitherK as chainFirstEitherK_, FromEither1, - fromEitherK as fromEitherK_ + fromEitherK as fromEitherK_, + tapEither as tapEither_ } from './FromEither' import { constNull, constUndefined, dual, flow, identity, LazyArg, pipe } from './function' import { bindTo as bindTo_, flap as flap_, Functor1, let as let__ } from './Functor' @@ -939,6 +939,31 @@ export const tap: { (f: (a: A) => Option<_>): (self: Option) => Option } = /*#__PURE__*/ dual(2, chainable.tap(Chain)) +/** + * Composes computations in sequence, using the return value of one computation to determine the next computation and + * keeping only the result of the first. + * + * @example + * import { pipe } from 'fp-ts/function' + * import * as O from 'fp-ts/Option' + * import * as E from 'fp-ts/Either' + * + * const compute = (value: number) => pipe( + * O.of(value), + * O.tapEither((value) => value > 0 ? E.right('ok') : E.left('error')), + * ) + * + * assert.deepStrictEqual(compute(1), O.of(1)) + * assert.deepStrictEqual(compute(-42), O.none) + * + * @category combinators + * @since 2.15.0 + */ +export const tapEither: { + (self: Option, f: (a: A) => Either): Option + (f: (a: A) => Either): (self: Option) => Option +} = /*#__PURE__*/ dual(2, tapEither_(FromEither, Chain)) + /** * @since 2.0.0 */ @@ -953,18 +978,21 @@ export const fromEitherK: , B>( ) => (...a: A) => Option = /*#__PURE__*/ fromEitherK_(FromEither) /** - * @category sequencing + * Alias of `tapEither`. + * + * @category legacy * @since 2.11.0 */ export const chainEitherK: (f: (a: A) => Either) => (ma: Option) => Option = /*#__PURE__*/ chainEitherK_(FromEither, Chain) /** - * @category sequencing + * Alias of `tapEither`. + * + * @category legacy * @since 2.12.0 */ -export const chainFirstEitherK: (f: (a: A) => Either) => (ma: Option) => Option = - /*#__PURE__*/ chainFirstEitherK_(FromEither, Chain) +export const chainFirstEitherK: (f: (a: A) => Either) => (ma: Option) => Option = tapEither /** * Constructs a new `Option` from a nullable type. If the value is `null` or `undefined`, returns `None`, otherwise diff --git a/src/ReaderEither.ts b/src/ReaderEither.ts index 3f50425de..427c6fb45 100644 --- a/src/ReaderEither.ts +++ b/src/ReaderEither.ts @@ -24,14 +24,14 @@ import { partitionMap as partitionMap_ } from './Filterable' import { - chainFirstEitherK as chainFirstEitherK_, chainOptionK as chainOptionK_, filterOrElse as filterOrElse_, FromEither3, fromEitherK as fromEitherK_, fromOption as fromOption_, fromOptionK as fromOptionK_, - fromPredicate as fromPredicate_ + fromPredicate as fromPredicate_, + tapEither as tapEither_ } from './FromEither' import { ask as ask_, @@ -610,6 +610,15 @@ export const Monad: Monad3 = { chain: flatMap } +/** + * @category instances + * @since 2.10.0 + */ +export const FromEither: FromEither3 = { + URI, + fromEither +} + /** * Composes computations in sequence, using the return value of one computation to determine the next computation and * keeping only the result of the first. @@ -628,6 +637,35 @@ export const tap: { ) => ReaderEither } = /*#__PURE__*/ dual(2, chainable.tap(Chain)) +/** + * Composes computations in sequence, using the return value of one computation to determine the next computation and + * keeping only the result of the first. + * + * @example + * import * as E from 'fp-ts/Either' + * import { pipe } from 'fp-ts/function' + * import * as RE from 'fp-ts/ReaderEither' + * + * const checkString = (value: string) => pipe( + * RE.ask(), + * RE.tapEither( + * (minLength) => value.length > minLength + * ? E.right('ok') + * : E.left('error') + * ) + * ) + * + * assert.deepStrictEqual(checkString('')(1), E.left('error')) + * assert.deepStrictEqual(checkString('fp-ts')(2), E.right(2)) + * + * @category combinators + * @since 2.16.0 + */ +export const tapEither: { + (self: ReaderEither, f: (a: A) => Either): ReaderEither + (f: (a: A) => Either): (self: ReaderEither) => ReaderEither +} = /*#__PURE__*/ dual(2, tapEither_(FromEither, Chain)) + /** * @category instances * @since 2.7.0 @@ -734,15 +772,6 @@ export const MonadThrow: MonadThrow3 = { throwError } -/** - * @category instances - * @since 2.10.0 - */ -export const FromEither: FromEither3 = { - URI, - fromEither -} - /** * @category conversions * @since 2.0.0 @@ -877,24 +906,28 @@ export const chainEitherKW: ( ) => (ma: ReaderEither) => ReaderEither = flatMapEither /** - * @category sequencing + * Alias of `tapEither`. + * + * @category legacy * @since 2.12.0 */ export const chainFirstEitherK: ( f: (a: A) => E.Either -) => (ma: ReaderEither) => ReaderEither = /*#__PURE__*/ chainFirstEitherK_(FromEither, Chain) +) => (ma: ReaderEither) => ReaderEither = tapEither /** + * Alias of `tapEither`. + * * Less strict version of [`chainFirstEitherK`](#chainfirsteitherk). * * The `W` suffix (short for **W**idening) means that the environment types will be merged. * - * @category sequencing + * @category legacy * @since 2.12.0 */ export const chainFirstEitherKW: ( f: (a: A) => Either -) => (ma: ReaderEither) => ReaderEither = chainFirstEitherK as any +) => (ma: ReaderEither) => ReaderEither = tapEither /** * @category lifting diff --git a/src/ReaderTaskEither.ts b/src/ReaderTaskEither.ts index 5e91664b8..074e51124 100644 --- a/src/ReaderTaskEither.ts +++ b/src/ReaderTaskEither.ts @@ -25,14 +25,14 @@ import { partitionMap as partitionMap_ } from './Filterable' import { - chainFirstEitherK as chainFirstEitherK_, chainOptionK as chainOptionK_, filterOrElse as filterOrElse_, FromEither3, fromEitherK as fromEitherK_, fromOption as fromOption_, fromOptionK as fromOptionK_, - fromPredicate as fromPredicate_ + fromPredicate as fromPredicate_, + tapEither as tapEither_ } from './FromEither' import { chainFirstIOK as chainFirstIOK_, chainIOK as chainIOK_, FromIO3, fromIOK as fromIOK_ } from './FromIO' import { @@ -967,6 +967,15 @@ export const MonadThrow: MonadThrow3 = { throwError } +/** + * @category instances + * @since 2.10.0 + */ +export const FromEither: FromEither3 = { + URI, + fromEither +} + /** * Composes computations in sequence, using the return value of one computation to determine the next computation and * keeping only the result of the first. @@ -985,6 +994,37 @@ export const tap: { ) => ReaderTaskEither } = /*#__PURE__*/ dual(2, chainable.tap(Chain)) +/** + * Composes computations in sequence, using the return value of one computation to determine the next computation and + * keeping only the result of the first. + * + * @example + * import * as E from 'fp-ts/Either' + * import { pipe } from 'fp-ts/function' + * import * as RTE from 'fp-ts/ReaderTaskEither' + * + * const checkString = (value: string) => pipe( + * RTE.ask(), + * RTE.tapEither((minLength) => value.length > minLength ? E.right('ok') : E.left('error')) + * ) + * + * async function test() { + * assert.deepStrictEqual(await checkString('')(2)(), E.left('error')) + * assert.deepStrictEqual(await checkString('fp-ts')(2)(), E.right(2)) + * } + * + * test() + * + * @category combinators + * @since 2.16.0 + */ +export const tapEither: { + (self: ReaderTaskEither, f: (a: A) => Either): ReaderTaskEither + (f: (a: A) => Either): ( + self: ReaderTaskEither + ) => ReaderTaskEither +} = /*#__PURE__*/ dual(2, tapEither_(FromEither, Chain)) + /** * @category instances * @since 2.7.0 @@ -1179,15 +1219,6 @@ export const chainFirstReaderIOK: ( f: (a: A) => ReaderIO ) => (ma: ReaderTaskEither) => ReaderTaskEither = chainFirstReaderIOKW -/** - * @category instances - * @since 2.10.0 - */ -export const FromEither: FromEither3 = { - URI, - fromEither -} - /** * @category conversions * @since 2.0.0 @@ -1322,27 +1353,28 @@ export const chainEitherKW: ( ) => (ma: ReaderTaskEither) => ReaderTaskEither = flatMapEither /** - * @category sequencing + * Alias of `tapEither`. + * + * @category legacy * @since 2.12.0 */ export const chainFirstEitherK: ( f: (a: A) => E.Either -) => (ma: ReaderTaskEither) => ReaderTaskEither = /*#__PURE__*/ chainFirstEitherK_( - FromEither, - Chain -) +) => (ma: ReaderTaskEither) => ReaderTaskEither = tapEither /** + * Alias of `tapEither`. + * * Less strict version of [`chainFirstEitherK`](#chainfirsteitherk). * * The `W` suffix (short for **W**idening) means that the environment types and the error types will be merged. * - * @category sequencing + * @category legacy * @since 2.12.0 */ export const chainFirstEitherKW: ( f: (a: A) => Either -) => (ma: ReaderTaskEither) => ReaderTaskEither = chainFirstEitherK as any +) => (ma: ReaderTaskEither) => ReaderTaskEither = tapEither /** * @category lifting diff --git a/src/StateReaderTaskEither.ts b/src/StateReaderTaskEither.ts index 124998e1c..ddd479775 100644 --- a/src/StateReaderTaskEither.ts +++ b/src/StateReaderTaskEither.ts @@ -10,14 +10,14 @@ import * as E from './Either' import { Endomorphism } from './Endomorphism' import { chainEitherK as chainEitherK_, - chainFirstEitherK as chainFirstEitherK_, chainOptionK as chainOptionK_, filterOrElse as filterOrElse_, FromEither4, fromEitherK as fromEitherK_, fromOption as fromOption_, fromOptionK as fromOptionK_, - fromPredicate as fromPredicate_ + fromPredicate as fromPredicate_, + tapEither as tapEither_ } from './FromEither' import { chainFirstIOK as chainFirstIOK_, chainIOK as chainIOK_, FromIO4, fromIOK as fromIOK_ } from './FromIO' import { @@ -736,6 +736,15 @@ export const MonadThrow: MonadThrow4 = { throwError } +/** + * @category instances + * @since 2.10.0 + */ +export const FromEither: FromEither4 = { + URI, + fromEither +} + /** * Composes computations in sequence, using the return value of one computation to determine the next computation and * keeping only the result of the first. @@ -753,6 +762,25 @@ export const tap: { ) => StateReaderTaskEither } = /*#__PURE__*/ dual(2, chainable.tap(Chain)) +/** + * Composes computations in sequence, using the return value of one computation to determine the next computation and + * keeping only the result of the first. + * + * @category combinators + * @since 2.16.0 + */ +export const tapEither: { + (self: StateReaderTaskEither, f: (a: A) => Either): StateReaderTaskEither< + S, + R1, + E1 | E2, + A + > + (f: (a: A) => Either): ( + self: StateReaderTaskEither + ) => StateReaderTaskEither +} = /*#__PURE__*/ dual(2, tapEither_(FromEither, Chain)) + /** * @category instances * @since 2.7.0 @@ -852,15 +880,6 @@ export const chainFirstReaderKW: ( ) => (ma: StateReaderTaskEither) => StateReaderTaskEither = chainFirstReaderK as any -/** - * @category instances - * @since 2.10.0 - */ -export const FromEither: FromEither4 = { - URI, - fromEither -} - /** * @category conversions * @since 2.0.0 @@ -933,26 +952,28 @@ export const chainEitherKW: ( ) => (ma: StateReaderTaskEither) => StateReaderTaskEither = chainEitherK as any /** - * @category sequencing + * Alias of `tapEither`. + * + * @category legacy * @since 2.12.0 */ export const chainFirstEitherK: ( f: (a: A) => E.Either -) => (ma: StateReaderTaskEither) => StateReaderTaskEither = - /*#__PURE__*/ chainFirstEitherK_(FromEither, Chain) +) => (ma: StateReaderTaskEither) => StateReaderTaskEither = tapEither /** + * Alias of `tapEither`. + * * Less strict version of [`chainFirstEitherK`](#chainfirsteitherk). * * The `W` suffix (short for **W**idening) means that the environment types and the error types will be merged. * - * @category sequencing + * @category legacy * @since 2.12.0 */ export const chainFirstEitherKW: ( f: (a: A) => Either -) => (ma: StateReaderTaskEither) => StateReaderTaskEither = - chainFirstEitherK as any +) => (ma: StateReaderTaskEither) => StateReaderTaskEither = tapEither /** * @category lifting diff --git a/src/TaskEither.ts b/src/TaskEither.ts index ffb034f29..f7cc317bd 100644 --- a/src/TaskEither.ts +++ b/src/TaskEither.ts @@ -32,14 +32,14 @@ import { partitionMap as partitionMap_ } from './Filterable' import { - chainFirstEitherK as chainFirstEitherK_, chainOptionK as chainOptionK_, filterOrElse as filterOrElse_, FromEither2, fromEitherK as fromEitherK_, fromOption as fromOption_, fromOptionK as fromOptionK_, - fromPredicate as fromPredicate_ + fromPredicate as fromPredicate_, + tapEither as tapEither_ } from './FromEither' import { chainFirstIOK as chainFirstIOK_, chainIOK as chainIOK_, FromIO2, fromIOK as fromIOK_ } from './FromIO' import { @@ -912,6 +912,15 @@ export const MonadThrow: MonadThrow2 = { throwError } +/** + * @category instances + * @since 2.10.0 + */ +export const FromEither: FromEither2 = { + URI, + fromEither +} + /** * Composes computations in sequence, using the return value of one computation to determine the next computation and * keeping only the result of the first. @@ -924,6 +933,35 @@ export const tap: { (f: (a: A) => TaskEither): (self: TaskEither) => TaskEither } = /*#__PURE__*/ dual(2, chainable.tap(Chain)) +/** + * Composes computations in sequence, using the return value of one computation to determine the next computation and + * keeping only the result of the first. + * + * @example + * import * as E from 'fp-ts/Either' + * import { pipe } from 'fp-ts/function' + * import * as TE from 'fp-ts/TaskEither' + * + * const checkString = (value: string) => pipe( + * TE.of(value), + * TE.tapEither(() => value.length > 0 ? E.right('ok') : E.left('error')) + * ) + * + * async function test() { + * assert.deepStrictEqual(await checkString('')(), E.left('error')) + * assert.deepStrictEqual(await checkString('fp-ts')(), E.right('fp-ts')) + * } + * + * test() + * + * @category combinators + * @since 2.16.0 + */ +export const tapEither: { + (self: TaskEither, f: (a: A) => Either): TaskEither + (f: (a: A) => Either): (self: TaskEither) => TaskEither +} = /*#__PURE__*/ dual(2, tapEither_(FromEither, Chain)) + /** * @category instances * @since 2.7.0 @@ -944,15 +982,6 @@ export const Alt: Alt2 = { alt: _alt } -/** - * @category instances - * @since 2.10.0 - */ -export const FromEither: FromEither2 = { - URI, - fromEither -} - /** * @category conversions * @since 2.0.0 @@ -1080,23 +1109,27 @@ export const chainEitherKW: ( ) => (ma: TaskEither) => TaskEither = flatMapEither /** - * @category sequencing + * Alias of `tapEither`. + * + * @category legacy * @since 2.12.0 */ export const chainFirstEitherK: (f: (a: A) => E.Either) => (ma: TaskEither) => TaskEither = - /*#__PURE__*/ chainFirstEitherK_(FromEither, Chain) + tapEither /** + * Alias of `tapEither`. + * * Less strict version of [`chainFirstEitherK`](#chainfirsteitherk). * * The `W` suffix (short for **W**idening) means that the error types will be merged. * - * @category sequencing + * @category legacy * @since 2.12.0 */ export const chainFirstEitherKW: ( f: (a: A) => E.Either -) => (ma: TaskEither) => TaskEither = chainFirstEitherK as any +) => (ma: TaskEither) => TaskEither = tapEither /** * @category lifting diff --git a/src/TaskOption.ts b/src/TaskOption.ts index a9e6482b2..071201d29 100644 --- a/src/TaskOption.ts +++ b/src/TaskOption.ts @@ -17,9 +17,9 @@ import { } from './Filterable' import { chainEitherK as chainEitherK_, - chainFirstEitherK as chainFirstEitherK_, FromEither1, - fromEitherK as fromEitherK_ + fromEitherK as fromEitherK_, + tapEither as tapEither_ } from './FromEither' import { chainFirstIOK as chainFirstIOK_, chainIOK as chainIOK_, FromIO1, fromIOK as fromIOK_ } from './FromIO' import { @@ -512,6 +512,15 @@ export const Chain: chainable.Chain1 = { chain: flatMap } +/** + * @category instances + * @since 2.11.0 + */ +export const FromEither: FromEither1 = { + URI, + fromEither +} + /** * Composes computations in sequence, using the return value of one computation to determine the next computation and * keeping only the result of the first. @@ -524,6 +533,36 @@ export const tap: { (f: (a: A) => TaskOption<_>): (self: TaskOption) => TaskOption } = /*#__PURE__*/ dual(2, chainable.tap(Chain)) +/** + * Composes computations in sequence, using the return value of one computation to determine the next computation and + * keeping only the result of the first. + * + * @example + * import { pipe } from 'fp-ts/function' + * import * as TO from 'fp-ts/TaskOption' + * import * as O from 'fp-ts/Option' + * import * as E from 'fp-ts/Either' + * + * const compute = (value: number) => pipe( + * TO.of(value), + * TO.tapEither((value) => value > 0 ? E.right('ok') : E.left('error')), + * ) + * + * async function test() { + * assert.deepStrictEqual(await compute(1)(), O.of(1)) + * assert.deepStrictEqual(await compute(-1)(), O.none) + * } + * + * test() + * + * @category combinators + * @since 2.16.0 + */ +export const tapEither: { + (self: TaskOption, f: (a: A) => Either): TaskOption + (f: (a: A) => Either): (self: TaskOption) => TaskOption +} = /*#__PURE__*/ dual(2, tapEither_(FromEither, Chain)) + /** * @category instances * @since 2.10.0 @@ -658,15 +697,6 @@ export const chainIOK: (f: (a: A) => IO) => (first: TaskOption) => T export const chainFirstIOK: (f: (a: A) => IO) => (first: TaskOption) => TaskOption = /*#__PURE__*/ chainFirstIOK_(FromIO, Chain) -/** - * @category instances - * @since 2.11.0 - */ -export const FromEither: FromEither1 = { - URI, - fromEither -} - /** * @category lifting * @since 2.12.0 @@ -683,11 +713,12 @@ export const chainEitherK: (f: (a: A) => Either) => (ma: TaskOpti /*#__PURE__*/ chainEitherK_(FromEither, Chain) /** - * @category sequencing + * Alias of `tapEither`. + * + * @category legacy * @since 2.12.0 */ -export const chainFirstEitherK: (f: (a: A) => Either) => (ma: TaskOption) => TaskOption = - /*#__PURE__*/ chainFirstEitherK_(FromEither, Chain) +export const chainFirstEitherK: (f: (a: A) => Either) => (ma: TaskOption) => TaskOption = tapEither /** * @category instances diff --git a/test/IOEither.ts b/test/IOEither.ts index af9751055..4a4a64e50 100644 --- a/test/IOEither.ts +++ b/test/IOEither.ts @@ -677,6 +677,13 @@ describe.concurrent('IOEither', () => { U.deepStrictEqual(f(_.left(1))(), 'left') }) + it('tapEither', async () => { + const f = (s: string) => E.right(s.length) + U.deepStrictEqual(pipe(_.right('a'), _.tapEither(f))(), E.right('a')) + const g = (s: string) => E.left(s.length) + U.deepStrictEqual(pipe(_.right('a'), _.tapEither(g))(), E.left(1)) + }) + it('chainFirstEitherK', async () => { const f = (s: string) => E.right(s.length) U.deepStrictEqual(pipe(_.right('a'), _.chainFirstEitherK(f))(), E.right('a')) diff --git a/test/IOOption.ts b/test/IOOption.ts index 899f326c1..0301521de 100644 --- a/test/IOOption.ts +++ b/test/IOOption.ts @@ -239,6 +239,15 @@ describe.concurrent('IOOption', () => { U.deepStrictEqual(g(_.of('aaa'))(), O.none) }) + it('tapEither', () => { + const f = (s: string) => (s.length <= 2 ? E.right(s + '!') : E.left(s.length)) + const g = _.tapEither(f) + U.deepStrictEqual(g(_.of(''))(), O.some('')) + U.deepStrictEqual(g(_.of('a'))(), O.some('a')) + U.deepStrictEqual(g(_.of('aa'))(), O.some('aa')) + U.deepStrictEqual(g(_.of('aaa'))(), O.none) + }) + it('chainFirstEitherK', () => { const f = (s: string) => (s.length <= 2 ? E.right(s + '!') : E.left(s.length)) const g = _.chainFirstEitherK(f) diff --git a/test/Option.ts b/test/Option.ts index addb0ad27..8bdcf3a71 100644 --- a/test/Option.ts +++ b/test/Option.ts @@ -545,6 +545,13 @@ describe.concurrent('Option', () => { ) }) + it('tapEither', async () => { + const f = (s: string) => E.right(s.length) + U.deepStrictEqual(pipe(_.some('a'), _.tapEither(f)), _.some('a')) + const g = (s: string) => E.left(s.length) + U.deepStrictEqual(pipe(_.some('a'), _.tapEither(g)), _.none) + }) + it('chainFirstEitherK', async () => { const f = (s: string) => E.right(s.length) U.deepStrictEqual(pipe(_.some('a'), _.chainFirstEitherK(f)), _.some('a')) diff --git a/test/ReaderEither.ts b/test/ReaderEither.ts index 7f267a805..59a66dcf8 100644 --- a/test/ReaderEither.ts +++ b/test/ReaderEither.ts @@ -400,6 +400,13 @@ describe.concurrent('ReaderEither', () => { U.deepStrictEqual(pipe(_.right<{}, never, number>(3), f)({}), E.right(3)) }) + it('tapEither', async () => { + const f = (s: string) => E.right(s.length) + U.deepStrictEqual(pipe(_.right('a'), _.tapEither(f))({}), E.right('a')) + const g = (s: string) => E.left(s.length) + U.deepStrictEqual(pipe(_.right('a'), _.tapEither(g))({}), E.left(1)) + }) + it('chainFirstEitherK', async () => { const f = (s: string) => E.right(s.length) U.deepStrictEqual(pipe(_.right('a'), _.chainFirstEitherK(f))({}), E.right('a')) diff --git a/test/ReaderTaskEither.ts b/test/ReaderTaskEither.ts index 6002cefa6..44113bd03 100644 --- a/test/ReaderTaskEither.ts +++ b/test/ReaderTaskEither.ts @@ -480,6 +480,11 @@ describe.concurrent('ReaderTaskEither', () => { U.deepStrictEqual(await pipe(_.right('a'), _.chainEitherK(f))(undefined)(), E.right(1)) }) + it('tapEither', async () => { + const f = (s: string) => E.right(s.length) + U.deepStrictEqual(await pipe(_.right<{}, number, string>('a'), _.tapEither(f))({})(), E.right('a')) + }) + it('chainFirstEitherKW', async () => { const f = (s: string) => E.right(s.length) U.deepStrictEqual(await pipe(_.right<{}, number, string>('a'), _.chainFirstEitherKW(f))({})(), E.right('a')) diff --git a/test/StateReaderTaskEither.ts b/test/StateReaderTaskEither.ts index a6ed5e93a..82da8711a 100644 --- a/test/StateReaderTaskEither.ts +++ b/test/StateReaderTaskEither.ts @@ -501,6 +501,13 @@ describe.concurrent('StateReaderTaskEither', () => { U.deepStrictEqual(await _.asksStateReaderTaskEither(f)({})(e)(), E.right(tuple(1, {}))) }) + it('tapEither', async () => { + const f = (s: string) => E.right(s.length) + U.deepStrictEqual(await pipe(_.right('a'), _.tapEither(f), _.evaluate(state))({})(), E.right('a')) + const g = (s: string) => E.left(s.length) + U.deepStrictEqual(await pipe(_.right('a'), _.tapEither(g), _.evaluate(state))({})(), E.left(1)) + }) + it('chainFirstEitherK', async () => { const f = (s: string) => E.right(s.length) U.deepStrictEqual(await pipe(_.right('a'), _.chainFirstEitherK(f), _.evaluate(state))({})(), E.right('a')) diff --git a/test/TaskEither.ts b/test/TaskEither.ts index 7eff3c0db..335167118 100644 --- a/test/TaskEither.ts +++ b/test/TaskEither.ts @@ -529,6 +529,13 @@ describe.concurrent('TaskEither', () => { U.deepStrictEqual(await pipe(_.right('a'), _.chainEitherK(f))(), E.right(1)) }) + it('tapEither', async () => { + const f = (s: string) => E.right(s.length) + U.deepStrictEqual(await pipe(_.right('a'), _.tapEither(f))(), E.right('a')) + const g = (s: string) => E.left(s.length) + U.deepStrictEqual(await pipe(_.right('a'), _.tapEither(g))(), E.left(1)) + }) + it('chainFirstEitherK', async () => { const f = (s: string) => E.right(s.length) U.deepStrictEqual(await pipe(_.right('a'), _.chainFirstEitherK(f))(), E.right('a')) diff --git a/test/TaskOption.ts b/test/TaskOption.ts index 11e932467..f9c219f4f 100644 --- a/test/TaskOption.ts +++ b/test/TaskOption.ts @@ -364,6 +364,15 @@ describe.concurrent('TaskOption', () => { U.deepStrictEqual(await g(_.of('aaa'))(), O.none) }) + it('tapEither', async () => { + const f = (s: string) => (s.length <= 2 ? E.right(s + '!') : E.left(s.length)) + const g = _.tapEither(f) + U.deepStrictEqual(await g(_.of(''))(), O.some('')) + U.deepStrictEqual(await g(_.of('a'))(), O.some('a')) + U.deepStrictEqual(await g(_.of('aa'))(), O.some('aa')) + U.deepStrictEqual(await g(_.of('aaa'))(), O.none) + }) + it('chainFirstEitherK', async () => { const f = (s: string) => (s.length <= 2 ? E.right(s + '!') : E.left(s.length)) const g = _.chainFirstEitherK(f)