Skip to content
This repository has been archived by the owner on May 3, 2021. It is now read-only.

Commit

Permalink
feat: task adt and reorganize
Browse files Browse the repository at this point in the history
* fix type only imports so deno test --coverage --unstable passes
* move derivation functions into their own file derivations.ts
* simplify derivation types so there is no need for duplicates
* update imports in either.ts
* update imports in option.ts
* simplify types for getOptionM in optionT.ts and remove dups
* prefix all exports in sequence.ts with _ denoting unstable
* initial implementation of task adt
* simplify types for typeclass law asserts and rename file to assert.ts
* update tests that use typeclass asserts
* implement initial task tests
  • Loading branch information
baetheus committed Sep 15, 2020
1 parent 497d7fc commit 79e762e
Show file tree
Hide file tree
Showing 14 changed files with 353 additions and 159 deletions.
4 changes: 2 additions & 2 deletions composition.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { $ } from "./hkts.ts";
import * as TC from "./type-classes.ts";
import type { $ } from "./hkts.ts";
import type * as TC from "./type-classes.ts";

/***************************************************************************************************
* @section Composition Modules
Expand Down
67 changes: 67 additions & 0 deletions derivations.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import type * as TC from "./type-classes.ts";
import { identity } from "./fns.ts";

/***************************************************************************************************
* @section Derivations
**************************************************************************************************/

/**
* Derive Monad from of, map, and join.
*/
export function createMonad<T>({
of,
chain,
}: Pick<TC.Monad<T>, "of" | "chain">): TC.Monad<T> {
const map: TC.Functor<T>["map"] = (fab, ta) => chain((a) => of(fab(a)), ta);
return {
of,
map,
chain,
join: (tta) => chain(identity, tta),
ap: (tfab, ta) => chain((f) => map(f, ta), tfab),
};
}

/**
* Derive Monad2 from of, map, and join.
*/
export function createMonad2<T>(
M: Pick<TC.Monad2<T>, "of" | "chain">
): TC.Monad2<T> {
return createMonad<T>(M as TC.Monad<T>) as TC.Monad2<T>;
}

/**
* Derive MonadP from Monad or Monad2.
*/
export const createPipeableMonad: {
<T>(M: TC.Monad<T>): TC.MonadP<T>;
<T>(M: TC.Monad2<T>): TC.Monad2P<T>;
} = <T>({ of, ap, map, join, chain }: TC.Monad<T>): TC.MonadP<T> => ({
of,
join,
map: (fab) => (ta) => map(fab, ta),
chain: (fatb) => (ta) => chain(fatb, ta),
ap: (tfab) => (ta) => ap(tfab, ta),
});

/**
* Derive TraversableP from Traversable or Traversable2.
*/
export const createPipeableTraversable: {
<T>(M: TC.Traversable<T>): TC.TraversableP<T>;
<T>(M: TC.Traversable2<T>): TC.Traversable2P<T>;
} = <T>({ traverse, reduce, map }: TC.Traversable<T>): TC.TraversableP<T> => ({
map: (fab) => (ta) => map(fab, ta),
reduce: (faba, a) => (ta) => reduce(faba, a, ta),
traverse: (A, faub) => (ta) => traverse(A, faub, ta),
});

/**
* Derive BifunctorP from Bifunctor.
*/
export const createPipeableBifunctor = <T>({
bimap,
}: TC.Bifunctor<T>): TC.BifunctorP<T> => ({
bimap: (fab, fcd) => (tac) => bimap(fab, fcd, tac),
});
19 changes: 13 additions & 6 deletions either.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import * as TC from "./type-classes.ts";
import { _0, _1 } from "./hkts.ts";
import type * as TC from "./type-classes.ts";
import type { _0, _1 } from "./hkts.ts";

import { isNotNil, Lazy, Predicate, Refinement } from "./fns.ts";
import {
createMonad2,
createPipeableBifunctor,
createPipeableMonad,
createPipeableTraversable,
} from "./derivations.ts";

/***************************************************************************************************
* @section Types
Expand Down Expand Up @@ -95,7 +102,7 @@ export const Foldable: TC.Foldable2<Either<_0, _1>> = {
reduce: (faba, a, tb) => (isRight(tb) ? faba(a, tb.right) : a),
};

export const Monad = TC.createMonad2<Either<_0, _1>>({
export const Monad = createMonad2<Either<_0, _1>>({
of: right,
chain: (fatb, ta) => (isRight(ta) ? fatb(ta.right) : ta),
});
Expand Down Expand Up @@ -127,8 +134,8 @@ export const Bifunctor: TC.Bifunctor<Either<_0, _1>> = {
* @section Pipeables
**************************************************************************************************/

export const { of, ap, map, join, chain } = TC.createPipeableMonad2(Monad);
export const { of, ap, map, join, chain } = createPipeableMonad(Monad);

