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

Commit

Permalink
feat: started adding documentation and examples
Browse files Browse the repository at this point in the history
Initial run at documentation of option.ts to see how well
it works with the deno auto-documentation
  • Loading branch information
baetheus committed Oct 12, 2020
1 parent 896f5d2 commit c90a933
Showing 1 changed file with 124 additions and 10 deletions.
134 changes: 124 additions & 10 deletions option.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,80 @@ import { constant, isNotNil } from "./fns.ts";
import * as D from "./derivations.ts";

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

/**
* The None type represents the non-existence of a value.
*/
export type None = { tag: "None" };

/**
* The Some type represents the existence of a value.
*/
export type Some<V> = { tag: "Some"; value: V };

/**
* The Option<A> represents a type A that may or may not exist. It's the functional
* progamming equivalent of A | undefined | null.
*/
export type Option<A> = None | Some<A>;

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

/**
* The cannonical implementation of the None type. Since all None values are equivalent there
* is no reason to construct more than one object instance.
*/
export const none: None = { tag: "None" };

/**
* The some constructer takes any value and wraps it in the Some type.
*/
export const some = <A>(value: A): Option<A> => ({ tag: "Some", value });

/**
* constNone is a thunk that returns the canonical none instance.
*/
export const constNone = () => none;

/**
* fromNullable takes a potentially null or undefined value and maps null or undefined to
* None and non-null and non-undefined values to Some<NonNullable<A>>
*
* @example
* const a: number | undefined = undefined;
* const b: number | undefined = 2;
* const optionNumber = fromNullable(a); // None
* const optionNumber = fromNullable(b); // Some<number>
* const numberArray = [1, 2, 3];
* const optionFourthEntry = fromNullable(numberArray[3]); // None
*/
export const fromNullable = <A>(a: A): Option<NonNullable<A>> =>
isNotNil(a) ? some(a) : none;

/**
* fromPredicate will test the value a with the predicate. If
* the predicate evaluates to false then the function will return a None,
* otherwise the value wrapped in Some
*
* @example
* const fromPositiveNumber = fromPredicate((n: number) => n > 0);
* const a = fromPositiveNumber(-1); // None
* const a = fromPositiveNumber(1); // Some<number>
*/
export const fromPredicate = <A>(predicate: Predicate<A>) =>
(
a: A,
): Option<A> => (predicate(a) ? some(a) : none);

/**
* tryCatch takes a thunk that can potentially throw and wraps it
* in a try/catch statement. If the thunk throws then tryCatch returns
* None, otherwise it returns the result of the thunk wrapped in a Some.
*/
export const tryCatch = <A>(f: Lazy<A>): Option<A> => {
try {
return some(f());
Expand All @@ -38,47 +89,110 @@ export const tryCatch = <A>(f: Lazy<A>): Option<A> => {
};

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

/**
* fold is the standard catamorphism on an Option<A>. It operates like a switch case
* operator over the two potential cases for an Option type. One supplies functions for
* handling the Some case and the None case with matching return types and fold calls
* the correct function for the given option.
*
* @example
* const toNumber = fold((a: number) => a, () => 0);
* 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));

/**
* getOrElse operates like a simplified fold. One supplies a thunk that returns a default
* inner value of the Option for the cases where the option is None.
*
* @example
* const toNumber = getOrElse(() => 0);
* const a = toNumber(some(1)); // 1
* const b = toNumber(none); // 0
*/
export const getOrElse = <B>(onNone: () => B) =>
(ta: Option<B>): B => isNone(ta) ? onNone() : ta.value;

/**
* 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 =>
isNone(ma) ? null : ma.value;

/**
* toUndefined returns either undefined or the inner value of an Option. This is useful for
* interacting with code that handles undefined but has no concept of the Option type.
*/
export const toUndefined = <A>(ma: Option<A>): A | undefined =>
isNone(ma) ? undefined : ma.value;

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

/**
* mapNullable is useful for piping an option's values through functions that may return
* null or undefined.
*
* @example
* const a = pipe(
* some([1, 2, 3]),
* mapNullable(numbers => numbers[3])
* ); // None (Option<number>)
*/
export const mapNullable = <A, B>(f: (a: A) => B | null | undefined) =>
(
ma: Option<A>,
): Option<B> => (isNone(ma) ? none : fromNullable(f(ma.value)));

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

/**
* Tests wether an Option is None. Can be used as a predicate.
*/
export const isNone = <A>(m: Option<A>): m is None => m.tag === "None";

/**
* Tests wether an Option is Some. Can be used as a predicate.
*/
export const isSome = <A>(m: Option<A>): m is Some<A> => m.tag === "Some";

/***************************************************************************************************
* @section Module Getters
* 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)
Expand Down Expand Up @@ -112,7 +226,7 @@ export const getGroup = <A>(G: TC.Group<A>): TC.Group<Option<A>> => ({
});

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

export const Functor: TC.Functor<Option<_>> = {
Expand Down Expand Up @@ -181,7 +295,7 @@ export const Traversable: TC.Traversable<Option<_>> = {
};

/***************************************************************************************************
* @section Transformers
* Transformers
**************************************************************************************************/

// deno-fmt-ignore
Expand All @@ -208,15 +322,15 @@ export const getOptionM: GetOptionMonad = <T>(M: TC.Monad<T>) =>
});

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

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

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

/***************************************************************************************************
* @section Sequence
* Sequence
**************************************************************************************************/

export const sequenceTuple = createSequenceTuple(Apply);
Expand Down

0 comments on commit c90a933

Please sign in to comment.