From 36b5248618f6efe06a633882baead089df042ca5 Mon Sep 17 00:00:00 2001 From: Brandon Blaylock Date: Sat, 10 Apr 2021 13:20:24 -0700 Subject: [PATCH] feat: io tests complete --- derivations.ts | 28 +++++++++---- io.ts | 13 ++---- testing/io.test.ts | 99 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 124 insertions(+), 16 deletions(-) create mode 100644 testing/io.test.ts diff --git a/derivations.ts b/derivations.ts index c01a502..3620bcd 100644 --- a/derivations.ts +++ b/derivations.ts @@ -36,20 +36,34 @@ export const createDo = ( name: N, ): (( ta: Kind, - // deno-lint-ignore no-explicit-any ) => Kind) => + // deno-lint-ignore no-explicit-any M.map((a: any): any => ({ [name]: a })), bind: ( name: Exclude, fati: (a: A) => Kind, - ): (( - ma: Kind, - ) => Kind< - URI, - [{ readonly [K in keyof A | N]: K extends keyof A ? A[K] : I }, B, C, D] - > // deno-lint-ignore no-explicit-any + ): ( + ( + ma: Kind, + ) => Kind< + URI, + [{ readonly [K in keyof A | N]: K extends keyof A ? A[K] : I }, B, C, D] + > ) => + // deno-lint-ignore no-explicit-any M.chain((a: any): any => + // deno-lint-ignore no-explicit-any pipe(a, fati, M.map((b: any): any => ({ ...a, [name]: b }))) ), }); + +/******************************************************************************* + * Derive getSemigroup from Apply + ******************************************************************************/ + +export const createApplySemigroup = (A: TC.Apply) => + ( + S: TC.Semigroup, + ): TC.Semigroup> => ({ + concat: (a) => A.ap(pipe(a, A.map(S.concat))), + }); diff --git a/io.ts b/io.ts index f74ecc1..32bcb70 100644 --- a/io.ts +++ b/io.ts @@ -3,7 +3,7 @@ import type * as TC from "./type_classes.ts"; import { createSequenceStruct, createSequenceTuple } from "./sequence.ts"; import { apply, constant, flow, pipe } from "./fns.ts"; -import { createDo } from "./derivations.ts"; +import { createApplySemigroup, createDo } from "./derivations.ts"; /******************************************************************************* * Types @@ -59,11 +59,6 @@ export const Monad: TC.Monad = { chain: Chain.chain, }; -export const Alt: TC.Alt = { - alt: constant, - map: Monad.map, -}; - export const Extends: TC.Extend = { map: Monad.map, extend: (ftab) => (ta) => () => ftab(ta), @@ -83,9 +78,7 @@ export const Traversable: TC.Traversable = { * Module Getters ******************************************************************************/ -export const getSemigroup = (S: TC.Semigroup): TC.Semigroup> => ({ - concat: (x) => (y) => () => S.concat(x())(y()), -}); +export const getSemigroup = createApplySemigroup(Apply); export const getMonoid = (M: TC.Monoid): TC.Monoid> => ({ ...getSemigroup(M), @@ -100,6 +93,8 @@ export const { of, ap, map, join, chain } = Monad; export const { reduce, traverse } = Traversable; +export const { extend } = Extends; + /******************************************************************************* * Sequenec ******************************************************************************/ diff --git a/testing/io.test.ts b/testing/io.test.ts new file mode 100644 index 0000000..aeca676 --- /dev/null +++ b/testing/io.test.ts @@ -0,0 +1,99 @@ +import { assertEquals } from "https://deno.land/std/testing/asserts.ts"; + +import * as AS from "./assert.ts"; + +import * as I from "../io.ts"; +import * as O from "../option.ts"; +import { semigroupSum } from "../semigroup.ts"; +import { monoidSum } from "../monoid.ts"; +import { pipe } from "../fns.ts"; + +// deno-lint-ignore no-explicit-any +const assertEqualsIO = (a: I.IO, b: I.IO) => assertEquals(a(), b()); + +Deno.test("IO getSemigroup", () => { + const Semigroup = I.getSemigroup(semigroupSum); + const concat = Semigroup.concat(I.of(1)); + + assertEqualsIO(concat(I.of(1)), I.of(2)); +}); + +Deno.test("IO getMonoid", () => { + const Monoid = I.getMonoid(monoidSum); + const empty = Monoid.empty(); + + assertEqualsIO(empty, I.of(0)); +}); + +Deno.test("IO of", () => { + assertEqualsIO(I.of(1), I.of(1)); +}); + +Deno.test("IO ap", () => { + const ap = I.ap(I.of(AS.add)); + assertEqualsIO(ap(I.of(1)), I.of(2)); +}); + +Deno.test("IO map", () => { + const map = I.map(AS.add); + assertEqualsIO(map(I.of(1)), I.of(2)); +}); + +Deno.test("IO join", () => { + assertEqualsIO(I.join(I.of(I.of(1))), I.of(1)); +}); + +Deno.test("IO chain", () => { + const chain = I.chain((n: number) => I.of(n + 1)); + assertEqualsIO(chain(I.of(1)), I.of(2)); +}); + +Deno.test("IO reduce", () => { + const reduce = I.reduce((acc: number, cur: number) => acc + cur, 0); + assertEquals(reduce(I.of(1)), 1); +}); + +Deno.test("IO traverse", () => { + const fold = O.fold((n: I.IO) => n(), () => -1); + const t0 = I.traverse(O.Applicative); + const t1 = t0((n: number) => n === 0 ? O.none : O.some(n)); + const t2 = fold(t1(I.of(0))); + const t3 = fold(t1(I.of(1))); + + assertEquals(t2, -1); + assertEquals(t3, 1); +}); + +Deno.test("IO extend", () => { + const extend = I.extend((ta: I.IO) => ta() + 1); + assertEqualsIO(extend(I.of(1)), I.of(2)); +}); + +Deno.test("IO sequenceTuple", () => { + const r1 = I.sequenceTuple(I.of(1), I.of("Hello World")); + assertEqualsIO(r1, I.of([1, "Hello World"])); +}); + +Deno.test("IO sequenceStruct", () => { + const r1 = I.sequenceStruct({ a: I.of(1), b: I.of("Hello World") }); + assertEqualsIO(r1, I.of({ a: 1, b: "Hello World" })); +}); + +Deno.test("IO Do, bind, bindTo", () => { + assertEqualsIO( + pipe( + I.Do(), + I.bind("one", () => I.of(1)), + I.bind("two", ({ one }) => I.of(one + one)), + I.map(({ one, two }) => one + two), + ), + I.of(3), + ); + assertEqualsIO( + pipe( + I.of(1), + I.bindTo("one"), + ), + I.of({ one: 1 }), + ); +});