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

Commit

Permalink
feat: add unknown escape hatch to schemable
Browse files Browse the repository at this point in the history
* add unknown combinator to Schemable
* implement unknown for decoder and guard
* add monoid test to array.test
* run formatter on graph.ts
  • Loading branch information
baetheus committed Jan 30, 2021
1 parent 62a4097 commit d0f454f
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 85 deletions.
14 changes: 7 additions & 7 deletions examples/fetch_archives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ import * as T from "../optics/traversal.ts";
import { flow, pipe } from "../fns.ts";

// Let's start by defining some error types and making some constructors
type FetchError = { type: "FetchError"; message: string };
const fetchError = (message: string): FetchError => ({
type FetchError = { type: "FetchError"; error: unknown };
const fetchError = (error: unknown): FetchError => ({
type: "FetchError",
message,
error,
});

type DecodeError = { type: "DecodeError"; message: string };
const decodeError = (message: string): DecodeError => ({
type DecodeError = { type: "DecodeError"; error: string };
const decodeError = (error: string): DecodeError => ({
type: "DecodeError",
message,
error,
});

/**
Expand Down Expand Up @@ -58,7 +58,7 @@ const fromDecode = <A>(decoder: D.Decoder<unknown, A>) =>
decoder, // The decoder
E.mapLeft((e) => decodeError(D.draw(e))), // Take any decoder errors and wrap them up
TE.fromEither, // Since a decoder returns a plain "Either" we wrap it in a Task to make a TaskEither
TE.widen<FetchError>(), // This hack is necessary so we can have different error types in the flow
TE.widen<FetchError>(), // This is necessary so we can have different error types in the flow
)),
);

Expand Down
152 changes: 74 additions & 78 deletions graph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,7 @@ export type Connect<A> = {
readonly to: Graph<A>;
};

export type Graph<A> =
| Empty
| Vertex<A>
| Overlay<A>
| Connect<A>;
export type Graph<A> = Empty | Vertex<A> | Overlay<A> | Connect<A>;

export type AdjacencyMap<A> = Map<A, Set<A>>;

Expand All @@ -51,8 +47,8 @@ export const fold = <A, B>(
onEmpty: () => B,
onVertex: (a: A) => B,
onOverlay: (left: B, right: B) => B,
onConnect: (from: B, to: B) => B,
): (g: Graph<A>) => B => {
onConnect: (from: B, to: B) => B
): ((g: Graph<A>) => B) => {
const go = (g: Graph<A>): B => {
switch (g.tag) {
case "Empty":
Expand All @@ -73,7 +69,7 @@ export const fold = <A, B>(
* @section Constructors
**************************************************************************************************/

export const zero: Graph<never> = ({ tag: "Empty" });
export const zero: Graph<never> = { tag: "Empty" };

export const empty = <A = never>(): Graph<A> => zero;

Expand Down Expand Up @@ -138,7 +134,7 @@ const getInstanceHelpers = memoize(<A>(SA: TC.Setoid<A>) => {
S.empty,
S.of,
(a, b) => setUnion(a)(b),
(a, b) => setUnion(a)(b),
(a, b) => setUnion(a)(b)
);

const edgeSet = (g: Graph<A>): Set<[A, A]> => {
Expand All @@ -150,10 +146,7 @@ const getInstanceHelpers = memoize(<A>(SA: TC.Setoid<A>) => {
return setTupleUnion(edgeSet(g.left))(edgeSet(g.right));
case "Connect":
return setTupleUnion(setTupleUnion(edgeSet(g.from))(edgeSet(g.to)))(
chainS(
(x) => mapS((y) => [x, y], vertexSet(g.to)),
vertexSet(g.from),
),
chainS((x) => mapS((y) => [x, y], vertexSet(g.to)), vertexSet(g.from))
);
}
};
Expand All @@ -164,7 +157,7 @@ const getInstanceHelpers = memoize(<A>(SA: TC.Setoid<A>) => {
} else {
return and(
setSetoid.equals(vertexSet(x), vertexSet(y)),
setTupleSetoid.equals(edgeSet(x), edgeSet(y)),
setTupleSetoid.equals(edgeSet(x), edgeSet(y))
);
}
});
Expand Down Expand Up @@ -192,23 +185,15 @@ const getInstanceHelpers = memoize(<A>(SA: TC.Setoid<A>) => {

export const Monad = D.createMonad<Graph<_>>({
of: vertex,
map: (fab) =>
flow(
fold(
empty,
flow(fab, vertex),
overlay,
connect,
),
),
map: (fab) => flow(fold(empty, flow(fab, vertex), overlay, connect)),
chain: (fatb) =>
flow(
fold(
empty,
flow(fatb, fold(empty, vertex, overlay, connect)),
overlay,
connect,
),
connect
)
),
});

Expand All @@ -225,7 +210,7 @@ export const getSetoid = <A>(SA: TC.Setoid<A>): TC.Setoid<Graph<A>> =>

export const getShow = <A>(
SA: TC.Setoid<A>,
SH: TC.Show<A>,
SH: TC.Show<A>
): TC.Show<Graph<A>> => {
const _toAdjacencyMap = toAdjacencyMap(SA);
const _set = S.getShow(SH);
Expand Down Expand Up @@ -255,59 +240,68 @@ export const sequenceTuple = createSequenceTuple(Apply);

export const sequenceStruct = createSequenceStruct(Apply);

export const hasVertex = <A>(SA: TC.Setoid<A>) =>
(vertex: A) =>
(graph: Graph<A>): boolean =>
pipe(
graph,
fold(constFalse, (a) => SA.equals(vertex, a), or, or),
);
export const hasVertex = <A>(SA: TC.Setoid<A>) => (vertex: A) => (
graph: Graph<A>
): boolean =>
pipe(
graph,
fold(constFalse, (a) => SA.equals(vertex, a), or, or)
);

export const hasEdge = <A>(SA: TC.Setoid<A>) =>
(edgeFrom: A, edgeTo: A) =>
(g: Graph<A>): boolean => {
const f = pipe(
g,
fold(
() => (n: number) => n,
(a) =>
(n) =>
n === 0
? (SA.equals(edgeFrom, a) ? 1 : 0)
: SA.equals(edgeTo, a)
? 2
: 1,
(left, right) =>
(n) => {
const hold = left(n);
return hold === 0
? right(n)
: hold === 1
? right(n) === 2 ? 2 : 1
: 2;
},
(from, to) =>
(n: number) => {
const res = from(n);
return res === 2 ? 2 : to(res);
},
),
);
export const hasEdge = <A>(SA: TC.Setoid<A>) => (edgeFrom: A, edgeTo: A) => (
g: Graph<A>
): boolean => {
const f = pipe(
g,
fold(
() => (n: number) => n,
(a) => (n) =>
n === 0
? SA.equals(edgeFrom, a)
? 1
: 0
: SA.equals(edgeTo, a)
? 2
: 1,
(left, right) => (n) => {
const hold = left(n);
return hold === 0
? right(n)
: hold === 1
? right(n) === 2
? 2
: 1
: 2;
},
(from, to) => (n: number) => {
const res = from(n);
return res === 2 ? 2 : to(res);
}
)
);

return f(0) === 2;
};
return f(0) === 2;
};

export const induce = <A>(predicate: Predicate<A>) =>
(ta: Graph<A>): Graph<A> =>
pipe(ta, chain((a: A) => predicate(a) ? vertex(a) : empty()));
export const induce = <A>(predicate: Predicate<A>) => (
ta: Graph<A>
): Graph<A> =>
pipe(
ta,
chain((a: A) => (predicate(a) ? vertex(a) : empty()))
);

export const removeVertex = <A>(SA: TC.Setoid<A>) =>
(v: A): (g: Graph<A>) => Graph<A> => induce((a) => !SA.equals(v, a));
export const removeVertex = <A>(SA: TC.Setoid<A>) => (
v: A
): ((g: Graph<A>) => Graph<A>) => induce((a) => !SA.equals(v, a));

export const splitVertex = <A>(SA: TC.Setoid<A>) =>
(a: A, as: A[]) =>
(ta: Graph<A>): Graph<A> =>
pipe(ta, chain((v) => SA.equals(a, v) ? vertices(as) : vertex(v)));
export const splitVertex = <A>(SA: TC.Setoid<A>) => (a: A, as: A[]) => (
ta: Graph<A>
): Graph<A> =>
pipe(
ta,
chain((v) => (SA.equals(a, v) ? vertices(as) : vertex(v)))
);

export const isEmpty = <A>(g: Graph<A>): boolean =>
fold(constTrue, constFalse, and, and)(g);
Expand All @@ -319,7 +313,9 @@ export const transpose = <A>(g: Graph<A>): Graph<A> =>
pipe(g, fold(empty, vertex, overlay, swap(connect)));

const _simple = <A>(SA: TC.Setoid<A>) => {
const { graphSetoid: { equals } } = getInstanceHelpers(SA);
const {
graphSetoid: { equals },
} = getInstanceHelpers(SA);
return (op: Fn<[Graph<A>, Graph<A>], Graph<A>>) => {
return (a: Graph<A>, b: Graph<A>): Graph<A> => {
const c = op(a, b);
Expand Down Expand Up @@ -353,7 +349,7 @@ export const toAdjacencyMap = <A>(SA: TC.Setoid<A>) => {
productEdges.set(key, new Set(y.keys()));
}
return foldMonoid(monoidMap)([x, y, productEdges]);
},
}
);
};

Expand All @@ -363,6 +359,6 @@ export const isSubgraph = <A>(SA: TC.Setoid<A>) => {
const submap = M.isSubmap(SA, subsetSetoid);
const adjacenctMap = toAdjacencyMap(SA);

return (parent: Graph<A>) =>
(sub: Graph<A>): boolean => submap(adjacenctMap(sub))(adjacenctMap(parent));
return (parent: Graph<A>) => (sub: Graph<A>): boolean =>
submap(adjacenctMap(sub))(adjacenctMap(parent));
};
3 changes: 3 additions & 0 deletions schemable/decoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ export const undefinable = <I, A>(
* @section Schemables
**************************************************************************************************/

export const unknown = fromGuard(G.unknown, "unknown");

export const literal = <A extends readonly [S.Literal, ...S.Literal[]]>(
...values: A
): Decoder<unknown, A[number]> => {
Expand Down Expand Up @@ -361,6 +363,7 @@ export const lazy = <I, A>(
**************************************************************************************************/

export const Schemable: S.Schemable<Decoder<unknown, _>> = {
unknown: () => unknown,
literal,
string: () => string,
number: () => number,
Expand Down
3 changes: 3 additions & 0 deletions schemable/guard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ export const refineUnknown: <A>(
* @section Guard Schemables
**************************************************************************************************/

export const unknown = (u: unknown): u is unknown => true;

export const literal = <A extends readonly [S.Literal, ...S.Literal[]]>(
...values: A
): Guard<unknown, A[number]> =>
Expand Down Expand Up @@ -170,6 +172,7 @@ export const sum = <T extends string, A>(
**************************************************************************************************/

export const Schemable: S.Schemable<Guard<unknown, _>> = {
unknown: () => unknown,
literal,
string: () => string,
number: () => number,
Expand Down
8 changes: 8 additions & 0 deletions schemable/schemable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@ export type Literal = string | number | boolean | null;
* @section Schemables
**************************************************************************************************/

export type UnknownSchemable<T, L extends LS> = {
1: () => $<T, [unknown]>;
2: <E>() => $<T, [E, unknown]>;
3: <R, E>() => $<T, [R, E, unknown]>;
4: <S, R, E>() => $<T, [S, R, E, unknown]>;
}[L];

export type LiteralSchemable<T, L extends LS> = {
1: <A extends [Literal, ...Literal[]]>(...s: A) => $<T, [A[number]]>;
2: <E, A extends [Literal, ...Literal[]]>(...s: A) => $<T, [E, A[number]]>;
Expand Down Expand Up @@ -170,6 +177,7 @@ export type LazySchemable<T, L extends LS> = {
**************************************************************************************************/

export type Schemable<T, L extends LS = 1> = {
readonly unknown: UnknownSchemable<T, L>;
readonly literal: LiteralSchemable<T, L>;
readonly string: StringSchemable<T, L>;
readonly number: NumberSchemable<T, L>;
Expand Down
9 changes: 9 additions & 0 deletions testing/array.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,15 @@ Deno.test({
faba: (a: number, b: number) => a + b,
},
);

Test.assertMonoid<readonly any[]>(
A.Monoid,
{
a: [1],
b: [2],
c: [3],
},
);
},
});

Expand Down

0 comments on commit d0f454f

Please sign in to comment.