xstream icon indicating copy to clipboard operation
xstream copied to clipboard

Static Land style API

Open DylanRJohnston opened this issue 5 years ago • 2 comments

Hey guys, [email protected] moved away from a method chaining API to a more static land style API which means it doesn't gel very well with xstream anymore. Would you be opposed to exporting all of operator methods as static functions curried in their stream argument? Right now I have a little helper file that re-exports methods as needed.

e.g.

export const map = <A, B>(f: (a: A) => B)) => ($: Stream<A>): Stream<B> => $.map(F)

DylanRJohnston avatar Nov 01 '19 02:11 DylanRJohnston

I'm not sure how to get this to work with operators that work on Streams and MemoryStreams. I see a few different options.

  1. Namespace the operators, something like mapMemory or maybe have them as seperate modules so they can imported as import xs from 'xstream/static' and import xsm from 'xstream/static/memory or something like that.
  2. Use the higher kinded types method developed by fp-ts, but it's pretty difficult to understand for new users and would probably tie these two libraries too close together (Maybe this belongs as a seperate package?)
  3. Use a conditional type for the return. This is in a sense copying the higher kinded type method from fp-ts, but for a known finite domain of Functors. Something like this
type AnyStream<A> = Stream<A> | MemoryStream<A>
type StreamOf<S extends AnyStream<unknown>, B>
  = S extends MemoryStream<unknown> ? MemoryStream<B>
  : S extends Stream<unknown> ? Stream<B>
  : unknown

export const map = <A, B>(f: (a: A) => B) => <S extends Stream<A>>($: S): StreamOf<S, B> => ($.map as any)(f)

I think 3 should work fine given Stream<A> is invariant in A.

DylanRJohnston avatar Nov 01 '19 03:11 DylanRJohnston

Just realised Typescript already has pattern matching for types, function overloads!

type AnyStream<A> = Stream<A> | MemoryStream<A>

interface StreamOperator<A, B = A> {
  ($: MemoryStream<A>): MemoryStream<B>
  ($: Stream<A>): Stream<B>
}

export const map = <A, B>(f: (a: A) => B): StreamOperator<A, B> => ($: AnyStream<A>) =>
  ($ as any).map(f)

DylanRJohnston avatar Nov 01 '19 03:11 DylanRJohnston