Skip to content

Commit

Permalink
Snake is now a function which returns Either.
Browse files Browse the repository at this point in the history
This is part of fix to #5.

+ Added new method to Snake interface: map
+ Added type alias SnakeFunc
+ Added new method liftSnake
+ Updated tfSnake method
+ Update snake method
+ Added new errorSnake method
- Removed compose method
  • Loading branch information
ILikePizza555 committed Mar 16, 2019
1 parent 98b043b commit 59c2640
Showing 1 changed file with 42 additions and 16 deletions.
58 changes: 42 additions & 16 deletions src/snake.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,63 @@
import { Observable, OperatorFunction, UnaryFunction } from "rxjs";
import { isNullOrUndefined } from "util";
import { Either, Right, Left } from "./Either";

/**
* A Snake is an OperatorFunction that can be chained with another OperatorFunction
* to produce a new Snake with a different return value.
* A Snake is a unique operator function which creates a disjoint stream. It is capable of composing itself with other
* OperatorFunctions through the use of `map` or other `Snakes` through the use of `chain`.
*
* Because snakes build individually on each other, this creates a type-safe method
* of passing in a series of operator functions.
*
* ... This is just a Functor, isn't it?
*/
export interface Snake<T, R> extends OperatorFunction<T, R> {
chain<N>(op: OperatorFunction<R, N>) : Snake<T, N>
export interface Snake<I, E, R> {
(o: Observable<I>): Either<Observable<E>, Observable<R>>

map<N>(op: OperatorFunction<R, N>) : Snake<I, E, N>
chain<N>(s: Snake<R, E, N>): Snake<I, E, N>
}

function compose<A, B, C>(f1: UnaryFunction<A, B>, f2: UnaryFunction<B, C>): UnaryFunction<A, C> {
return (a: A) => f2(f1(a));
export type SnakeFunc<I, E, R> = (i: Observable<I>) => Either<Observable<E>, Observable<R>>;

/**
* Lifts a normal function matching Snake to a real Snake.
*/
export function liftSnake<I, E, R>(f: SnakeFunc<I, E, R>): Snake<I, E, R> {
const rv: Snake<I, E, R> = (o: Observable<I>) => f(o);

rv.map = <N>(f: OperatorFunction<R, N>) =>
liftSnake((i: Observable<I>) => rv(i).map(f));

rv.chain = <N>(s: Snake<R, E, N>) =>
liftSnake((i: Observable<I>) => rv(i).chain(s));

return rv;
}

/**
* Takes an OperatorFunction and transforms it into a Snake.
* @param op
*/
export function tfSnake<T, R>(op: OperatorFunction<T, R>): Snake<T, R> {
const rv: Snake<T, R> = (o: Observable<T>) => op(o);
rv.chain = <N>(f: OperatorFunction<R, N>) => tfSnake(compose(rv, f));
export function tfSnake<I, E, R>(op: OperatorFunction<I, R>): Snake<I, E, R> {
const rv: Snake<I, E, R> = (o: Observable<I>) => new Right(op(o));

rv.map = <N>(f: OperatorFunction<R, N>) =>
liftSnake((i: Observable<I>) => rv(i).map(f));

rv.chain = <N>(s: Snake<R, E, N>) =>
liftSnake((i: Observable<I>) => rv(i).chain(s));

return rv;
}

/**
* Creates a new snake from the OperatorFunction. If no operator function is passed,
* then an "indentity Snake" is created.
* Creates a new "identity snake".
*/
export function snake<I, E, R>(r: Observable<R>): Snake<I, E, R> {
return liftSnake(() => new Right(r));
}

/**
* Creates a new "error snake".
*/
export function snake<T, R = T>(op: OperatorFunction<T, R|T> = (t: Observable<T>) => t): Snake<T, R|T> {
return tfSnake(op);
export function errorSnake<I, E, R>(e: Observable<E>): Snake<I, E, R> {
return liftSnake(() => new Left(e));
}

0 comments on commit 59c2640

Please sign in to comment.