Skip to content

Commit

Permalink
Either: add flatMap
Browse files Browse the repository at this point in the history
  • Loading branch information
gcanti committed Apr 20, 2023
1 parent 3830e7f commit f817388
Show file tree
Hide file tree
Showing 5 changed files with 52 additions and 16 deletions.
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
{
"typescript.tsdk": "./node_modules/typescript/lib",
"eslint.validate": ["markdown", "javascript", "typescript"],
"editor.defaultFormatter": "esbenp.prettier-vscode",
"eslint.validate": ["typescript"],
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
Expand Down
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
**Note**: A feature tagged as Experimental is in a
high state of flux, you're at risk of it changing without notice.

# 2.14.0

**New Feature**

- `Either`
- add `flatMap`

# 2.13.2

- add `chainOptionKW`, #1846 (@DenisFrezzato)
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fp-ts",
"version": "2.13.2",
"version": "2.14.0",
"description": "Functional programming in TypeScript",
"main": "./lib/index.js",
"module": "./es6/index.js",
Expand Down
35 changes: 21 additions & 14 deletions src/Either.ts
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ import {
fromOptionK as fromOptionK_,
fromPredicate as fromPredicate_
} from './FromEither'
import { flow, identity, Lazy, pipe } from './function'
import { dual, flow, identity, Lazy, pipe } from './function'
import { bindTo as bindTo_, flap as flap_, Functor2, let as let__ } from './Functor'
import { HKT } from './HKT'
import * as _ from './internal'
Expand Down Expand Up @@ -155,11 +155,21 @@ export const left: <E = never, A = never>(e: E) => Either<E, A> = _.left
*/
export const right: <E = never, A = never>(a: A) => Either<E, A> = _.right

/**
* @category sequencing
* @since 2.14.0
*/
export const flatMap: {
<E2, A, B>(f: (a: A) => Either<E2, B>): <E1>(ma: Either<E1, A>) => Either<E1 | E2, B>
<E1, A, E2, B>(ma: Either<E1, A>, f: (a: A) => Either<E2, B>): Either<E1 | E2, B>
} = dual(
2,
<E1, A, E2, B>(ma: Either<E1, A>, f: (a: A) => Either<E2, B>): Either<E1 | E2, B> => (isLeft(ma) ? ma : f(ma.right))
)

const _map: Monad2<URI>['map'] = (fa, f) => pipe(fa, map(f))
const _ap: Monad2<URI>['ap'] = (fab, fa) => pipe(fab, ap(fa))
/* istanbul ignore next */
const _chain: Monad2<URI>['chain'] = (ma, f) => pipe(ma, chain(f))
/* istanbul ignore next */
const _reduce: Foldable2<URI>['reduce'] = (fa, b, f) => pipe(fa, reduce(b, f))
/* istanbul ignore next */
const _foldMap: Foldable2<URI>['foldMap'] = (M) => (fa, f) => {
Expand Down Expand Up @@ -549,18 +559,15 @@ export const Applicative: Applicative2<URI> = {
* @category sequencing
* @since 2.6.0
*/
export const chainW =
<E2, A, B>(f: (a: A) => Either<E2, B>) =>
<E1>(ma: Either<E1, A>): Either<E1 | E2, B> =>
isLeft(ma) ? ma : f(ma.right)
export const chainW: <E2, A, B>(f: (a: A) => Either<E2, B>) => <E1>(ma: Either<E1, A>) => Either<E2 | E1, B> = flatMap

/**
* Composes computations in sequence, using the return value of one computation to determine the next computation.
*
* @category sequencing
* @since 2.0.0
*/
export const chain: <E, A, B>(f: (a: A) => Either<E, B>) => (ma: Either<E, A>) => Either<E, B> = chainW
export const chain: <E, A, B>(f: (a: A) => Either<E, B>) => (ma: Either<E, A>) => Either<E, B> = flatMap

/**
* @category instances
Expand All @@ -570,7 +577,7 @@ export const Chain: Chain2<URI> = {
URI,
map: _map,
ap: _ap,
chain: _chain
chain: flatMap
}

/**
Expand All @@ -582,7 +589,7 @@ export const Monad: Monad2<URI> = {
map: _map,
ap: _ap,
of,
chain: _chain
chain: flatMap
}

/**
Expand Down Expand Up @@ -869,7 +876,7 @@ export const ChainRec: ChainRec2<URI> = {
URI,
map: _map,
ap: _ap,
chain: _chain,
chain: flatMap,
chainRec: _chainRec
}

Expand All @@ -887,7 +894,7 @@ export const MonadThrow: MonadThrow2<URI> = {
map: _map,
ap: _ap,
of,
chain: _chain,
chain: flatMap,
throwError
}

Expand Down Expand Up @@ -1670,7 +1677,7 @@ export const either: Monad2<URI> &
map: _map,
of,
ap: _ap,
chain: _chain,
chain: flatMap,
reduce: _reduce,
foldMap: _foldMap,
reduceRight: _reduceRight,
Expand Down Expand Up @@ -1751,7 +1758,7 @@ export function getValidation<E>(
_E: undefined as any,
map: _map,
of,
chain: _chain,
chain: flatMap,
bimap: _bimap,
mapLeft: _mapLeft,
reduce: _reduce,
Expand Down
21 changes: 21 additions & 0 deletions src/function.ts
Original file line number Diff line number Diff line change
Expand Up @@ -768,3 +768,24 @@ export const getEndomorphismMonoid = <A = never>(): Monoid<Endomorphism<A>> => (
concat: (first, second) => flow(first, second),
empty: identity
})

/** @internal */
export const dual: {
<DataLast extends (...args: Array<any>) => any, DataFirst extends (...args: Array<any>) => any>(
arity: Parameters<DataFirst>['length'],
body: DataFirst
): DataLast & DataFirst
<DataLast extends (...args: Array<any>) => any, DataFirst extends (...args: Array<any>) => any>(
isDataFirst: (args: IArguments) => boolean,
body: DataFirst
): DataLast & DataFirst
} = (arity, body) => {
const isDataFirst: (args: IArguments) => boolean = typeof arity === 'number' ? (args) => args.length >= arity : arity
return function (this: any) {
const args = Array.from(arguments)
if (isDataFirst(arguments)) {
return body.apply(this, args)
}
return (self: any) => body(self, ...args)
}
}

0 comments on commit f817388

Please sign in to comment.