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

Commit

Permalink
feat: add optics over option and either types
Browse files Browse the repository at this point in the history
Additionally, exported unsafe operators from array.ts
as interenal only functions. Implemented getRight and
getLeft destructors in either.ts
  • Loading branch information
baetheus committed Nov 22, 2020
1 parent 6032442 commit ab411b3
Show file tree
Hide file tree
Showing 7 changed files with 130 additions and 16 deletions.
16 changes: 8 additions & 8 deletions array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,10 +56,10 @@ export const _concat = <A>(
return result;
};

const isOutOfBounds = <A>(i: number, as: readonly A[]): boolean =>
export const _isOutOfBounds = <A>(i: number, as: readonly A[]): boolean =>
i < 0 || i >= as.length;

const unsafeInsertAt = <A>(
export const _unsafeInsertAt = <A>(
i: number,
a: A,
as: ReadonlyArray<A>,
Expand All @@ -69,7 +69,7 @@ const unsafeInsertAt = <A>(
return xs;
};

const unsafeUpdateAt = <A>(
export const _unsafeUpdateAt = <A>(
i: number,
a: A,
as: ReadonlyArray<A>,
Expand All @@ -83,7 +83,7 @@ const unsafeUpdateAt = <A>(
}
};

const unsafeDeleteAt = <A>(
export const _unsafeDeleteAt = <A>(
i: number,
as: ReadonlyArray<A>,
): ReadonlyArray<A> => {
Expand Down Expand Up @@ -240,19 +240,19 @@ export const {

export const lookup = (i: number) =>
<A>(as: readonly A[]): O.Option<A> =>
isOutOfBounds(i, as) ? O.none : O.some(as[i]);
_isOutOfBounds(i, as) ? O.none : O.some(as[i]);

export const insertAt = <A>(i: number, a: A) =>
(as: readonly A[]): O.Option<readonly A[]> =>
isOutOfBounds(i, as) ? O.none : O.some(unsafeInsertAt(i, a, as));
_isOutOfBounds(i, as) ? O.none : O.some(_unsafeInsertAt(i, a, as));

export const updateAt = <A>(i: number, a: A) =>
(as: readonly A[]): O.Option<readonly A[]> =>
isOutOfBounds(i, as) ? O.none : O.some(unsafeUpdateAt(i, a, as));
_isOutOfBounds(i, as) ? O.none : O.some(_unsafeUpdateAt(i, a, as));

export const deleteAt = (i: number) =>
<A>(as: readonly A[]): O.Option<readonly A[]> =>
isOutOfBounds(i, as) ? O.none : O.some(unsafeDeleteAt(i, as));
_isOutOfBounds(i, as) ? O.none : O.some(_unsafeDeleteAt(i, as));

/***************************************************************************************************
* @section Sequence
Expand Down
9 changes: 8 additions & 1 deletion either.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ import type {
Refinement,
} from "./types.ts";

import * as O from "./option.ts";
import { createSequenceStruct, createSequenceTuple } from "./sequence.ts";
import { isNotNil } from "./fns.ts";
import { isNotNil, pipe } from "./fns.ts";
import * as D from "./derivations.ts";

/***************************************************************************************************
Expand Down Expand Up @@ -73,6 +74,12 @@ export const fold = <L, R, B>(
export const getOrElse = <E, A>(onLeft: (e: E) => A) =>
(ma: Either<E, A>): A => isLeft(ma) ? onLeft(ma.left) : ma.right;

export const getRight = <E, A>(ma: Either<E, A>): O.Option<A> =>
pipe(ma, fold(O.constNone, O.some));

export const getLeft = <E, A>(ma: Either<E, A>): O.Option<E> =>
pipe(ma, fold(O.some, O.constNone));

/***************************************************************************************************
* @section Combinators
**************************************************************************************************/
Expand Down
50 changes: 46 additions & 4 deletions iso.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import type * as TC from "./type_classes.ts";
import type { _0, _1 } from "./types.ts";
import type { $, _0, _1 } from "./types.ts";
import type { Either } from "./either.ts";
import type { Lens } from "./lens.ts";
import type { Optional } from "./optional.ts";
import type { Prism } from "./prism.ts";
import type { Traversal } from "./traversal.ts";

import * as O from "./option.ts";
import { constant, flow, identity } from "./fns.ts";
import * as L from "./lens.ts";
import * as P from "./prism.ts";
import * as OP from "./optional.ts";
import * as T from "./traversal.ts";
import { constant, flow, identity, pipe } from "./fns.ts";

/***************************************************************************************************
* @section Types
Expand Down Expand Up @@ -74,10 +79,28 @@ export const asTraversal = <S, A>(sa: Iso<S, A>): Traversal<S, A> => ({
* @section Pipeable Compose
**************************************************************************************************/

export const compose = <A, B>(ab: Iso<A, B>) =>
<S>(sa: Iso<S, A>): Iso<S, B> => Category.compose(sa, ab);

export const composeLens = <A, B>(ab: Lens<A, B>) =>
<S>(sa: Iso<S, A>): Lens<S, B> => L.compose(ab)(asLens(sa));

export const composePrism = <A, B>(ab: Prism<A, B>) =>
<S>(sa: Iso<S, A>): Prism<S, B> => P.compose(ab)(asPrism(sa));

export const composeOptional = <A, B>(ab: Optional<A, B>) =>
<S>(sa: Iso<S, A>): Optional<S, B> => OP.compose(ab)(asOptional(sa));

export const composeTraversal = <A, B>(ab: Traversal<A, B>) =>
<S>(sa: Iso<S, A>): Traversal<S, B> => T.compose(ab)(asTraversal(sa));

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

export const modify = <A>(f: (a: A) => A) =>
<S>(sa: Iso<S, A>) => (s: S): S => sa.reverseGet(f(sa.get(s)));

export const map = <A, B>(
ab: (a: A) => B,
ba: (b: B) => A,
Expand All @@ -92,5 +115,24 @@ export const reverse = <S, A>(sa: Iso<S, A>): Iso<A, S> => ({
reverseGet: sa.get,
});

export const modify = <A>(f: (a: A) => A) =>
<S>(sa: Iso<S, A>) => (s: S): S => sa.reverseGet(f(sa.get(s)));
export const traverse = <T>(
M: TC.Traversable<T>,
) =>
<S, A>(sta: Iso<S, $<T, [A]>>): Traversal<S, A> =>
pipe(
asTraversal(sta),
T.compose(T.fromTraversable(M)()),
);

/***************************************************************************************************
* @section Pipeable Over ADT
**************************************************************************************************/

export const some: <S, A>(soa: Iso<S, O.Option<A>>) => Prism<S, A> =
composePrism(P.some());

export const right: <S, E, A>(sea: Iso<S, Either<E, A>>) => Prism<S, A> =
composePrism(P.right());

export const left: <S, E, A>(sea: Iso<S, Either<E, A>>) => Prism<S, E> =
composePrism(P.left());
19 changes: 17 additions & 2 deletions lens.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import type * as TC from "./type_classes.ts";
import type { $, _0, _1, Predicate, Refinement } from "./types.ts";
import type { Either } from "./either.ts";
import type { Iso } from "./iso.ts";
import type { Prism } from "./prism.ts";
import type { Traversal } from "./traversal.ts";
import type { Optional } from "./optional.ts";

import * as OP from "./optional.ts";
import * as O from "./option.ts";
import * as I from "./iso.ts";
import * as R from "./record.ts";
import * as I from "./iso.ts";
import * as P from "./prism.ts";
import * as OP from "./optional.ts";
import * as T from "./traversal.ts";
import { constant, flow, identity, pipe } from "./fns.ts";

Expand Down Expand Up @@ -206,3 +208,16 @@ export const traverse = <T>(
asTraversal(sta),
T.compose(T.fromTraversable(M)()),
);

/***************************************************************************************************
* @section Pipeable Over ADT
**************************************************************************************************/

export const some: <S, A>(soa: Lens<S, O.Option<A>>) => Optional<S, A> =
composePrism(P.some());

export const right: <S, E, A>(sea: Lens<S, Either<E, A>>) => Optional<S, A> =
composePrism(P.right());

export const left: <S, E, A>(sea: Lens<S, Either<E, A>>) => Optional<S, E> =
composePrism(P.left());
15 changes: 15 additions & 0 deletions optional.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type * as TC from "./type_classes.ts";
import type { _0, _1, Predicate, Refinement } from "./types.ts";
import type { Either } from "./either.ts";
import type { Iso } from "./iso.ts";
import type { Lens } from "./lens.ts";
import type { Prism } from "./prism.ts";
Expand Down Expand Up @@ -214,3 +215,17 @@ export const atKey = (key: string) =>
sa: Optional<S, Readonly<Record<string, A>>>,
): Optional<S, O.Option<A>> =>
pipe(sa, compose(L.asOptional(L.atRecord<A>().at(key))));

/***************************************************************************************************
* @section Pipeable Over ADT
**************************************************************************************************/

export const some: <S, A>(soa: Optional<S, O.Option<A>>) => Optional<S, A> =
composePrism(P.some());

export const right: <S, E, A>(
sea: Optional<S, Either<E, A>>,
) => Optional<S, A> = composePrism(P.right());

export const left: <S, E, A>(sea: Optional<S, Either<E, A>>) => Optional<S, E> =
composePrism(P.left());
21 changes: 20 additions & 1 deletion prism.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import type * as TC from "./type_classes.ts";
import type { _0, _1, Predicate, Refinement } from "./types.ts";
import type { Iso } from "./iso.ts";
import type { Traversal } from "./traversal.ts";
import type { Optional } from "./optional.ts";

import * as I from "./iso.ts";
import * as L from "./lens.ts";
import * as O from "./option.ts";
import * as E from "./either.ts";
import * as OP from "./optional.ts";
import * as T from "./traversal.ts";
import { flow, identity, pipe } from "./fns.ts";
Expand Down Expand Up @@ -174,3 +174,22 @@ export const key = (key: string) =>
export const atKey = (key: string) =>
<S, A>(sa: Prism<S, Readonly<Record<string, A>>>): Optional<S, O.Option<A>> =>
composeLens(L.atRecord<A>().at(key))(sa);

/***************************************************************************************************
* @section Pipeable Over ADT
**************************************************************************************************/

export const some = <A>(): Prism<O.Option<A>, A> => ({
getOption: identity,
reverseGet: O.some,
});

export const right = <E, A>(): Prism<E.Either<E, A>, A> => ({
getOption: E.getRight,
reverseGet: E.right,
});

export const left = <E, A>(): Prism<E.Either<E, A>, E> => ({
getOption: E.getLeft,
reverseGet: E.left,
});
16 changes: 16 additions & 0 deletions traversal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import type { Lens } from "./lens.ts";
import type { Prism } from "./prism.ts";
import type { Optional } from "./optional.ts";
import type { Option } from "./option.ts";
import type { Either } from "./either.ts";

import * as Id from "./identity.ts";
import * as I from "./iso.ts";
Expand Down Expand Up @@ -151,6 +152,21 @@ export const atKey = (key: string) =>
): Traversal<S, Option<A>> =>
pipe(sa, compose(L.asTraversal(L.atRecord<A>().at(key))));

/***************************************************************************************************
* @section Pipeable Over ADT
**************************************************************************************************/

export const some: <S, A>(soa: Traversal<S, Option<A>>) => Traversal<S, A> =
composePrism(P.some());

export const right: <S, E, A>(
sea: Traversal<S, Either<E, A>>,
) => Traversal<S, A> = composePrism(P.right());

export const left: <S, E, A>(
sea: Traversal<S, Either<E, A>>,
) => Traversal<S, E> = composePrism(P.left());

/***************************************************************************************************
* @section Utilities
**************************************************************************************************/
Expand Down

0 comments on commit ab411b3

Please sign in to comment.