UNPKG

spica

Version:

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

220 lines (188 loc) 5.99 kB
import { Maybe, Just, Nothing } from './maybe'; import { Sequence } from './sequence'; describe('Unit: lib/maybe', () => { const Return = Maybe.Return; function throwError(msg: string): never { throw new Error(msg); } describe('Maybe', () => { it('Just', () => { const result = Return(0) .bind(n => Just(n + 1)) .bind(n => Just(n + 1).bind(n => Just(`Just ${n}`))) .extract(() => 'Nothing'); assert(result === 'Just 2'); }); it('Just nest', () => { const result = Return(Return(0)) .bind(m => Just(m)) .bind(m => m.bind(n => Just(n + 1)).bind(n => Just(`Just ${n}`))) .extract(() => 'Nothing'); assert(result === 'Just 1'); }); it('Nothing', () => { const result = Return(0) .bind(n => Just(n + 1)) .bind(n => Just(`Just ${n}`).bind(() => Nothing)) .bind(throwError) .extract(() => 'Nothing'); assert(result === 'Nothing'); try { Nothing.extract(); throw 1; } catch (reason) { assert(reason instanceof Error); } }); it('Nothing nest', () => { const result = Return(Return('')) .bind(m => m.bind(() => Nothing).bind(throwError)) .bind(throwError) .extract(() => 'Nothing'); assert(result === 'Nothing'); }); it('maybe', () => { assert(Just(0).extract(() => -1, n => n + 1) === 1); assert(Nothing.extract(() => -1, () => 0 + 1) === -1); }); }); describe('Functor', () => { it('fmap', () => { assert(Maybe.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( Maybe.pure((a: number) => a) .ap(Maybe.pure(1)) .extract(), 1); assert.strictEqual( Maybe.pure((a: number) => a) .ap(Nothing) .extract(() => 0), 0); assert.strictEqual( Maybe.pure(throwError) .ap(Nothing) .extract(() => 0), 0); }); it('ap 2', () => { assert.strictEqual( Maybe.pure((a: number, b: number) => a + b) .ap(Maybe.pure(1)) .ap(Maybe.pure(2)) .extract(), 3); }); it('ap 3', () => { assert.strictEqual( Maybe.pure((a: number, b: number, c: number) => a + b + c) .ap(Maybe.pure(1)) .ap(Maybe.pure(2)) .ap(Maybe.pure(3)) .extract(), 6); }); }); describe('Monad', () => { it('bind', () => { assert(Maybe.bind(n => Return(n + 1), Return(0)).extract() === 1); }); it('join', () => { assert(Return(Return(0)).join().extract() === 0); }); it('guard', () => { assert(Just(0).guard(true).extract(() => 1) === 0); assert(Just(0).guard(false).extract(() => 1) === 1); assert(Nothing.guard(true).extract(() => 1) === 1); assert(Nothing.guard(false).extract(() => 1) === 1); }); it('sequence', async () => { assert.deepStrictEqual(Maybe.sequence([Just(0), Just(1)]).extract(), [0, 1]); assert.deepStrictEqual(Maybe.sequence([Nothing, Just(1)]).extract(() => [] as number[]), []); assert(await Maybe.sequence(Just(Promise.resolve(0))).then(m => m.extract()) === 0); assert(await Maybe.sequence(Just(Promise.reject(1))).catch(b => b) === 1); assert(await Maybe.sequence(Nothing) === Nothing); }); 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()); }); }); describe('MonadPlus', () => { it('MonadPlus law 1', () => { assert(Maybe.mplus(Maybe.mzero, Return(0)).extract() === 0); }); it('MonadPlus law 2', () => { assert(Maybe.mplus(Return(0), Maybe.mzero).extract() === 0); }); it('MonadPlus law 3', () => { Sequence.from([1, 2, 4]) .mapM(n => Sequence.from<Maybe<number>>([Return(n), Maybe.mzero])) .extract() .forEach(([m, n, o]) => { const ma = Maybe.mplus(m, Maybe.mplus(n, o)); const mb = Maybe.mplus(Maybe.mplus(m, n), o); assert(ma.extract(() => -1) === mb.extract(() => -1)); }); }); it('MonadPlus law add 1', () => { const f = (n: number) => Return(n + 1); const ma = Maybe.mzero; const mb = Maybe.mzero.bind(f); assert(ma.extract(() => -1) === mb.extract(() => -1)); }); it('MonadPlus law add 2', () => { const k = (n: number) => Return(n * 10); Sequence.from([1, 2]) .mapM(n => Sequence.from<Maybe<number>>([Return(n), Maybe.mzero])) .extract() .forEach(([m, n]) => { const ma = Maybe.mplus(m, n).bind(k); const mb = Maybe.mplus(m.bind(k), n.bind(k)); assert(ma.extract(() => -1) === mb.extract(() => -1)); }); }); }); });