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

Commit

Permalink
feat: option tests complete
Browse files Browse the repository at this point in the history
  • Loading branch information
baetheus committed Apr 11, 2021
1 parent e74e93a commit a3a3fa3
Show file tree
Hide file tree
Showing 5 changed files with 416 additions and 68 deletions.
2 changes: 1 addition & 1 deletion map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,8 @@ export const getMonoid = <K, A>(
pipe(
lookupKey(bk)(a),
O.fold(
([ak, aa]) => r.set(ak, SA.concat(aa)(ba)),
() => r.set(bk, ba),
([ak, aa]) => r.set(ak, SA.concat(aa)(ba)),
),
);
}
Expand Down
136 changes: 71 additions & 65 deletions option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@ export type Some<V> = { tag: "Some"; value: V };
*/
export type Option<A> = Some<A> | None;

/*******************************************************************************
* Kind Registration
******************************************************************************/

export const URI = "Option";

export type URI = typeof URI;
Expand Down Expand Up @@ -115,10 +119,8 @@ export const tryCatch = <A>(f: Lazy<A>): Option<A> => {
* const a = toNumber(some(1)); // 1
* const b = toNumber(none); // 0
*/
export const fold = <A, B>(onSome: (a: A) => B, onNone: () => B) =>
(
ta: Option<A>,
): B => (isNone(ta) ? onNone() : onSome(ta.value));
export const fold = <A, B>(onNone: () => B, onSome: (a: A) => B) =>
(ta: Option<A>): B => (isNone(ta) ? onNone() : onSome(ta.value));

/**
* getOrElse operates like a simplified fold. One supplies a thunk that returns a default
Expand All @@ -136,7 +138,7 @@ export const getOrElse = <B>(onNone: () => B) =>
* toNullable returns either null or the inner value of an Option. This is useful for
* interacting with code that handles null but has no concept of the Option type.
*/
export const toNullable = <A>(ma: Option<A>): A | null =>
export const toNull = <A>(ma: Option<A>): A | null =>
isNone(ma) ? null : ma.value;

/**
Expand Down Expand Up @@ -179,66 +181,6 @@ export const isNone = <A>(m: Option<A>): m is None => m.tag === "None";
*/
export const isSome = <A>(m: Option<A>): m is Some<A> => m.tag === "Some";

/*******************************************************************************
* Module Getters
******************************************************************************/

/**
* Generates a Show module for an option with inner type of A.
*
* @example
* const Show = getShow({ show: (n: number) => n.toString() }); // Show<Option<number>>
* const a = Show.show(some(1)); // "Some(1)"
* const b = Show.show(none); // "None"
*/
export const getShow = <A>({ show }: TC.Show<A>): TC.Show<Option<A>> => ({
show: (ma) => (isNone(ma) ? "None" : `${"Some"}(${show(ma.value)})`),
});

/**
* Generates a Setoid module for an option with inner type of A.
*
* @example
* const Setoid = getSetoid({ equals: (a: number, b: number) => a === b });
* const a = Setoid.equals(some(1), some(2)); // false
* const b = Setoid.equals(some(1), some(1)); // true
* const c = Setoid.equals(none, none); // true
* const d = Setoid.equals(some(1), none); // false
*/
export const getSetoid = <A>(S: TC.Setoid<A>): TC.Setoid<Option<A>> => ({
equals: (a) =>
(b) =>
a === b || isNone(a)
? isNone(b)
: (isNone(b) ? false : S.equals(a.value)(b.value)),
});

export const getOrd = <A>(O: TC.Ord<A>): TC.Ord<Option<A>> => ({
...getSetoid(O),
lte: (a) =>
(b) =>
a === b || isNone(a)
? isNone(b)
: (isNone(b) ? false : O.lte(a.value)(b.value)),
});

export const getSemigroup = <A>(
S: TC.Semigroup<A>,
): TC.Semigroup<Option<A>> => ({
concat: (x) =>
(y) => isNone(x) ? y : isNone(y) ? x : of(S.concat(x.value)(y.value)),
});

export const getMonoid = <A>(M: TC.Monoid<A>): TC.Monoid<Option<A>> => ({
...getSemigroup(M),
empty: constNone,
});

export const getGroup = <A>(G: TC.Group<A>): TC.Group<Option<A>> => ({
...getMonoid(G),
invert: (ta) => isNone(ta) ? ta : some(G.invert(ta.value)),
});

/*******************************************************************************
* Modules
******************************************************************************/
Expand Down Expand Up @@ -324,6 +266,70 @@ export const Traversable: TC.Traversable<URI> = {
isNone(ta) ? A.of(constNone()) : pipe(favi(ta.value), A.map(some)),
};

/*******************************************************************************
* Module Getters
******************************************************************************/

/**
* Generates a Show module for an option with inner type of A.
*
* @example
* const Show = getShow({ show: (n: number) => n.toString() }); // Show<Option<number>>
* const a = Show.show(some(1)); // "Some(1)"
* const b = Show.show(none); // "None"
*/
export const getShow = <A>({ show }: TC.Show<A>): TC.Show<Option<A>> => ({
show: (ma) => (isNone(ma) ? "None" : `${"Some"}(${show(ma.value)})`),
});

/**
* Generates a Setoid module for an option with inner type of A.
*
* @example
* const Setoid = getSetoid({ equals: (a: number, b: number) => a === b });
* const a = Setoid.equals(some(1), some(2)); // false
* const b = Setoid.equals(some(1), some(1)); // true
* const c = Setoid.equals(none, none); // true
* const d = Setoid.equals(some(1), none); // false
*/
export const getSetoid = <A>(S: TC.Setoid<A>): TC.Setoid<Option<A>> => ({
equals: (a) =>
(b) =>
a === b ||
((isSome(a) && isSome(b))
? S.equals(a.value)(b.value)
: (isNone(a) && isNone(b))),
});

export const getOrd = <A>(O: TC.Ord<A>): TC.Ord<Option<A>> => ({
...getSetoid(O),
lte: (a) =>
(b) => {
if (a === b) {
return true;
}
if (isNone(a)) {
return true;
}
if (isNone(b)) {
return false;
}
return O.lte(a.value)(b.value);
},
});

export const getSemigroup = <A>(
S: TC.Semigroup<A>,
): TC.Semigroup<Option<A>> => ({
concat: (x) =>
(y) => isNone(x) ? y : isNone(y) ? x : of(S.concat(x.value)(y.value)),
});

export const getMonoid = <A>(M: TC.Monoid<A>): TC.Monoid<Option<A>> => ({
...getSemigroup(M),
empty: constNone,
});

/*******************************************************************************
* Pipeables
******************************************************************************/
Expand Down
2 changes: 1 addition & 1 deletion testing/fns.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ Deno.test("fns wait", async () => {
const within = (high: number, low: number) =>
(value: number): boolean => value >= low && value <= high;
const target = 100;
const high = 200;
const high = 900; // github actions on macos tend to drag
const low = 50;

const test = within(high, low);
Expand Down
2 changes: 1 addition & 1 deletion testing/io.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ Deno.test("IO reduce", () => {
});

Deno.test("IO traverse", () => {
const fold = O.fold((n: I.IO<number>) => n(), () => -1);
const fold = O.fold(() => -1, (n: I.IO<number>) => n());
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)));
Expand Down
Loading

0 comments on commit a3a3fa3

Please sign in to comment.