export const { reduce, traverse } = TC.createPipeableTraversable2(Traversable);
export const { reduce, traverse } = createPipeableTraversable(Traversable);

export const { bimap } = TC.createPipeableBifunctor(Bifunctor);
export const { bimap } = createPipeableBifunctor(Bifunctor);
28 changes: 17 additions & 11 deletions option.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
import type * as TC from "./type-classes.ts";
import type { _ } from "./hkts.ts";

import { isNotNil, Lazy, Predicate } from "./fns.ts";
import * as SL from "./type-classes.ts";
import { _ } from "./hkts.ts";
import {
createMonad,
createPipeableMonad,
createPipeableTraversable,
} from "./derivations.ts";

/***************************************************************************************************
* @section Types
Expand Down Expand Up @@ -69,39 +75,39 @@ export const isSome = <A>(m: Option<A>): m is Some<A> => m.tag === "Some";
* @section Modules
**************************************************************************************************/

export const getShow = <A>({ show }: SL.Show<A>): SL.Show<Option<A>> => ({
export const getShow = <A>({ show }: TC.Show<A>): TC.Show<Option<A>> => ({
show: (ma) => (isNone(ma) ? "None" : `${"Some"}(${show(ma.value)})`),
});

export const Monad = SL.createMonad<Option<_>>({
export const Monad = createMonad<Option<_>>({
of: some,
chain: (fatb, ta) => (isSome(ta) ? fatb(ta.value) : ta),
});

export const Applicative: SL.Applicative<Option<_>> = {
export const Applicative: TC.Applicative<Option<_>> = {
of: some,
ap: Monad.ap,
map: Monad.map,
};

export const Apply: SL.Apply<Option<_>> = {
export const Apply: TC.Apply<Option<_>> = {
ap: Monad.ap,
map: Monad.map,
};

export const Alternative: SL.Alternative<Option<_>> = {
export const Alternative: TC.Alternative<Option<_>> = {
of: some,
ap: Monad.ap,
map: Monad.map,
zero: constNone,
alt: (a, b) => (isSome(a) ? a : b),
};

export const Foldable: SL.Foldable<Option<_>> = {
export const Foldable: TC.Foldable<Option<_>> = {
reduce: (faba, a, tb) => (isSome(tb) ? faba(a, tb.value) : a),
};

export const Traversable: SL.Traversable<Option<_>> = {
export const Traversable: TC.Traversable<Option<_>> = {
map: Monad.map,
reduce: Foldable.reduce,
traverse: (F, faub, ta) =>
Expand All @@ -112,6 +118,6 @@ export const Traversable: SL.Traversable<Option<_>> = {
* @section Pipeables
**************************************************************************************************/

export const { of, ap, map, join, chain } = SL.createPipeableMonad(Monad);
export const { of, ap, map, join, chain } = createPipeableMonad(Monad);

export const { reduce, traverse } = SL.createPipeableTraversable(Traversable);
export const { reduce, traverse } = createPipeableTraversable(Traversable);
37 changes: 11 additions & 26 deletions optionT.ts
Original file line number Diff line number Diff line change
@@ -1,37 +1,22 @@
import { Monad, Monad2 } from "./type-classes.ts";
import { _ } from "./hkts.ts";
import type { Monad, Monad2 } from "./type-classes.ts";
import type { _ } from "./hkts.ts";

import * as O from "./option.ts";
import * as C from "./composition.ts";

export const getOptionM = <T>(
M: Monad<T>
): C.MonadComposition<T, O.Option<_>> => {
export const getOptionM: {
<T>(M: Monad<T>): C.MonadComposition<T, O.Option<_>>;
<T>(M: Monad2<T>): C.MonadComposition2<T, O.Option<_>>;
} = <T>(M: Monad<T>): C.MonadComposition<T, O.Option<_>> => {
const { of, ap, map } = C.getApplicativeComposition(M, O.Applicative);

const chain: C.MonadComposition<T, O.Option<_>>["chain"] = (fatob, toa) =>
M.chain((oa) => (O.isNone(oa) ? M.of(O.none) : fatob(oa.value)), toa);

return {
of,
ap,
map,
chain,
join: (FGFGa) => chain((x) => x, FGFGa),
};
};

export const getOptionM2 = <T>(
M: Monad2<T>
): C.MonadComposition2<T, O.Option<_>> => {
const { of, ap, map } = C.getApplicative2Composition(M, O.Applicative);
const chain: C.MonadComposition2<T, O.Option<_>>["chain"] = (fatob, toa) =>
M.chain((oa) => (O.isNone(oa) ? M.of(O.none) : fatob(oa.value)), toa);

return {
of,
ap,
map,
chain,
join: (FGFGa) => chain((x) => x, FGFGa),
chain: (fatob, toa) =>
M.chain((oa) => (O.isNone(oa) ? M.of(O.none) : fatob(oa.value)), toa),
join: (FGFGa) =>
M.chain((GFGa) => (O.isNone(GFGa) ? M.of(O.none) : GFGa.value), FGFGa),
};
};
29 changes: 15 additions & 14 deletions sequence.ts
Original file line number Diff line number Diff line change
@@ -1,56 +1,57 @@
import { $, _, _0, _1 } from "./hkts.ts";
import { Apply, Apply2 } from "./type-classes.ts";
import type { $, _, _0, _1 } from "./hkts.ts";
import type { Apply, Apply2 } from "./type-classes.ts";

/**
* @todo Credit gcanti or reimplemment
* Sequence is not yet in its final implementation
*/

function tuple<T extends ReadonlyArray<any>>(...t: T): T {
function _tuple<T extends ReadonlyArray<any>>(...t: T): T {
return t;
}

function curried(f: Function, n: number, acc: ReadonlyArray<unknown>) {
function _curried(f: Function, n: number, acc: ReadonlyArray<unknown>) {
return function (x: unknown) {
const combined = Array(acc.length + 1);
for (let i = 0; i < acc.length; i++) {
combined[i] = acc[i];
}
combined[acc.length] = x;
return n === 0 ? f.apply(null, combined) : curried(f, n - 1, combined);
return n === 0 ? f.apply(null, combined) : _curried(f, n - 1, combined);
};
}

const tupleConstructors: Record<number, (a: unknown) => any> = {
const _tupleConstructors: Record<number, (a: unknown) => any> = {
1: (a) => [a],
2: (a) => (b: any) => [a, b],
3: (a) => (b: any) => (c: any) => [a, b, c],
4: (a) => (b: any) => (c: any) => (d: any) => [a, b, c, d],
5: (a) => (b: any) => (c: any) => (d: any) => (e: any) => [a, b, c, d, e],
};

function getTupleConstructor(len: number): (a: unknown) => any {
if (!tupleConstructors.hasOwnProperty(len)) {
tupleConstructors[len] = curried(tuple, len - 1, []);
function _getTupleConstructor(len: number): (a: unknown) => any {
if (!_tupleConstructors.hasOwnProperty(len)) {
_tupleConstructors[len] = _curried(_tuple, len - 1, []);
}
return tupleConstructors[len];
return _tupleConstructors[len];
}

export function sequenceTuple<T>({ map, ap }: Apply<T>) {
export function _sequenceTuple<T>({ map, ap }: Apply<T>) {
return <A, M extends $<T, [any]>[]>(
head: $<T, [A]>,
...tail: M
): $<
T,
[[A, ...{ [K in keyof M]: M[K] extends $<T, [infer U]> ? U : never }]]
> => tail.reduce(ap, map(getTupleConstructor(tail.length + 1), head));
> => tail.reduce(ap, map(_getTupleConstructor(tail.length + 1), head));
}

export function sequenceTuple2<T>({ map, ap }: Apply2<T>) {
export function _sequenceTuple2<T>({ map, ap }: Apply2<T>) {
return <E, R, M extends $<T, [E, any]>[]>(
head: $<T, [E, R]>,
...tail: M
): $<
T,
[E, [R, ...{ [K in keyof M]: M[K] extends $<T, [E, infer A]> ? A : never }]]
> => tail.reduce(ap, map(getTupleConstructor(tail.length + 1), head));
> => tail.reduce(ap, map(_getTupleConstructor(tail.length + 1), head));
}
60 changes: 60 additions & 0 deletions task.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import type * as TC from "./type-classes.ts";
import type { _ } from "./hkts.ts";
import { createMonad, createPipeableMonad } from "./derivations.ts";

/***************************************************************************************************
* @section Types
**************************************************************************************************/

export type Task<A> = () => Promise<A>;

/***************************************************************************************************
* @section Constructors
**************************************************************************************************/

export const of = <A>(a: A): Task<A> => () => Promise.resolve(a);

/***************************************************************************************************
* @section Destructors
**************************************************************************************************/

/***************************************************************************************************
* @section Combinators
**************************************************************************************************/

export const delay = (ms: number) => <A>(ma: Task<A>): Task<A> => () =>
new Promise((resolve) => {
setTimeout(() => {
ma().then(resolve);
}, ms);
});

/***************************************************************************************************
* @section Guards
**************************************************************************************************/

/***************************************************************************************************
* @section Modules
**************************************************************************************************/

export const Monad = createMonad<Task<_>>({
of,
chain: (fatb, ta) => () => ta().then((a) => fatb(a)()) as Promise<any>,
});

export const Applicative: TC.Applicative<Task<_>> = {
of,
ap: Monad.ap,
map: Monad.map,
};

export const Apply: TC.Apply<Task<_>> = {
ap: Monad.ap,
map: Monad.map,
};

/***************************************************************************************************
* @section Pipeables
**************************************************************************************************/

export const { ap, map, join, chain } = createPipeableMonad(Monad);
Loading

0 comments on commit 79e762e

Please sign in to comment.