gcanti / fp-ts-rxjs

fp-ts bindings for RxJS
https://gcanti.github.io/fp-ts-rxjs/
MIT License
188 stars 29 forks source link

Add `ObservableOption` #17

Closed OliverJAsh closed 3 years ago

OliverJAsh commented 4 years ago

I suspect these are pretty common compositions, so it might be good if we could add them to this library?

Here's ObservableOption: https://functionalprogramming.slack.com/archives/C7BBY9A95/p1574023884170200?thread_ts=1574019476.167400&cid=C7BBY9A95

/cc @giogonzo

mlegenhausen commented 4 years ago

I will will provide a PR for ObservableEither and ReaderObservableEither

OliverJAsh commented 4 years ago

Looks like we have ObservableEither now https://github.com/gcanti/fp-ts-rxjs/blob/master/src/ObservableEither.ts

mlegenhausen commented 4 years ago

@OliverJAsh here is a quick write up analog to TaskOption. Feel free to contribute:

/**
 * @since 0.6.10
 */
import { Option } from 'fp-ts/lib/Option'
import * as R from './Observable'
import { Alt1 } from 'fp-ts/lib/Alt'
import { Monad1 } from 'fp-ts/lib/Monad'
import { MonadObservable1 } from './MonadObservable'
import { Observable } from 'rxjs'
import { Task } from 'fp-ts/lib/Task'
import { getOptionM } from 'fp-ts/lib/OptionT'
import { pipeable } from 'fp-ts/lib/pipeable'
import { IO } from 'fp-ts/lib/IO'

const T = getOptionM(R.observable)

declare module 'fp-ts/lib/HKT' {
  interface URItoKind<A> {
    ObservableOption: ObservableOption<A>
  }
}

/**
 * @since 0.6.10
 */
export const URI = 'ObservableOption'
/**
 * @since 0.6.10
 */
export type URI = typeof URI

/**
 * @since 0.6.10
 */
export interface ObservableOption<A> extends Observable<Option<A>> {}

/**
 * @since 0.6.10
 */
export const fromObservable: <A = never>(ma: Observable<A>) => ObservableOption<A> = T.fromM

/**
 * @since 0.6.10
 */
export const fromOption: <A>(fa: Option<A>) => ObservableOption<A> = R.of

/**
 * @since 0.6.10
 */
export const none: ObservableOption<never> = T.none()

/**
 * @since 0.6.10
 */
export const some: <A>(a: A) => ObservableOption<A> = T.of

/**
 * @since 0.6.10
 */
export function fromTaskOption<A>(t: Task<Option<A>>): ObservableOption<A> {
  return R.fromTask(t)
}

/**
 * @since 0.6.10
 */
export function toTaskOption<A>(o: ObservableOption<A>): Task<Option<A>> {
  return () => o.toPromise()
}

/**
 * @since 0.6.10
 */
export function fromTask<A>(ma: Task<A>): ObservableOption<A> {
  return fromObservable(R.fromTask(ma))
}

/**
 * @since 0.6.10
 */
export function fromIO<A>(ma: IO<A>): ObservableOption<A> {
  return fromObservable(R.fromIO(ma))
}

/**
 * @since 0.6.10
 */
export function fold<A, B>(
  onNone: () => Observable<B>,
  onRight: (a: A) => Observable<B>
): (ma: ObservableOption<A>) => Observable<B> {
  return ma => T.fold(ma, onNone, onRight)
}

/**
 * @since 0.6.10
 */
export function getOrElse<A>(onNone: () => Observable<A>): (ma: ObservableOption<A>) => Observable<A> {
  return ma => T.getOrElse(ma, onNone)
}

/**
 * @since 0.6.10
 */
export const observableOption: Monad1<URI> & Alt1<URI> & MonadObservable1<URI> = {
  URI,
  map: T.map,
  of: T.of,
  ap: T.ap,
  chain: T.chain,
  alt: T.alt,
  fromObservable,
  fromTask,
  fromIO
}

const { alt, ap, apFirst, apSecond, chain, chainFirst, flatten, map } = pipeable(observableOption)

export {
  /**
   * @since 0.6.10
   */
  alt,
  /**
   * @since 0.6.10
   */
  ap,
  /**
   * @since 0.6.10
   */
  apFirst,
  /**
   * @since 0.6.10
   */
  apSecond,
  /**
   * @since 0.6.10
   */
  chain,
  /**
   * @since 0.6.10
   */
  chainFirst,
  /**
   * @since 0.6.10
   */
  flatten,
  /**
   * @since 0.6.10
   */
  map
}
OliverJAsh commented 3 years ago

https://github.com/gcanti/fp-ts-rxjs/pull/59