a lightweight algebraic structure library
Find a file
2025-04-06 15:52:57 -05:00
src finished list 2025-04-06 15:52:57 -05:00
tests overhaul monad impls 2025-03-29 06:09:49 -05:00
.gitignore initial commit 2025-03-26 21:28:33 -05:00
jsconfig.json wip cons list 2025-04-06 12:50:31 -05:00
package-lock.json :3c 2025-04-05 22:39:39 -05:00
package.json :3c 2025-04-05 22:39:39 -05:00
README.md im really bad at proofreading orz 2025-03-31 21:15:01 -05:00

kojima

a small functional/monad library

Usage

Example

import { Option, Some, None, Result, Ok, Err } from 'kojima'

const maybe = Option.of('thing')
maybe.isSome() // true
const isnt = maybe.chain(None).map(_x => 'other') // None
isnt.isSome() // false

const result = Result.of('3:41am')
result.isOk() // true

result.chain(() => Err(new Error(-Infinity)))
    .map(_x => '4:10am')
    .chain(Ok)

result.isErr() // true

// crimes!
None == None() // true

Documentation

curry

(* -> a) -> (* -> a)

Returns a curried equivalent of the provided function.

import { curry } from './curry.js'

const add = (a, b, c) => a + b + c
const curriedAdd = curry(add)
const add1 = curriedAdd(1)
const add3 = add1(2)
const add4 = curriedAdd(2, 2)

const six = add3(3) // 6
const eight = add4(2) // 7
const twelve = curriedAdd(4, 4, 4) // 12

Option

Option<T> = Some<T> | None

Represents a value which may not exist.

Methods

of :: Option f => a -> f a

Option.of<a>(value: a) -> Option<a>

Creates a new Some<T> from T

const some = Option.of(1) // Some<number>(1)
zero :: Option f => () -> f a

Option.zero() -> Option<()>

Creates a new None

const none = Option.zero() // None
chain :: Option m => m a ~> (a -> m b) -> m b

Option<T>.chain<U>(fn: (value: T) -> Option<U>) -> Option<U>

Transform a Option<T> into Option<U> by applying fn(T) -> Option<U>.

const some = Option.of(1)
const next = some.chain(x => Some(`value ${x}`)) // Some<string>('value 1')
None.chain(x => Some(`value ${x}`)) // None
const none = some.chain(None) // None
none.chain(() => Some(1)) // None
map :: Option f => f a ~> (a -> b) -> f b

Option<T>.map<U>(fn: (value: T) -> U) -> Option<U>

alt :: Option f => f a ~> f a -> f a

Option<T>.alt(other: Option<T>) -> Option<T>

Choose between either the first or second Option based on existence.

const some = Option.of(1)
const none = Option.zero()

some.alt(none) // Some<number>(1)
none.alt(some) // Some<number>(1)
some.alt(Some(2)) // Some<number>(1)
Some(2).alt(some) // Some<number>(2)
none.alt(None) // None
None.alt(none) // None
fold :: Option f => f a ~> ((b, a) -> b, b) -> b

Option<T>.fold<U>(fn: ((acc: U, value: T) -> U, initial: U) -> U) -> U

Fold over a Option, accumulating the value. This is unwrap_or_default in Rust.

const some = Some(1)
some.fold((acc, x) => x), 2) // 1
some.fold((acc, x) => acc), 2) // 2

const none = Option.zero()
none.fold((acc, x) => x, 2) // 2
isSome :: Option f => () -> boolean

Option<T>.isSome() -> boolean

Returns a boolean based on whether the Option is Some<T>

Some(1).isSome() // true
None.isSome() // false
isNone :: Option f => () -> boolean

Option<T>.isNone() -> boolean

Returns a boolean based on whether the Option is None

Some(1).isNone() // false
None.isNone() // true

Result

Result<T, E> = Ok<T> | Err<E>

Represents the result of a computation which may fail.

Methods

of :: Result f => a -> f a

Result.of<a>(value: a) -> Result<a, ()>

Creates a new Ok<T> from T

const ok = Result.of(1) // Ok<number>(1)
zero :: Result f => () -> f a

Result.zero() -> Result<(), ()>

Creates a new Err<()>

const err = Result.zero() // Err<()>()
chain :: Result m => m a ~> (a -> m b) -> m b

Result<T, E>.chain<U>(fn: (value: T) -> Result<U, E>) -> Result<U, E>

Transform a Result<T, E> into Result<U, E> by applying fn(T) -> Result<U, E>.

const ok = Result.of(1)
const next = ok.chain(x => Ok(`value ${x}`)) // Ok<string>('value 1')
Err(0).chain(x => Ok(1)) // Err(0)
const err = next.chain(() => Err(0)) // Err(0)
err.chain(() => Ok(1)) // Err(0)
map :: Result f => f a ~> (a -> b) -> f b

Result<T, E>.map<U>(fn: (value: T) -> U) -> Result<U, E>

Transform a Result<T, E> into a Result<U, E> by applying fn(T) -> U

const ok = Result.of(1)
const next = ok.map(x => `value ${x}`) // Ok<string>('value 1')
Err(0).map(x => Ok(1)) // Err(0)

alt :: Result f => f a ~> f a -> f a

Result<T, E>.alt(other: Result<T, E>) -> Result<T, E>

Choose between either the first or second Result based on success.

const ok = Result.of(1)
const err = Result.zero()

ok.alt(err) // Ok<number>(1)
err.alt(ok) // Ok<number>(1)
ok.alt(Ok(2)) // Ok<number>(1)
Ok(2).alt(ok) // Ok<number>(2)
err.alt(Err(new Error('wont see this'))) // Err<()>()
Err(new Error('hi! :3')).alt(err) // Err<Error>(new Error('hi! :3'))
fold :: Result f => f a ~> ((b, a) -> b, b) -> b

Result<T, E>.fold<U>(fn: ((acc: U, value: T) -> U, initial: U) -> U) -> U

Fold over a Result, accumulating the value. This is unwrap_or_default in Rust.

const ok = Ok(1)
ok.fold((acc, x) => x), 2) // 1
ok.fold((acc, x) => acc), 2) // 2

const err = Result.zero()
err.fold((acc, x) => x, 2) // 2
bimap :: Result f => f a c ~> (a -> b, c -> d) -> f b d

Result<T1, E1>.bimap<T2, E2>(x: (value: T1) -> T2, y: (error: E1) -> E2) -> Result<T2, E2>

const ok = Ok(1)

ok.bimap(
    x => x + 1,
    y => y * 2
) // Ok(2)

const err = Err(4)

err.bimap(
    x => x + 1,
    y => y * 2
) // Err(8)
isOk :: Result f => () -> boolean

Result<T, E>.isOk() -> boolean

Returns a boolean based on whether the Result is Ok<T>

Ok(1).isOk() // true
Err(1).isOk() // false
isErr :: Result f => () -> boolean

Result<T, E>.isErr() -> boolean

Returns a boolean based on whether the Result is Err<E>

Ok(1).isErr() // false
Err(1).isErr() // true