/** * Fantasy-land Algebraic Data Type Compatability. * Cell satisfies the Functor, Apply, and Applicative categories * @see {@link https://github.com/fantasyland/fantasy-land} for more info * @see {@link https://github.com/sanctuary-js/sanctuary/blob/master/test/Maybe/Maybe.js} for valid test examples (Sanctuary's Maybe) */ import * as jsc from 'jsverify'; import { S} from "../test-utils/Sanctuary"; import { Cell, StreamSink, Stream, Transaction} from '../../lib/Lib'; import * as laws from 'fantasy-laws'; import { testSequence } from '../test-utils/Sequence'; /* * Cell */ //would be nice if we could push all samples off to listeners... like in Fantasy-land Practical Tests below, but for unit testing it's okay function CellArb(arb: jsc.Arbitrary) { return arb.smap(x => new Cell(x), x => x.sample()); } function CellEq(a: Cell, b: Cell) { return a.sample() === b.sample(); } function CellHead(x: string): Cell { const head = S.head(x); return new Cell(head.isNothing ? "" : head.value); } function CellParseInt(radix: number): ((x: number) => Cell) { return function (x: number) { const m = S.parseInt(radix)(x); return new Cell(m.isNothing ? 0 : m.value); }; } test('Cell - Functor Laws', () => { const testLaws = laws.Functor(CellEq); testLaws.identity( CellArb(jsc.number) ); testLaws.composition( CellArb(jsc.number), jsc.constant(Math.sqrt), jsc.constant(Math.abs) ); }); test('Apply Laws', () => { const testLaws = laws.Apply(CellEq); testLaws.composition( CellArb(jsc.constant(Math.sqrt)), CellArb(jsc.constant(Math.abs)), CellArb(jsc.number) ); }); test('Appplicative Laws', () => { const testLaws = laws.Applicative(CellEq, Cell); testLaws.identity( CellArb(jsc.number) ); testLaws.homomorphism( jsc.constant(Math.abs), jsc.number ); testLaws.interchange( CellArb(jsc.constant(Math.abs)), jsc.number ); }); test('Lift', (done) => { const addFunctors = S.lift2(S.add); const cResult = addFunctors(new Cell(2)) (new Cell(3)); const kill = cResult.listen((n: number) => { expect(n).toBe(5); done(); }); kill(); }); test('Sequence', (done) => { testSequence (S.sequence(Cell)) (done) }); test('Concat', (done) => { const s1 = new StreamSink>(); const s2 = new StreamSink>(); const s3 = S.concat(s1) (s2); const kill = s3.listen((n: Array) => { expect(n).toEqual([5, 3, 42]); done(); }); Transaction.run(() => { s1.send([5]); s2.send([3, 42]); }) kill(); }); /* Stream describe('Fantasy-land Stream', () => { /* TODO: figure out right way to define arb and equality here If a solution is found that uses listen(), consider porting Cell to that approach as well. function StreamArb(arb: jsc.Arbitrary) { return arb.smap(x => { const sink = new StreamSink(); sink.listen(() => {}); sink.send(x); return sink; }, x => x.hold(undefined).sample()); } function StreamEq(a: Stream, b: Stream) { console.log(a.hold(undefined).sample()); return a.hold(undefined).sample() === b.hold(undefined).sample(); } describe('Functor Laws', () => { const testLaws = laws.Functor(StreamEq); it('Identity', testLaws.identity( StreamArb(jsc.number) )); it('Composition', testLaws.composition( StreamArb(jsc.number), jsc.constant(Math.sqrt), jsc.constant(Math.abs) )); }); */