Functional programming module
This commit is contained in:
parent
a8f8bdda99
commit
9ddda03bd6
28
src/lib/util/basic/either.ts
Normal file
28
src/lib/util/basic/either.ts
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
export type Left<E> = {readonly _tag: 'Left', readonly left: E}
|
||||||
|
export type Right<A> = {readonly _tag: 'Right', readonly right: A}
|
||||||
|
export type Either<E, A> = Left<E> | Right<A>
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
export const left = <E>(e: E): Either<E, never> => ({
|
||||||
|
_tag: 'Left',
|
||||||
|
left: e,
|
||||||
|
})
|
||||||
|
export const right = <A>(a: A): Either<never, A> => ({
|
||||||
|
_tag: 'Right',
|
||||||
|
right: a,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Operations
|
||||||
|
export const eitherMap =
|
||||||
|
<E, A, B>(f: (a: A) => B) => (fa: Either<E, A>): Either<E, B> =>
|
||||||
|
fa._tag === 'Right' ? right(f(fa.right)) : fa
|
||||||
|
|
||||||
|
export const eitherChain =
|
||||||
|
<E, A, B>(f: (a: A) => Either<E, B>) =>
|
||||||
|
(fa: Either<E, A>): Either<E, B> =>
|
||||||
|
fa._tag === 'Right' ? f(fa.right) : fa
|
||||||
|
|
||||||
|
export const fold =
|
||||||
|
<E, A, B>(onLeft: (e: E) => B, onRight: (a: A) => B) =>
|
||||||
|
(fa: Either<E, A>): B =>
|
||||||
|
fa._tag === 'Left' ? onLeft(fa.left) : onRight(fa.right)
|
||||||
36
src/lib/util/basic/list.ts
Normal file
36
src/lib/util/basic/list.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
export type Nil = { readonly _tag: 'Nil' };
|
||||||
|
export type Cons<A> = {
|
||||||
|
readonly _tag: 'Cons';
|
||||||
|
readonly head: A;
|
||||||
|
readonly tail: List<A>;
|
||||||
|
};
|
||||||
|
export type List<A> = Nil | Cons<A>;
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
export const nil: Nil = { _tag: 'Nil' };
|
||||||
|
export const cons = <A>(head: A, tail: List<A>): List<A> => ({
|
||||||
|
_tag: 'Cons',
|
||||||
|
head,
|
||||||
|
tail,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Operations
|
||||||
|
type ListMap = <A, B>(f: (a: A) => B) => (fa: List<A>) => List<B>;
|
||||||
|
export const listMap: ListMap = (f) => (fa) =>
|
||||||
|
fa._tag === 'Cons' ? cons(f(fa.head), listMap(f)(fa.tail)) : nil;
|
||||||
|
|
||||||
|
type ListReduce = <A, B>(
|
||||||
|
f: (b: B, a: A) => B,
|
||||||
|
initial: B
|
||||||
|
) => (fa: List<A>) => B;
|
||||||
|
export const listReduce: ListReduce = (f, initial) => (fa) =>
|
||||||
|
fa._tag === 'Cons' ? listReduce(f, f(initial, fa.head))(fa.tail) : initial;
|
||||||
|
|
||||||
|
// Helpers
|
||||||
|
type FromArray = <A>(arr: Array<A>) => List<A>;
|
||||||
|
export const fromArray: FromArray = <A>(arr: Array<A>) =>
|
||||||
|
arr.reduceRight((acc: List<A>, val: A) => cons(val, acc), nil as List<A>);
|
||||||
|
|
||||||
|
type ToArray = <A>(fa: List<A>) => Array<A>;
|
||||||
|
export const toArray: ToArray = <A>(fa: List<A>) =>
|
||||||
|
listReduce<A, Array<A>>((acc, val) => [...acc, val], [] as Array<A>)(fa);
|
||||||
22
src/lib/util/basic/option.ts
Normal file
22
src/lib/util/basic/option.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
export type None = {readonly _tag: 'None'}
|
||||||
|
export type Some<A> = {readonly _tag: 'Some', readonly value: A}
|
||||||
|
export type Option<A> = Some<A> | None
|
||||||
|
|
||||||
|
// Constructors
|
||||||
|
export const none: Option<never> = {_tag: 'None'}
|
||||||
|
export const some = <A>(value: A): Option<A> => ({
|
||||||
|
_tag: 'Some',
|
||||||
|
value,
|
||||||
|
})
|
||||||
|
|
||||||
|
// Operations
|
||||||
|
export const map =
|
||||||
|
<A, B>(f: (a: A) => B) => (fa: Option<A>): Option<B> =>
|
||||||
|
fa._tag === 'Some' ? some(f(fa.value)) : none
|
||||||
|
|
||||||
|
export const chain =
|
||||||
|
<A, B>(f: (a: A) => Option<B>) => (fa: Option<A>): Option<B> =>
|
||||||
|
fa._tag === 'Some' ? f(fa.value) : none
|
||||||
|
|
||||||
|
export const getOrElse = <A>(defaultValue: A) => (fa: Option<A>): A =>
|
||||||
|
fa._tag === 'Some' ? fa.value : defaultValue
|
||||||
22
src/lib/util/basic/utility.ts
Normal file
22
src/lib/util/basic/utility.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { type Either, left, right } from './either'
|
||||||
|
import { none, type Option, some } from './option'
|
||||||
|
|
||||||
|
// Pipe function for composition
|
||||||
|
export const pipe = <A, B, C>(
|
||||||
|
a: A,
|
||||||
|
ab: (a: A) => B,
|
||||||
|
bc: (b: B) => C,
|
||||||
|
): C => bc(ab(a))
|
||||||
|
|
||||||
|
// TryCatch helper using Either
|
||||||
|
export const tryCatch = <A>(f: () => A): Either<Error, A> => {
|
||||||
|
try {
|
||||||
|
return right(f())
|
||||||
|
} catch (e) {
|
||||||
|
return left(e instanceof Error ? e : new Error(String(e)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Option from nullable
|
||||||
|
export const fromNullable = <A>(a: A | null | undefined): Option<A> =>
|
||||||
|
a == null ? none : some(a)
|
||||||
Loading…
Reference in New Issue
Block a user