wip types
This commit is contained in:
parent
1e09ca655f
commit
853601f2c5
8 changed files with 426 additions and 188 deletions
|
@ -1,145 +1,145 @@
|
|||
import { id, map, ap, prepend, reduce, thunk } from '../../vendor/izuna/src/index.js'
|
||||
/** @import { Morphism } from './types.js' */
|
||||
import { Algebra, Monad } from './interfaces.js'
|
||||
import { curry } from '../..//vendor/izuna/src/index.js'
|
||||
|
||||
/** @import { Applicative, ApplicativeTypeRef, Morphism } from './types.js' */
|
||||
|
||||
|
||||
/** @import { InferredMorphism } from './types.js' */
|
||||
|
||||
const kleisli = curry((f, g, x) => f(x).chain(g))
|
||||
|
||||
/**
|
||||
* @template A
|
||||
* @typedef {Pure<A> | Impure<A>} Free
|
||||
* @template T
|
||||
* @type {Pure<T> | Impure<T>} Free
|
||||
*/
|
||||
export class Free extends Algebra(Monad) {
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value
|
||||
*/
|
||||
static of(value) {
|
||||
return new Pure(value)
|
||||
}
|
||||
}
|
||||
|
||||
/** @template A */
|
||||
export class Pure {
|
||||
/** @template T */
|
||||
export class Pure extends Free {
|
||||
#value
|
||||
|
||||
/**
|
||||
* @param {A} value
|
||||
* @param {T} value
|
||||
*/
|
||||
constructor(value) {
|
||||
super()
|
||||
this.#value = value
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {Free<B>} B
|
||||
* @param {Morphism<A, B>} f
|
||||
* @returns {B}
|
||||
* @param {InferredMorphism<T>} f
|
||||
*/
|
||||
chain(f) {
|
||||
return f(this.#value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template B
|
||||
* @param {Morphism<A, B>} f
|
||||
* @returns {Free<B>}
|
||||
* @param {InferredMorphism<T>} f
|
||||
*/
|
||||
map(f) {
|
||||
// @ts-ignore
|
||||
return pure(f(this.#value))
|
||||
}
|
||||
|
||||
/**
|
||||
* @template B, C
|
||||
* @param {Free<B>} b
|
||||
* @returns {Free<C>}
|
||||
* @template U
|
||||
* @template {Applicative<U>} M
|
||||
* @template {ApplicativeTypeRef<U, M>} TypeRef
|
||||
* @param {TypeRef} _A
|
||||
* @param {*} f
|
||||
* @returns {M}
|
||||
*/
|
||||
ap(b) {
|
||||
return b.map(this.#value)
|
||||
traverse(_A, f) {
|
||||
return f(this.#value).map(pure)
|
||||
}
|
||||
|
||||
traverse(f, of) {
|
||||
return of(this.map(f))
|
||||
run() {
|
||||
return this.#value
|
||||
}
|
||||
|
||||
/**
|
||||
* @template B
|
||||
* @param {(acc: B, value: A) => B} f
|
||||
* @param {B} init
|
||||
* @returns {B}
|
||||
*/
|
||||
reduce(f, init) {
|
||||
return f(init, this.#value)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @template A
|
||||
* @typedef {<B>(value: A) => Free<B>} Computation
|
||||
*/
|
||||
|
||||
/** @template A */
|
||||
export class Impure {
|
||||
/** @template T */
|
||||
export class Impure extends Free {
|
||||
#value
|
||||
#next
|
||||
|
||||
/**
|
||||
* @param {() => Free<any>} next
|
||||
* @param {T} value
|
||||
* @param {(value: T) => Free<any>} f
|
||||
*/
|
||||
constructor(next) {
|
||||
this.#next = next
|
||||
constructor(value, f) {
|
||||
super()
|
||||
this.#value = value
|
||||
this.#next = f
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {Free<B>} B
|
||||
* @param {Morphism<A, B>} f
|
||||
* @returns {B}
|
||||
* @param {<U>(value: T) => Free<U>} f
|
||||
*/
|
||||
chain(f) {
|
||||
// @ts-ignore
|
||||
return new Impure(
|
||||
// @ts-ignore
|
||||
() => this.#next().chain(f)
|
||||
)
|
||||
return impure(this.#value, kleisli(this.#next, f))
|
||||
}
|
||||
|
||||
/**
|
||||
* @template B
|
||||
* @param {Morphism<A, B>} f
|
||||
* @returns {Free<B>}
|
||||
*/
|
||||
map(f) {
|
||||
return new Impure(
|
||||
// @ts-ignore
|
||||
() => this.#next().map(f)
|
||||
return impure(
|
||||
this.#value,
|
||||
y => f(y).chain(f)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template B
|
||||
* @param {Free<any>} b
|
||||
* @returns {Free<B>}
|
||||
* @template U
|
||||
* @template {Applicative<U>} M
|
||||
* @template {ApplicativeTypeRef<U, M>} TypeRef
|
||||
* @param {TypeRef} A
|
||||
* @param {(value: T) => M} f
|
||||
* @returns {M}
|
||||
*/
|
||||
ap(b) {
|
||||
return new Impure(
|
||||
// @ts-ignore
|
||||
() => b.map(this.#next())
|
||||
traverse(A, f) {
|
||||
const fb = f(this.#value)
|
||||
const rest = this.#next(this.#value).traverse(A, f)
|
||||
|
||||
return rest.ap(
|
||||
fb.map(b => next => impure(b, () => next))
|
||||
)
|
||||
}
|
||||
|
||||
traverse(f, of) {
|
||||
return of(liftF(
|
||||
this.#next().traverse(f, of)
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
* @template B
|
||||
* @param {(acc: B, value: A) => B} f
|
||||
* @param {B} acc
|
||||
* @returns {B}
|
||||
*/
|
||||
reduce(f, acc) {
|
||||
|
||||
run() {
|
||||
return this.#next(this.#value).run()
|
||||
}
|
||||
}
|
||||
|
||||
const sequence = (of, iter) => {
|
||||
return reduce((acc, x) => {
|
||||
ap(acc, map(prepend, x))
|
||||
}, of([]), iter)
|
||||
}
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} x
|
||||
* @returns {Pure<T>}
|
||||
*/
|
||||
const pure = x => new Pure(x)
|
||||
|
||||
export const pure = x => new Pure(x)
|
||||
export const impure = f => new Impure(f)
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} x
|
||||
* @param {(value: T) => Free<any>} f
|
||||
* @returns {Impure<T>}
|
||||
*/
|
||||
const impure = (x, f) => new Impure(x, f)
|
||||
|
||||
export const liftF = effect => impure(thunk(effect))
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} x
|
||||
* @returns Impure<T>
|
||||
*/
|
||||
export const liftF = x => impure(x, pure)
|
||||
import { id, Identity } from './identity.js'
|
||||
import { Option, some } from './option.js'
|
||||
|
||||
const a = liftF(1).chain(() => liftF(2)).traverse(Option, x => some(1))
|
||||
console.log(a.chain(x => x).run())
|
||||
|
||||
|
|
65
src/algebra/identity.js
Normal file
65
src/algebra/identity.js
Normal file
|
@ -0,0 +1,65 @@
|
|||
import { Algebra, Comonad, Monad } from './interfaces.js'
|
||||
|
||||
/** @import { Morphism } from './types.js' */
|
||||
|
||||
/** @template T */
|
||||
export class Identity extends Algebra(Monad, Comonad) {
|
||||
#value
|
||||
|
||||
/**
|
||||
* @param {T} value
|
||||
*/
|
||||
constructor(value) {
|
||||
super()
|
||||
|
||||
this.#value = value
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value
|
||||
*/
|
||||
static of(value) {
|
||||
return id(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @param {Morphism<T, U>} f
|
||||
*/
|
||||
map(f) {
|
||||
return id(f(this.#value))
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @this {Identity<Morphism<U, T>>}
|
||||
* @param {Identity<U>} b
|
||||
* @returns {Identity<T>}
|
||||
*/
|
||||
ap(b) {
|
||||
return b.map(this.#value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @param {Morphism<T, Identity<U>>} f
|
||||
*/
|
||||
chain(f) {
|
||||
return f(this.#value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {(value: Identity<T>) => T} f
|
||||
*/
|
||||
extend(f) {
|
||||
return id(f(this))
|
||||
}
|
||||
|
||||
extract() {
|
||||
return this.#value
|
||||
}
|
||||
}
|
||||
|
||||
export const id = value => new Identity(value)
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { mix } from '../mixin.js'
|
||||
import { mix, Mixin } from '../mixin.js'
|
||||
|
||||
/** @import { MixinFunction } from '../mixin.js' */
|
||||
/** @import { Fn } from './types.js' */
|
||||
|
@ -130,7 +130,7 @@ class Method {
|
|||
*/
|
||||
_getInstallationPoint(target) {
|
||||
switch (this.#type) {
|
||||
case 0: return target.prototype
|
||||
case 0: return Object.getPrototypeOf(target)
|
||||
case 1: return target
|
||||
default: return target
|
||||
}
|
||||
|
@ -152,6 +152,9 @@ const Implementations = Symbol()
|
|||
class Interface {
|
||||
#name
|
||||
|
||||
/** @type {MixinFunction} */
|
||||
#mixin
|
||||
|
||||
/** @type {Set<Method>} */
|
||||
#methods = new Set()
|
||||
|
||||
|
@ -161,20 +164,20 @@ class Interface {
|
|||
/** @param {string} name */
|
||||
constructor(name) {
|
||||
this.#name = name
|
||||
this.#mixin = Mixin(this.build.bind(this))
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this.#name
|
||||
}
|
||||
|
||||
findInterfaces() {
|
||||
findInterfaces(type) {
|
||||
let interfaces = new Set()
|
||||
let current = Object.getPrototypeOf(this)
|
||||
console.log(current)
|
||||
let current = type
|
||||
|
||||
while (current != null) {
|
||||
interfaces = new Set(current[Implementations])
|
||||
current = Object.getPrototypeOf(this)
|
||||
interfaces = new Set(current[Implementations]).union(interfaces)
|
||||
current = Object.getPrototypeOf(current)
|
||||
}
|
||||
|
||||
return interfaces
|
||||
|
@ -185,7 +188,7 @@ class Interface {
|
|||
* @returns {boolean}
|
||||
*/
|
||||
implementedBy(type) {
|
||||
return this.findInterfaces().has(type)
|
||||
return this.findInterfaces(type).has(this)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -212,10 +215,10 @@ class Interface {
|
|||
}
|
||||
|
||||
/**
|
||||
* @returns {MixinWrapper}
|
||||
* @returns {MixinFunction}
|
||||
*/
|
||||
intoWrapper() {
|
||||
return this.build.bind(this)
|
||||
asMixin() {
|
||||
return this.#mixin
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -223,19 +226,20 @@ class Interface {
|
|||
*/
|
||||
build(base) {
|
||||
const interfaces = [...this.#interfaces.values()]
|
||||
const wrappers = interfaces.map(x => x.intoWrapper())
|
||||
const mixins = interfaces.map(x => x.asMixin())
|
||||
|
||||
const Interfaces = mix(base).with(...wrappers)
|
||||
const Interfaces = mix(base).with(...mixins)
|
||||
|
||||
//const Algebra = class extends Interfaces { }
|
||||
const Algebra = class extends Interfaces { }
|
||||
|
||||
for (const method of this.#methods) {
|
||||
method.implement(this, Interfaces)
|
||||
method.implement(this, Algebra)
|
||||
}
|
||||
|
||||
Interfaces.prototype[Implementations] = new Set([...interfaces])
|
||||
const prototype = Object.getPrototypeOf(Algebra)
|
||||
prototype[Implementations] = new Set(interfaces)
|
||||
|
||||
return Interfaces
|
||||
return Algebra
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -272,7 +276,7 @@ export const apply = (methodName, f, obj) => {
|
|||
* @returns {(...algebras: Interface[]) => FunctionConstructor}
|
||||
*/
|
||||
export const AlgebraWithBase = base => (...algebras) => {
|
||||
return mix(base).with(...algebras.map(x => x.intoWrapper()))
|
||||
return mix(base).with(...algebras.map(x => x.asMixin()))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -2,8 +2,22 @@ import { Algebra, Foldable, Monad, Monoid, Setoid } from './interfaces.js'
|
|||
|
||||
/** @import { Morphism } from './types.js' */
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @type {Some<T> | None<T>} Option
|
||||
*/
|
||||
export class Option extends Algebra(Setoid, Monad, Foldable) {
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value
|
||||
*/
|
||||
static of(value) {
|
||||
return Some.of(value)
|
||||
}
|
||||
}
|
||||
|
||||
/** @template T */
|
||||
export class Some extends Algebra(Setoid, Monoid, Monad, Foldable) {
|
||||
export class Some extends Option {
|
||||
/** @type {T} */
|
||||
#value
|
||||
|
||||
|
@ -23,11 +37,18 @@ export class Some extends Algebra(Setoid, Monoid, Monad, Foldable) {
|
|||
return new Some(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Option<T>} other
|
||||
*/
|
||||
equals(other) {
|
||||
return other instanceof Some &&
|
||||
other.chain(v => v === this.#value)
|
||||
}
|
||||
|
||||
ap(b) {
|
||||
return b.chain(f => this.map(f))
|
||||
}
|
||||
|
||||
/**
|
||||
* @template R
|
||||
* @param {Morphism<T, R>} f
|
||||
|
@ -64,18 +85,21 @@ export class Some extends Algebra(Setoid, Monoid, Monad, Foldable) {
|
|||
reduce(f, init) {
|
||||
return f(init, this.#value)
|
||||
}
|
||||
|
||||
empty() {
|
||||
return Option.empty()
|
||||
}
|
||||
}
|
||||
|
||||
/** @template T */
|
||||
export class None extends Algebra(Setoid, Monoid, Monad, Foldable) {
|
||||
export class None extends Option {
|
||||
/**
|
||||
* @param {Option<T>} other
|
||||
*/
|
||||
equals(other) {
|
||||
return other === none
|
||||
}
|
||||
|
||||
ap(_b) {
|
||||
return this
|
||||
}
|
||||
|
||||
/**
|
||||
* @template R
|
||||
* @param {Morphism<T, R>} _f
|
||||
|
@ -118,24 +142,6 @@ export class None extends Algebra(Setoid, Monoid, Monad, Foldable) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @type {Some<T> | None<T>} Option
|
||||
*/
|
||||
export class Option {
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value
|
||||
*/
|
||||
static of(value) {
|
||||
return Some.of(value)
|
||||
}
|
||||
|
||||
static empty() {
|
||||
return none
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value
|
||||
|
|
|
@ -1,11 +1,26 @@
|
|||
import { Algebra, Foldable, Functor, Monad, Setoid } from './interfaces.js'
|
||||
import { Algebra, Foldable, Monad, Setoid } from './interfaces.js'
|
||||
|
||||
/** @import { Morphism } from './types.js' */
|
||||
|
||||
/**
|
||||
* @template T, E
|
||||
* @type {Ok<T, E> | Err<T, E>} Result
|
||||
*/
|
||||
export class Ok extends Algebra(Setoid, Monad, Foldable) {
|
||||
export class Result extends Algebra(Setoid, Monad, Foldable) {
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value
|
||||
* @returns {Ok}
|
||||
*/
|
||||
static of(value) {
|
||||
return new Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T, E
|
||||
*/
|
||||
export class Ok extends Result {
|
||||
/** @type {T} */
|
||||
#value
|
||||
|
||||
|
@ -20,7 +35,7 @@ export class Ok extends Algebra(Setoid, Monad, Foldable) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @param {Ok<T, E>} other
|
||||
* @param {Result<T, E>} other
|
||||
*/
|
||||
equals(other) {
|
||||
return other instanceof Ok &&
|
||||
|
@ -86,7 +101,7 @@ export class Ok extends Algebra(Setoid, Monad, Foldable) {
|
|||
/**
|
||||
* @template T, E
|
||||
*/
|
||||
export class Err extends Algebra(Setoid, Functor, Monad) {
|
||||
export class Err extends Result {
|
||||
/** @type {E} */
|
||||
#value
|
||||
|
||||
|
@ -100,6 +115,9 @@ export class Err extends Algebra(Setoid, Functor, Monad) {
|
|||
this.#value = value
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Result<T, E>} other
|
||||
*/
|
||||
equals(other) {
|
||||
return other instanceof Err &&
|
||||
other.chain(v => v === this.#value)
|
||||
|
@ -158,21 +176,6 @@ export class Err extends Algebra(Setoid, Functor, Monad) {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T, E
|
||||
* @type {Ok<T, E> | Err<T, E>} Result
|
||||
*/
|
||||
export class Result {
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value
|
||||
* @returns {Ok}
|
||||
*/
|
||||
static of(value) {
|
||||
return new Ok(value)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T, E
|
||||
* @param {T} v
|
||||
|
|
|
@ -17,17 +17,197 @@ export default {}
|
|||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {(value: T) => Chain<T>} ChainConstructor
|
||||
* @typedef {{
|
||||
equals: (b: Setoid<T>) => boolean
|
||||
* }} Setoid
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {<R>(f: Morphism<T, R>) => R} chain
|
||||
* @typedef {{
|
||||
lte: (b: Ord<T>) => boolean
|
||||
* }} Ord
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T, U
|
||||
* @typedef {*} Semigroupoid
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template I, J
|
||||
* @typedef {{
|
||||
compose: <K>(b: Semigroupoid<J, K>) => Semigroupoid<I, K>
|
||||
* }} Category
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T, U
|
||||
* @template {Category<T, U>} M
|
||||
* @typedef {{
|
||||
id: () => (value: T) => M
|
||||
* }} CategoryTypeRef
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {{ chain: chain<T> }} Chain
|
||||
* @typedef {{
|
||||
concat: (b: Semigroup<T>) => Semigroup<T>
|
||||
* }} Semigroup
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef{Semigroup<T>} Monoid
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template {Monoid<T>} M
|
||||
* @typedef {{
|
||||
empty: () => M
|
||||
* }} MonoidTypeRef
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {
|
||||
Monoid<T> &
|
||||
{ invert: () => Group<T> }
|
||||
* } Group
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {{
|
||||
filter: <U>(f: (acc: U, val: T) => U, init: U) => U
|
||||
* }} Filterable
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {{
|
||||
map: <U>(f: Morphism<T, U>) => Functor<U>
|
||||
* }} Functor
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {{
|
||||
contramap: <U>(f: Morphism<U, T>) => Contravariant<T>
|
||||
* }} Contravariant
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {{
|
||||
ap: <U>(f: Apply<Morphism<T, U>>) => Apply<U>
|
||||
* }} Apply
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {{
|
||||
ap: <U>(b: Applicative<Morphism<U, T>>) => Applicative<T>,
|
||||
map: <U>(f: Morphism<T, U>) => Applicative<U>
|
||||
* }} Applicative
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template {Applicative<T>} M
|
||||
* @typedef {{
|
||||
of: (value: T) => M
|
||||
* }} ApplicativeTypeRef
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {
|
||||
Functor<T> &
|
||||
{ alt: (b: Alt<T>) => Alt<T> }
|
||||
* } Alt
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Alt<T>} Plus
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template {Plus<T>} M
|
||||
* @typedef {
|
||||
Alt<T> &
|
||||
{ zero: () => M }
|
||||
* } PlusTypeRef
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Applicative<T> & Plus<T>} Alternative
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template {Applicative<T> & Plus<T>} M
|
||||
* @typedef {ApplicativeTypeRef<T, M> & PlusTypeRef<T, M>} AlternativeTypeRef
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {{
|
||||
filter: <U>(f: (acc: U, val: T) => U, init: U) => U
|
||||
* }} Foldable
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {
|
||||
Functor<T> & Foldable<T> &
|
||||
{ traverse: <U>(A: ApplicativeTypeRef<U, Applicative<U>>, f: (val: T) => Applicative<U>) => Applicative<Traversable<U>> }
|
||||
* } Traversable
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {
|
||||
Apply<T> &
|
||||
{ chain: <U>(f: (value: T) => Chain<U>) => Chain<U> }
|
||||
* } Chain
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Chain<T>} ChainRec
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Functor<T> & Applicative<T> & Chain<T>} Monad
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @template {Monad<T>} M
|
||||
* @typedef {ApplicativeTypeRef<T, M>} MonadTypeDef
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {
|
||||
Functor<T> &
|
||||
{ extend: <U>(f: (val: Extend<T>) => U) => Extend<U>}
|
||||
* } Extend
|
||||
*/
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {
|
||||
Extend<T> &
|
||||
{ extract: () => T }
|
||||
* } Comonad<T>
|
||||
*/
|
||||
|
||||
/** @typedef {(...args: any[]) => any} Fn */
|
||||
|
||||
|
|
|
@ -246,7 +246,7 @@ class MixinBuilder {
|
|||
/**
|
||||
* Applies `mixins` in order to the superclass given to `mix()`.
|
||||
*
|
||||
* @param {Array.<Mixin>} mixins
|
||||
* @param {Array.<MixinFunction>} mixins
|
||||
* @return {FunctionConstructor} a subclass of `superclass` with `mixins` applied
|
||||
*/
|
||||
with(...mixins) {
|
||||
|
|
|
@ -1,28 +1,29 @@
|
|||
// @ts-nocheck
|
||||
import { it, assert } from 'folktest'
|
||||
import { Option, Result } from '../../src/index.js'
|
||||
import { Chain, Ord, Setoid } from '../../src/algebra/interfaces.js'
|
||||
import { IO, List, Option, Reader, Result, Some } from '../../src/index.js'
|
||||
import { Applicative, Chain, Ord, Setoid } from '../../src/algebra/interfaces.js'
|
||||
|
||||
const Algebra = [Option, Result]
|
||||
const Algebra = [Option, Result, List, IO, Reader]
|
||||
|
||||
const inc = x => F.of(x + 1)
|
||||
const dbl = x => F.of(x * 2)
|
||||
const inc = F => x => F.of(x + 1)
|
||||
const dbl = F => x => F.of(x * 2)
|
||||
|
||||
const impl = i => m => i.implementedBy(m)
|
||||
|
||||
export const Tests = [
|
||||
it('unit is a left identity', () => {
|
||||
console.log(Chain.implementedBy(Result))
|
||||
Algebra.filter(impl(Chain)).forEach(algebra => {
|
||||
Algebra.filter(impl(Setoid, Applicative, Chain)).forEach(algebra => {
|
||||
const f = inc(algebra)
|
||||
|
||||
assert(
|
||||
algebra.of(1).chain(inc) == inc(1),
|
||||
algebra.of(1).chain(f).equals(f(1)),
|
||||
`${algebra.name} is not a left identity`
|
||||
)
|
||||
})
|
||||
}),
|
||||
|
||||
it('unit is a right identity', () => {
|
||||
Algebra.filter(impl(Chain)).forEach(algebra => {
|
||||
Algebra.filter(impl(Setoid, Applicative, Chain)).forEach(algebra => {
|
||||
const m = algebra.of(1)
|
||||
|
||||
assert(
|
||||
|
@ -33,36 +34,15 @@ export const Tests = [
|
|||
}),
|
||||
|
||||
it('unit is associative', () => {
|
||||
Algebra.filter(impl(Chain)).forEach(algebra => {
|
||||
Algebra.filter(impl(Setoid, Applicative, Chain)).forEach(algebra => {
|
||||
const a = algebra.of(1)
|
||||
const f = inc(algebra)
|
||||
const g = dbl(algebra)
|
||||
|
||||
const x = a.chain(inc).chain(dbl)
|
||||
const y = a.chain(x => inc(x).chain(dbl))
|
||||
const x = a.chain(f).chain(g)
|
||||
const y = a.chain(x => f(x).chain(g))
|
||||
|
||||
assert(x == y, `${algebra.name} is not associative`)
|
||||
assert(x.equals(y), `${algebra.name} is not associative`)
|
||||
})
|
||||
}),
|
||||
|
||||
it('unit is reflexive', () => {
|
||||
Algebra.filter(impl(Setoid)).forEach(algebra => {
|
||||
const a = algebra.of(1)
|
||||
assert(a.equals(a), `${algebra.name} is not reflexive`)
|
||||
})
|
||||
}),
|
||||
|
||||
it('unit is symmetrical', () => {
|
||||
Algebra.filter(impl(Setoid)).forEach(algebra => {
|
||||
const a = algebra.of(1)
|
||||
const b = algebra.of(1)
|
||||
assert(a.equals(b) && b.equals(a), `${algebra.name} is not symmetrical`)
|
||||
})
|
||||
}),
|
||||
|
||||
it('unit is antisymmetrical', () => {
|
||||
Algebra.filter(impl(Ord)).forEach(algebra => {
|
||||
const a = algebra.of(1)
|
||||
const b = algebra.of(1)
|
||||
assert(a.lte(b) && b.lte(a) && a.equals(b))
|
||||
})
|
||||
})
|
||||
]
|
||||
|
|
Loading…
Add table
Reference in a new issue