From 9648e08f45f24366500c93c855b10cdbe61138d9 Mon Sep 17 00:00:00 2001 From: Wayne Van Son Date: Tue, 20 Oct 2020 15:45:54 +0000 Subject: [PATCH] feat: adds Do syntax for modules All modules now export bindTo and Bind --- package-lock.json | 2 +- src/Observable.ts | 24 ++++++++++++++++++++ src/ObservableEither.ts | 28 +++++++++++++++++++++++- src/ReaderObservable.ts | 28 +++++++++++++++++++++++- src/ReaderObservableEither.ts | 32 ++++++++++++++++++++++++++- src/StateReaderObservableEither.ts | 34 +++++++++++++++++++++++++++++ test/Observable.ts | 13 +++++++++++ test/ObservableEither.ts | 12 ++++++++++ test/ReaderObservable.ts | 12 ++++++++++ test/ReaderObservableEither.ts | 13 +++++++++++ test/StateReaderObservableEither.ts | 13 +++++++++++ 11 files changed, 207 insertions(+), 4 deletions(-) diff --git a/package-lock.json b/package-lock.json index fea69b8..762a448 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "fp-ts-rxjs", - "version": "0.6.9", + "version": "0.6.10", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/src/Observable.ts b/src/Observable.ts index 553aeca..ac6ff9e 100644 --- a/src/Observable.ts +++ b/src/Observable.ts @@ -185,3 +185,27 @@ export { */ separate } + +/** + * @category Do + * @since 0.6.11 + */ +export function bindTo(name: K): (fa: Observable) => Observable<{ [P in K]: A }> { + return map(a => ({ [name]: a } as { [P in K]: A })) +} + +/** + * @category Do + * @since 0.6.11 + */ +export function bind( + name: Exclude, + f: (a: A) => Observable +): (fa: Observable) => Observable<{ [P in keyof A | K]: P extends keyof A ? A[P] : B }> { + return chain(a => + pipe( + f(a), + map(b => ({ ...a, [name]: b } as any)) + ) + ) +} diff --git a/src/ObservableEither.ts b/src/ObservableEither.ts index 549b46f..d107d23 100644 --- a/src/ObservableEither.ts +++ b/src/ObservableEither.ts @@ -14,7 +14,7 @@ import { MonadObservable2 } from './MonadObservable' import { Observable } from 'rxjs' import { Task } from 'fp-ts/lib/Task' import { getEitherM } from 'fp-ts/lib/EitherT' -import { pipeable } from 'fp-ts/lib/pipeable' +import { pipe, pipeable } from 'fp-ts/lib/pipeable' const T = getEitherM(R.observable) @@ -191,3 +191,29 @@ export { */ mapLeft } + +/** + * @category Do + * @since 0.6.11 + */ +export function bindTo( + name: K +): (fa: ObservableEither) => ObservableEither { + return map(a => ({ [name]: a } as { [P in K]: A })) +} + +/** + * @category Do + * @since 0.6.11 + */ +export function bind( + name: Exclude, + f: (a: A) => ObservableEither +): (fa: ObservableEither) => ObservableEither { + return chain(a => + pipe( + f(a), + map(b => ({ ...a, [name]: b } as any)) + ) + ) +} diff --git a/src/ReaderObservable.ts b/src/ReaderObservable.ts index 2bf48fe..9c6581b 100644 --- a/src/ReaderObservable.ts +++ b/src/ReaderObservable.ts @@ -4,7 +4,7 @@ import { Observable } from 'rxjs' import { getReaderM } from 'fp-ts/lib/ReaderT' import * as R from './Observable' -import { pipeable } from 'fp-ts/lib/pipeable' +import { pipe, pipeable } from 'fp-ts/lib/pipeable' import { IO } from 'fp-ts/lib/IO' import { Monad2 } from 'fp-ts/lib/Monad' import { Monoid } from 'fp-ts/lib/Monoid' @@ -258,3 +258,29 @@ export { */ separate } + +/** + * @category Do + * @since 0.6.11 + */ +export function bindTo( + name: K +): (fa: ReaderObservable) => ReaderObservable { + return map(a => ({ [name]: a } as { [P in K]: A })) +} + +/** + * @category Do + * @since 0.6.11 + */ +export function bind( + name: Exclude, + f: (a: A) => ReaderObservable +): (fa: ReaderObservable) => ReaderObservable { + return chain(a => + pipe( + f(a), + map(b => ({ ...a, [name]: b } as any)) + ) + ) +} diff --git a/src/ReaderObservableEither.ts b/src/ReaderObservableEither.ts index 2df4cdb..1b1eea1 100644 --- a/src/ReaderObservableEither.ts +++ b/src/ReaderObservableEither.ts @@ -7,7 +7,7 @@ import { MonadObservable3 } from './MonadObservable' import { MonadThrow3 } from 'fp-ts/lib/MonadThrow' import { Bifunctor3 } from 'fp-ts/lib/Bifunctor' import { getReaderM } from 'fp-ts/lib/ReaderT' -import { pipeable } from 'fp-ts/lib/pipeable' +import { pipe, pipeable } from 'fp-ts/lib/pipeable' import { Observable } from 'rxjs' /** @@ -194,3 +194,33 @@ export { */ mapLeft } + +// DO + +/** + * @category Do + * @since 0.6.11 + */ +export function bindTo( + name: K +): (fa: ReaderObservableEither) => ReaderObservableEither { + return map(a => ({ [name]: a } as { [P in K]: A })) +} + +/** + * @category Do + * @since 0.6.11 + */ +export function bind( + name: Exclude, + f: (a: A) => ReaderObservableEither +): ( + fa: ReaderObservableEither +) => ReaderObservableEither { + return chain(a => + pipe( + f(a), + map(b => ({ ...a, [name]: b } as any)) + ) + ) +} diff --git a/src/StateReaderObservableEither.ts b/src/StateReaderObservableEither.ts index f7cfcb1..d8788a5 100644 --- a/src/StateReaderObservableEither.ts +++ b/src/StateReaderObservableEither.ts @@ -217,3 +217,37 @@ export { */ mapLeft } + +// DO SYNTAX + +/** + * @category Do + * @since 0.6.11 + */ +export function bindTo( + name: K +): (fa: StateReaderObservableEither) => StateReaderObservableEither { + return fa => + pipe( + fa, + map(value => ({ [name]: value } as any)) + ) +} + +/** + * @category Do + * @since 0.6.11 + */ +export function bind( + name: Exclude, + f: (a: A) => StateReaderObservableEither +): ( + fa: StateReaderObservableEither +) => StateReaderObservableEither { + return chain(a => + pipe( + f(a), + map(b => ({ ...a, [name]: b } as any)) + ) + ) +} diff --git a/test/Observable.ts b/test/Observable.ts index 1afbb73..41f03bb 100644 --- a/test/Observable.ts +++ b/test/Observable.ts @@ -7,6 +7,7 @@ import * as T from 'fp-ts/lib/Task' import { identity } from 'fp-ts/lib/function' import { observable as R } from '../src' +import { pipe } from 'fp-ts/lib/pipeable' describe('Observable', () => { it('of', () => { @@ -206,4 +207,16 @@ describe('Observable', () => { const t = await R.toTask(R.of(1))() assert.deepStrictEqual(t, 1) }) + + it('do notation', async () => { + const t = await pipe( + R.of(1), + R.bindTo('a'), + R.bind('b', () => R.of('b')) + ) + .pipe(bufferTime(10)) + .toPromise() + + assert.deepStrictEqual(t, [{ a: 1, b: 'b' }]) + }) }) diff --git a/test/ObservableEither.ts b/test/ObservableEither.ts index f960d29..6a5db10 100644 --- a/test/ObservableEither.ts +++ b/test/ObservableEither.ts @@ -255,4 +255,16 @@ describe('ObservableEither', () => { assert.deepStrictEqual(e1, e2) }) }) + + it('do notation', async () => { + const t = await pipe( + _.right(1), + _.bindTo('a'), + _.bind('b', () => _.right('b')) + ) + .pipe(bufferTime(10)) + .toPromise() + + assert.deepStrictEqual(t, [E.right({ a: 1, b: 'b' })]) + }) }) diff --git a/test/ReaderObservable.ts b/test/ReaderObservable.ts index 70ec6f0..6c5158d 100644 --- a/test/ReaderObservable.ts +++ b/test/ReaderObservable.ts @@ -298,4 +298,16 @@ describe('ReaderObservable', () => { const x = await _.run(pipe(_.of('a'), _.chainTaskK(f)), undefined) assert.deepStrictEqual(x, 1) }) + + it('do notation', async () => { + const t = await pipe( + _.of(1), + _.bindTo('a'), + _.bind('b', () => _.of('b')) + )(undefined) + .pipe(bufferTime(10)) + .toPromise() + + assert.deepStrictEqual(t, [{ a: 1, b: 'b' }]) + }) }) diff --git a/test/ReaderObservableEither.ts b/test/ReaderObservableEither.ts index 8dfb703..475fc68 100644 --- a/test/ReaderObservableEither.ts +++ b/test/ReaderObservableEither.ts @@ -125,4 +125,17 @@ describe('ReaderObservable', () => { const x = await robe({})() assert.deepStrictEqual(x, [E.right(1)]) }) + + // robe should expose right + it('do notation', async () => { + const t = await pipe( + ROBE.of(1), + ROBE.bindTo('a'), + ROBE.bind('b', () => ROBE.of('b')) + )(undefined) + .pipe(bufferTime(10)) + .toPromise() + + assert.deepStrictEqual(t, [E.right({ a: 1, b: 'b' })]) + }) }) diff --git a/test/StateReaderObservableEither.ts b/test/StateReaderObservableEither.ts index 47e3f25..d29fe32 100644 --- a/test/StateReaderObservableEither.ts +++ b/test/StateReaderObservableEither.ts @@ -82,4 +82,17 @@ describe('stateReaderObservableEither', () => { const x = await srobe(2)() assert.deepStrictEqual(x, [E.right(1)]) }) + + // should expose of + it('do notation', async () => { + const srobe = pipe( + SROBE.right(1), + SROBE.bindTo('a'), + SROBE.bind('b', () => SROBE.right('b')), + buffer + ) + + const x = await srobe('state')('reader')() + assert.deepStrictEqual(x, [E.right([{ a: 1, b: 'b' }, 'state'])]) + }) })