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 { Algebra, Monad } from './interfaces.js'
|
||||||
/** @import { Morphism } from './types.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
|
* @template T
|
||||||
* @typedef {Pure<A> | Impure<A>} Free
|
* @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 */
|
/** @template T */
|
||||||
export class Pure {
|
export class Pure extends Free {
|
||||||
#value
|
#value
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {A} value
|
* @param {T} value
|
||||||
*/
|
*/
|
||||||
constructor(value) {
|
constructor(value) {
|
||||||
|
super()
|
||||||
this.#value = value
|
this.#value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {Free<B>} B
|
* @param {InferredMorphism<T>} f
|
||||||
* @param {Morphism<A, B>} f
|
|
||||||
* @returns {B}
|
|
||||||
*/
|
*/
|
||||||
chain(f) {
|
chain(f) {
|
||||||
return f(this.#value)
|
return f(this.#value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template B
|
* @param {InferredMorphism<T>} f
|
||||||
* @param {Morphism<A, B>} f
|
|
||||||
* @returns {Free<B>}
|
|
||||||
*/
|
*/
|
||||||
map(f) {
|
map(f) {
|
||||||
// @ts-ignore
|
|
||||||
return pure(f(this.#value))
|
return pure(f(this.#value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template B, C
|
* @template U
|
||||||
* @param {Free<B>} b
|
* @template {Applicative<U>} M
|
||||||
* @returns {Free<C>}
|
* @template {ApplicativeTypeRef<U, M>} TypeRef
|
||||||
|
* @param {TypeRef} _A
|
||||||
|
* @param {*} f
|
||||||
|
* @returns {M}
|
||||||
*/
|
*/
|
||||||
ap(b) {
|
traverse(_A, f) {
|
||||||
return b.map(this.#value)
|
return f(this.#value).map(pure)
|
||||||
}
|
}
|
||||||
|
|
||||||
traverse(f, of) {
|
run() {
|
||||||
return of(this.map(f))
|
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 T */
|
||||||
* @template A
|
export class Impure extends Free {
|
||||||
* @typedef {<B>(value: A) => Free<B>} Computation
|
#value
|
||||||
*/
|
|
||||||
|
|
||||||
/** @template A */
|
|
||||||
export class Impure {
|
|
||||||
#next
|
#next
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {() => Free<any>} next
|
* @param {T} value
|
||||||
|
* @param {(value: T) => Free<any>} f
|
||||||
*/
|
*/
|
||||||
constructor(next) {
|
constructor(value, f) {
|
||||||
this.#next = next
|
super()
|
||||||
|
this.#value = value
|
||||||
|
this.#next = f
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {Free<B>} B
|
* @param {<U>(value: T) => Free<U>} f
|
||||||
* @param {Morphism<A, B>} f
|
|
||||||
* @returns {B}
|
|
||||||
*/
|
*/
|
||||||
chain(f) {
|
chain(f) {
|
||||||
// @ts-ignore
|
return impure(this.#value, kleisli(this.#next, f))
|
||||||
return new Impure(
|
|
||||||
// @ts-ignore
|
|
||||||
() => this.#next().chain(f)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @template B
|
|
||||||
* @param {Morphism<A, B>} f
|
|
||||||
* @returns {Free<B>}
|
|
||||||
*/
|
|
||||||
map(f) {
|
map(f) {
|
||||||
return new Impure(
|
return impure(
|
||||||
// @ts-ignore
|
this.#value,
|
||||||
() => this.#next().map(f)
|
y => f(y).chain(f)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template B
|
* @template U
|
||||||
* @param {Free<any>} b
|
* @template {Applicative<U>} M
|
||||||
* @returns {Free<B>}
|
* @template {ApplicativeTypeRef<U, M>} TypeRef
|
||||||
|
* @param {TypeRef} A
|
||||||
|
* @param {(value: T) => M} f
|
||||||
|
* @returns {M}
|
||||||
*/
|
*/
|
||||||
ap(b) {
|
traverse(A, f) {
|
||||||
return new Impure(
|
const fb = f(this.#value)
|
||||||
// @ts-ignore
|
const rest = this.#next(this.#value).traverse(A, f)
|
||||||
() => b.map(this.#next())
|
|
||||||
|
return rest.ap(
|
||||||
|
fb.map(b => next => impure(b, () => next))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
traverse(f, of) {
|
run() {
|
||||||
return of(liftF(
|
return this.#next(this.#value).run()
|
||||||
this.#next().traverse(f, of)
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @template B
|
|
||||||
* @param {(acc: B, value: A) => B} f
|
|
||||||
* @param {B} acc
|
|
||||||
* @returns {B}
|
|
||||||
*/
|
|
||||||
reduce(f, acc) {
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const sequence = (of, iter) => {
|
/**
|
||||||
return reduce((acc, x) => {
|
* @template T
|
||||||
ap(acc, map(prepend, x))
|
* @param {T} x
|
||||||
}, of([]), iter)
|
* @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 { MixinFunction } from '../mixin.js' */
|
||||||
/** @import { Fn } from './types.js' */
|
/** @import { Fn } from './types.js' */
|
||||||
|
@ -130,7 +130,7 @@ class Method {
|
||||||
*/
|
*/
|
||||||
_getInstallationPoint(target) {
|
_getInstallationPoint(target) {
|
||||||
switch (this.#type) {
|
switch (this.#type) {
|
||||||
case 0: return target.prototype
|
case 0: return Object.getPrototypeOf(target)
|
||||||
case 1: return target
|
case 1: return target
|
||||||
default: return target
|
default: return target
|
||||||
}
|
}
|
||||||
|
@ -152,6 +152,9 @@ const Implementations = Symbol()
|
||||||
class Interface {
|
class Interface {
|
||||||
#name
|
#name
|
||||||
|
|
||||||
|
/** @type {MixinFunction} */
|
||||||
|
#mixin
|
||||||
|
|
||||||
/** @type {Set<Method>} */
|
/** @type {Set<Method>} */
|
||||||
#methods = new Set()
|
#methods = new Set()
|
||||||
|
|
||||||
|
@ -161,20 +164,20 @@ class Interface {
|
||||||
/** @param {string} name */
|
/** @param {string} name */
|
||||||
constructor(name) {
|
constructor(name) {
|
||||||
this.#name = name
|
this.#name = name
|
||||||
|
this.#mixin = Mixin(this.build.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
get name() {
|
get name() {
|
||||||
return this.#name
|
return this.#name
|
||||||
}
|
}
|
||||||
|
|
||||||
findInterfaces() {
|
findInterfaces(type) {
|
||||||
let interfaces = new Set()
|
let interfaces = new Set()
|
||||||
let current = Object.getPrototypeOf(this)
|
let current = type
|
||||||
console.log(current)
|
|
||||||
|
|
||||||
while (current != null) {
|
while (current != null) {
|
||||||
interfaces = new Set(current[Implementations])
|
interfaces = new Set(current[Implementations]).union(interfaces)
|
||||||
current = Object.getPrototypeOf(this)
|
current = Object.getPrototypeOf(current)
|
||||||
}
|
}
|
||||||
|
|
||||||
return interfaces
|
return interfaces
|
||||||
|
@ -185,7 +188,7 @@ class Interface {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
implementedBy(type) {
|
implementedBy(type) {
|
||||||
return this.findInterfaces().has(type)
|
return this.findInterfaces(type).has(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -212,10 +215,10 @@ class Interface {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns {MixinWrapper}
|
* @returns {MixinFunction}
|
||||||
*/
|
*/
|
||||||
intoWrapper() {
|
asMixin() {
|
||||||
return this.build.bind(this)
|
return this.#mixin
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -223,19 +226,20 @@ class Interface {
|
||||||
*/
|
*/
|
||||||
build(base) {
|
build(base) {
|
||||||
const interfaces = [...this.#interfaces.values()]
|
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) {
|
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}
|
* @returns {(...algebras: Interface[]) => FunctionConstructor}
|
||||||
*/
|
*/
|
||||||
export const AlgebraWithBase = base => (...algebras) => {
|
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' */
|
/** @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 */
|
/** @template T */
|
||||||
export class Some extends Algebra(Setoid, Monoid, Monad, Foldable) {
|
export class Some extends Option {
|
||||||
/** @type {T} */
|
/** @type {T} */
|
||||||
#value
|
#value
|
||||||
|
|
||||||
|
@ -23,11 +37,18 @@ export class Some extends Algebra(Setoid, Monoid, Monad, Foldable) {
|
||||||
return new Some(value)
|
return new Some(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Option<T>} other
|
||||||
|
*/
|
||||||
equals(other) {
|
equals(other) {
|
||||||
return other instanceof Some &&
|
return other instanceof Some &&
|
||||||
other.chain(v => v === this.#value)
|
other.chain(v => v === this.#value)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ap(b) {
|
||||||
|
return b.chain(f => this.map(f))
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template R
|
* @template R
|
||||||
* @param {Morphism<T, R>} f
|
* @param {Morphism<T, R>} f
|
||||||
|
@ -64,18 +85,21 @@ export class Some extends Algebra(Setoid, Monoid, Monad, Foldable) {
|
||||||
reduce(f, init) {
|
reduce(f, init) {
|
||||||
return f(init, this.#value)
|
return f(init, this.#value)
|
||||||
}
|
}
|
||||||
|
|
||||||
empty() {
|
|
||||||
return Option.empty()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @template T */
|
/** @template T */
|
||||||
export class None extends Algebra(Setoid, Monoid, Monad, Foldable) {
|
export class None extends Option {
|
||||||
|
/**
|
||||||
|
* @param {Option<T>} other
|
||||||
|
*/
|
||||||
equals(other) {
|
equals(other) {
|
||||||
return other === none
|
return other === none
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ap(_b) {
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template R
|
* @template R
|
||||||
* @param {Morphism<T, R>} _f
|
* @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
|
* @template T
|
||||||
* @param {T} value
|
* @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' */
|
/** @import { Morphism } from './types.js' */
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T, E
|
* @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} */
|
/** @type {T} */
|
||||||
#value
|
#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) {
|
equals(other) {
|
||||||
return other instanceof Ok &&
|
return other instanceof Ok &&
|
||||||
|
@ -86,7 +101,7 @@ export class Ok extends Algebra(Setoid, Monad, Foldable) {
|
||||||
/**
|
/**
|
||||||
* @template T, E
|
* @template T, E
|
||||||
*/
|
*/
|
||||||
export class Err extends Algebra(Setoid, Functor, Monad) {
|
export class Err extends Result {
|
||||||
/** @type {E} */
|
/** @type {E} */
|
||||||
#value
|
#value
|
||||||
|
|
||||||
|
@ -100,6 +115,9 @@ export class Err extends Algebra(Setoid, Functor, Monad) {
|
||||||
this.#value = value
|
this.#value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Result<T, E>} other
|
||||||
|
*/
|
||||||
equals(other) {
|
equals(other) {
|
||||||
return other instanceof Err &&
|
return other instanceof Err &&
|
||||||
other.chain(v => v === this.#value)
|
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
|
* @template T, E
|
||||||
* @param {T} v
|
* @param {T} v
|
||||||
|
|
|
@ -17,17 +17,197 @@ export default {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @typedef {(value: T) => Chain<T>} ChainConstructor
|
* @typedef {{
|
||||||
|
equals: (b: Setoid<T>) => boolean
|
||||||
|
* }} Setoid
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @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
|
* @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 */
|
/** @typedef {(...args: any[]) => any} Fn */
|
||||||
|
|
||||||
|
|
|
@ -246,7 +246,7 @@ class MixinBuilder {
|
||||||
/**
|
/**
|
||||||
* Applies `mixins` in order to the superclass given to `mix()`.
|
* 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
|
* @return {FunctionConstructor} a subclass of `superclass` with `mixins` applied
|
||||||
*/
|
*/
|
||||||
with(...mixins) {
|
with(...mixins) {
|
||||||
|
|
|
@ -1,28 +1,29 @@
|
||||||
// @ts-nocheck
|
// @ts-nocheck
|
||||||
import { it, assert } from 'folktest'
|
import { it, assert } from 'folktest'
|
||||||
import { Option, Result } from '../../src/index.js'
|
import { IO, List, Option, Reader, Result, Some } from '../../src/index.js'
|
||||||
import { Chain, Ord, Setoid } from '../../src/algebra/interfaces.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 inc = F => x => F.of(x + 1)
|
||||||
const dbl = x => F.of(x * 2)
|
const dbl = F => x => F.of(x * 2)
|
||||||
|
|
||||||
const impl = i => m => i.implementedBy(m)
|
const impl = i => m => i.implementedBy(m)
|
||||||
|
|
||||||
export const Tests = [
|
export const Tests = [
|
||||||
it('unit is a left identity', () => {
|
it('unit is a left identity', () => {
|
||||||
console.log(Chain.implementedBy(Result))
|
Algebra.filter(impl(Setoid, Applicative, Chain)).forEach(algebra => {
|
||||||
Algebra.filter(impl(Chain)).forEach(algebra => {
|
const f = inc(algebra)
|
||||||
|
|
||||||
assert(
|
assert(
|
||||||
algebra.of(1).chain(inc) == inc(1),
|
algebra.of(1).chain(f).equals(f(1)),
|
||||||
`${algebra.name} is not a left identity`
|
`${algebra.name} is not a left identity`
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it('unit is a right 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)
|
const m = algebra.of(1)
|
||||||
|
|
||||||
assert(
|
assert(
|
||||||
|
@ -33,36 +34,15 @@ export const Tests = [
|
||||||
}),
|
}),
|
||||||
|
|
||||||
it('unit is associative', () => {
|
it('unit is associative', () => {
|
||||||
Algebra.filter(impl(Chain)).forEach(algebra => {
|
Algebra.filter(impl(Setoid, Applicative, Chain)).forEach(algebra => {
|
||||||
const a = algebra.of(1)
|
const a = algebra.of(1)
|
||||||
|
const f = inc(algebra)
|
||||||
|
const g = dbl(algebra)
|
||||||
|
|
||||||
const x = a.chain(inc).chain(dbl)
|
const x = a.chain(f).chain(g)
|
||||||
const y = a.chain(x => inc(x).chain(dbl))
|
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