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

Commit

Permalink
feat: prism optic tests complete
Browse files Browse the repository at this point in the history
  • Loading branch information
baetheus committed Apr 14, 2021
1 parent cf3aa3c commit 4a07033
Show file tree
Hide file tree
Showing 2 changed files with 232 additions and 14 deletions.
29 changes: 15 additions & 14 deletions optics/prism.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ export type To<T> = T extends Prism<infer _, infer A> ? A : never;
* Constructors
******************************************************************************/

export const make = <S, A>(
getOption: (s: S) => O.Option<A>,
reverseGet: (a: A) => S,
): Prism<S, A> => ({
getOption,
reverseGet,
});

type FromPredicate = {
<S, A extends S>(refinement: Refinement<S, A>): Prism<S, A>;
<A>(predicate: Predicate<A>): Prism<A, A>;
Expand Down Expand Up @@ -160,27 +168,20 @@ export const traverse = <URI extends URIS>(T: TC.Traversable<URI>) => {
);
};

export const traverseOption = <S, A>(sa: Prism<S, A>) =>
(faa: (a: A) => A) =>
(s: S): O.Option<S> =>
export const modify = <A>(f: (a: A) => A) =>
<S>(sa: Prism<S, A>) =>
(s: S): S =>
pipe(
sa.getOption(s),
O.map((_a: A) => {
const na = faa(_a);
return na === _a ? s : sa.reverseGet(na);
O.map((a) => {
const na = f(a);
return na === a ? s : sa.reverseGet(na);
}),
);

export const modify = <S, A>(sa: Prism<S, A>) =>
(f: (a: A) => A) =>
(s: S): S =>
pipe(
traverseOption(sa)(f)(s),
O.getOrElse(() => s),
);

export const getSet = <S, A>(sa: Prism<S, A>) =>
(a: A) => (s: S): S => modify(sa)(() => a)(s);
(a: A) => pipe(sa, modify(constant(a)));

export const prop = <A, P extends keyof A>(
prop: P,
Expand Down
217 changes: 217 additions & 0 deletions testing/optics/prism.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,217 @@
import { assertEquals } from "https://deno.land/std/testing/asserts.ts";

import * as P from "../../optics/prism.ts";
import * as I from "../../optics/iso.ts";
import * as L from "../../optics/lens.ts";
import * as O from "../../option.ts";
import * as E from "../../either.ts";
import { pipe } from "../../fns.ts";

type T1 = { one: number };

type T2 = { one: number; two: number; three: number };

Deno.test("Prism make", () => {
const { getOption, reverseGet } = P.make(
(n: number) => n === 0 ? O.none : O.some(n),
(n: number) => n,
);
assertEquals(getOption(0), O.none);
assertEquals(getOption(1), O.some(1));
assertEquals(reverseGet(0), 0);
});

Deno.test("Prism fromPredicate", () => {
const { getOption, reverseGet } = P.fromPredicate((n: number) => n > 0);
assertEquals(getOption(0), O.none);
assertEquals(getOption(1), O.some(1));
assertEquals(reverseGet(0), 0);
});

Deno.test("Prism asOptional", () => {
const { getOption, set } = P.asOptional(
P.fromPredicate((n: number) => n > 0),
);
assertEquals(getOption(0), O.none);
assertEquals(getOption(1), O.some(1));
assertEquals(set(0)(0), 0);
assertEquals(set(0)(1), 0);
});

Deno.test("Prism asTraversal", () => {
const { traverse } = P.asTraversal(P.id<number>());
const t1 = traverse(O.Applicative);
const t2 = t1((n: number) => n === 0 ? O.none : O.some(n));
assertEquals(t2(0), O.none);
assertEquals(t2(1), O.some(1));
});

Deno.test("Prism fromNullable", () => {
const prism = P.make(
(n: number) => n === 0 ? O.some(undefined) : O.some(n),
(n: number | undefined) => n === undefined ? 0 : n,
);
const { getOption, reverseGet } = P.fromNullable(prism);
assertEquals(getOption(0), O.none);
assertEquals(getOption(1), O.some(1));
assertEquals(reverseGet(0), 0);
assertEquals(reverseGet(1), 1);
});

Deno.test("Prism id", () => {
const { getOption, reverseGet } = P.id<number>();
assertEquals(getOption(0), O.some(0));
assertEquals(reverseGet(0), 0);
});

Deno.test("Prism compose", () => {
const prism = P.make(
(n: number) => O.some(n.toString()),
(s) => parseFloat(s),
);
const { getOption, reverseGet } = pipe(P.id<number>(), P.compose(prism));
assertEquals(getOption(0), O.some("0"));
assertEquals(reverseGet("0"), 0);
});

Deno.test("Prism composeIso", () => {
const iso = I.make((n: number) => n + 1, (n: number) => n - 1);
const { getOption, reverseGet } = pipe(P.id<number>(), P.composeIso(iso));
assertEquals(getOption(0), O.some(1));
assertEquals(reverseGet(0), -1);
});

Deno.test("Prism composeLens", () => {
const lens = L.make(
(n: number) => n.toString(),
(s: string) => (_: number) => parseFloat(s),
);
const { getOption, set } = pipe(P.id<number>(), P.composeLens(lens));
assertEquals(getOption(0), O.some("0"));
assertEquals(set("0")(0), 0);
assertEquals(set("0")(1), 0);
});

Deno.test("Prism composeOptional", () => {
const { getOption, set } = pipe(
P.id<number>(),
P.composeOptional(P.asOptional(P.fromPredicate((n: number) => n > 0))),
);
assertEquals(getOption(0), O.none);
assertEquals(getOption(1), O.some(1));
assertEquals(set(0)(0), 0);
assertEquals(set(0)(1), 0);
});

Deno.test("Prism composeTraversal", () => {
const { traverse } = pipe(
P.id<number>(),
P.composeTraversal(P.asTraversal(P.fromPredicate((n: number) => n > 0))),
);
const t1 = traverse(O.Applicative);
const t2 = t1((n) => n === 0 ? O.none : O.some(n));
assertEquals(t2(0), O.some(0));
assertEquals(t2(1), O.some(1));
});

Deno.test("Prism filter", () => {
const { getOption, reverseGet } = pipe(
P.id<number>(),
P.filter((n: number) => n > 0),
);
assertEquals(getOption(0), O.none);
assertEquals(getOption(1), O.some(1));
assertEquals(reverseGet(0), 0);
});

Deno.test("Prism traverse", () => {
const { traverse } = pipe(
P.id<E.Either<number, number>>(),
P.traverse(E.Traversable),
);
const t1 = traverse(O.Applicative);
const t2 = t1((n) => n > 0 ? O.some(n) : O.none);
assertEquals(t2(E.left(0)), O.some(E.left(0)));
assertEquals(t2(E.right(0)), O.none);
assertEquals(t2(E.right(1)), O.some(E.right(1)));
});

Deno.test("Prism modify", () => {
const modify = pipe(P.id<number>(), P.modify((n) => n + 1));
assertEquals(modify(0), 1);
});

Deno.test("Prism getSet", () => {
const set = P.getSet(P.id<number>());
assertEquals(set(0)(0), 0);
assertEquals(set(0)(1), 0);
});

Deno.test("Prism prop", () => {
const { getOption, set } = pipe(P.id<T1>(), P.prop("one"));
assertEquals(getOption({ one: 1 }), O.some(1));
assertEquals(set(0)({ one: 0 }), { one: 0 });
assertEquals(set(0)({ one: 1 }), { one: 0 });
});

Deno.test("Prism props", () => {
const { getOption, set } = pipe(P.id<T2>(), P.props("one", "two"));
const t2: T2 = { one: 1, two: 2, three: 3 };
assertEquals(getOption(t2), O.some({ one: 1, two: 2 }));
assertEquals(set({ one: 1, two: 2 })(t2), t2);
assertEquals(set({ one: 2, two: 1 })(t2), { one: 2, two: 1, three: 3 });
});

Deno.test("Prism index", () => {
const { getOption, set } = pipe(L.id<ReadonlyArray<number>>(), L.index(1));
assertEquals(getOption([]), O.none);
assertEquals(getOption([1, 2]), O.some(2));
assertEquals(set(3)([]), []);
assertEquals(set(3)([1]), [1]);
assertEquals(set(3)([1, 2]), [1, 3]);
});

Deno.test("Prism key", () => {
const { getOption, set } = pipe(
L.id<Readonly<Record<string, number>>>(),
L.key("one"),
);
assertEquals(getOption({}), O.none);
assertEquals(getOption({ one: 1 }), O.some(1));
assertEquals(set(3)({}), {});
assertEquals(set(3)({ one: 1 }), { one: 3 });
});

Deno.test("Prism atKey", () => {
const { getOption, set } = pipe(
P.id<Readonly<Record<string, number>>>(),
P.atKey("one"),
);
assertEquals(getOption({}), O.some(O.none));
assertEquals(getOption({ one: 1 }), O.some(O.some(1)));
assertEquals(set(O.none)({}), {});
assertEquals(set(O.none)({ one: 1 }), {});
assertEquals(set(O.some(1))({ one: 1 }), { one: 1 });
assertEquals(set(O.some(2))({ one: 1 }), { one: 2 });
});

Deno.test("Prism some", () => {
const { getOption, reverseGet } = P.some<number>();
assertEquals(getOption(O.none), O.none);
assertEquals(getOption(O.some(1)), O.some(1));
assertEquals(reverseGet(1), O.some(1));
});

Deno.test("Prism right", () => {
const { getOption, reverseGet } = P.right<number, number>();
assertEquals(getOption(E.left(1)), O.none);
assertEquals(getOption(E.right(1)), O.some(1));
assertEquals(reverseGet(1), E.right(1));
});

Deno.test("Prism left", () => {
const { getOption, reverseGet } = P.left<number, number>();
assertEquals(getOption(E.left(1)), O.some(1));
assertEquals(getOption(E.right(1)), O.none);
assertEquals(reverseGet(1), E.left(1));
});

0 comments on commit 4a07033

Please sign in to comment.