UNPKG

spica

Version:

Supervisor, Coroutine, Channel, select, AtomicPromise, Cancellation, Cache, List, Queue, Stack, and some utils.

174 lines (148 loc) 4.77 kB
import { Either, Left, Right } from './either'; describe('Unit: lib/either', () => { const Return = Either.Return; function throwError(msg: string): never { throw new Error(msg); } describe('Either', () => { it('Left', () => { const result = Return(0) .bind(n => Right(n + 1)) .bind(n => Right(n + 1).bind(n => Left(`Left ${n}`))) .bind(n => Right(`Right ${n}`)) .extract(l => `Left ${l}`); assert(result === 'Left Left 2'); try { Left(0).extract(); throw 1; } catch (reason) { assert(reason === 0); } }); it('Left nest', () => { const result = Return(Return('')) .bind(m => m.bind(() => Left(NaN)).bind(throwError)) .bind(throwError) .extract(() => 'Nothing'); assert(result === 'Nothing'); }); it('Right', () => { const result = Return(0) .bind(n => Right(n + 1)) .bind(n => Right(n + 1).bind(n => Right(`Right ${n}`))) .extract(l => `Left ${l}`); assert(result === 'Right 2'); }); it('Right nest', () => { const result = Return(Return(0)) .bind(m => Right(m)) .bind(m => m.bind(n => Right(n + 1)).bind(n => Right(`Right ${n}`))) .extract(() => 'Nothing'); assert(result === 'Right 1'); }); it('Either', () => { const result = Return(0) .bind<number, number>(n => <Right<number> | Left<number> | Either<number, number>>Right(n).bind(n => <Right<number> | Left<number>>Right(n) || Left(0))) .bind(n => <Right<number> | Left<number> | Either<number, number>>Right(n).bind(n => <Right<number> | Left<number>>Right(n) || Left(0))) .extract(n => n + ''); assert(result === 0); }); it('either', () => { assert(Right(0).extract(() => -1, n => n + 1) === 1); assert(Left(0).extract(n => n -1, () => 0 + 1) === -1); }); }); describe('Functor', () => { it('fmap', () => { assert(Either.fmap(n => n + 1, Return(0)).extract() === 1); }); it('Functor law 1', () => { const f = <T>(n: T) => n; const x = 0; const fa = Return(x).fmap(f); const fb = f(Return(x)); assert(fa.extract() === fb.extract()); }); it('Functor law 2', () => { const f = (n: number) => n + 2; const g = (n: number) => n * 3; const x = 1; const fa = Return(x).fmap(n => g(f(n))); const fb = Return(x).fmap(f).fmap(g); assert(fa.extract() === fb.extract()); }); }); describe('Applicative', () => { it('ap 1', () => { assert.strictEqual( Either.pure((a: number) => a) .ap(Either.pure(1)) .extract(), 1); assert.strictEqual( Either.pure<number, typeof throwError>(throwError) .ap(Left(0)) .extract(n => n), 0); }); it('ap 2', () => { assert.strictEqual( Either.pure((a: number, b: number) => a + b) .ap(Either.pure(1)) .ap(Either.pure(2)) .extract(), 3); }); it('ap 3', () => { assert.strictEqual( Either.pure((a: number, b: number, c: number) => a + b + c) .ap(Either.pure(1)) .ap(Either.pure(2)) .ap(Either.pure(3)) .extract(), 6); }); }); describe('Monad', () => { it('bind', () => { assert(Either.bind(n => Return(n + 1), Return(0)).extract() === 1); }); it('join', () => { assert(Return(Return(0)).join().extract() === 0); }); it('sequence', async () => { assert.deepStrictEqual(Either.sequence([Right(0), Right(1)]).extract(), [0, 1]); assert.deepStrictEqual(Either.sequence([Left([]), Right(1)]).extract(a => a), []); assert(await Either.sequence(Right(Promise.resolve(0))).then(m => m.extract()) === 0); assert(await Either.sequence(Right(Promise.reject(1))).catch(a => a) === 1); assert(await Either.sequence(Left(1)).then(m => m.extract(a => a)) === 1); }); it('Monad law 1', () => { const f = (n: number) => Return(n + 1); const x = 0; const ma = Return(x).bind(f); const mb = f(x); assert(ma.extract() === mb.extract()); }); it('Monad law 2', () => { const x = 0; const ma = Return(x); const mb = ma.bind(Return); assert(ma.extract() === mb.extract()); }); it('Monad law 3', () => { const f = (n: number) => Return(n + 2); const g = (n: number) => Return(n * 3); const x = 1; const ma = Return(x) .bind(f) .bind(g); const mb = Return(x) .bind(x => f(x) .bind(g)); assert(ma.extract() === mb.extract()); }); }); });