update free/list
This commit is contained in:
parent
cafa3a8fa7
commit
5d7d810d8d
4 changed files with 245 additions and 220 deletions
|
@ -1,22 +1,23 @@
|
|||
import { Algebra, Foldable, Monad, Traversable } from './interfaces.js'
|
||||
import { curry } from '../..//vendor/izuna/src/index.js'
|
||||
import { Reducer } from './utilities.js'
|
||||
import { Algebra, Monad } from './interfaces.js'
|
||||
|
||||
/** @import { Applicative, ApplicativeTypeRef, Apply, Chain, Foldable as FoldableT, Functor, Morphism, Traversable as TraversableT } from './types.js' */
|
||||
/** @import { Applicative, ApplicativeTypeRef, Apply, Chain, Functor, Morphism } from './types.js' */
|
||||
|
||||
const kleisli = curry((f, g, x) => f(x).chain(g))
|
||||
const Interfaces = Algebra(Monad)
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Pure<T> | Impure<T>} Free
|
||||
* @typedef {Applicative<T> & (Pure<T> | Impure<T>)} Free
|
||||
*/
|
||||
|
||||
/** @template T */
|
||||
export class Pure extends Algebra(Monad, Traversable) {
|
||||
/**
|
||||
* @template T
|
||||
* @implements {Applicative<T>}
|
||||
*/
|
||||
class Pure extends Interfaces {
|
||||
#value
|
||||
|
||||
/**
|
||||
* @param {T} value
|
||||
* @param {T} value
|
||||
*/
|
||||
constructor(value) {
|
||||
super()
|
||||
|
@ -24,7 +25,18 @@ export class Pure extends Algebra(Monad, Traversable) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value
|
||||
*/
|
||||
static of(value) {
|
||||
return liftF(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @type {Chain<T>['chain']}
|
||||
* @param {Morphism<T, Pure<U>>} f
|
||||
* @returns {Pure<U>}
|
||||
*/
|
||||
chain(f) {
|
||||
return f(this.#value)
|
||||
|
@ -34,165 +46,130 @@ export class Pure extends Algebra(Monad, Traversable) {
|
|||
* @type {Functor<T>['map']}
|
||||
*/
|
||||
map(f) {
|
||||
return pure(f(this.#value))
|
||||
return this.chain(x => pure(f(x)))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @type {Apply<T>['ap']}
|
||||
* @this {Pure<Morphism<U, T>>}
|
||||
* @param {Pure<U>} b
|
||||
* @returns {Free<T>}
|
||||
* @param {Free<Morphism<T, U>>} b
|
||||
* @returns {Free<U>}
|
||||
*/
|
||||
ap(b) {
|
||||
return /** @type {Free<T>} */ (b.map(this.#value))
|
||||
return /** @type {Free<U>} */ (b.chain(f =>
|
||||
/** @type {Chain<U>} */(this.map(f))
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {FoldableT<T>['reduce']}
|
||||
*/
|
||||
reduce(f, acc) {
|
||||
return f(acc, this.#value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @template {Applicative<U>} M
|
||||
* @template {ApplicativeTypeRef<U, M>} TypeRef
|
||||
* @param {TypeRef} _A
|
||||
* @param {(value: T) => M} f
|
||||
* @returns {Applicative<Free<U>>}
|
||||
*/
|
||||
traverse(_A, f) {
|
||||
return /** @type {Applicative<Free<U>>} */ (f(this.#value).map(pure))
|
||||
}
|
||||
|
||||
run() {
|
||||
interpret() {
|
||||
return this.#value
|
||||
}
|
||||
|
||||
toString() {
|
||||
return `Pure(${this.#value})`
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T, [N=any]
|
||||
* @implements Functor<T>
|
||||
* @implements Chain<T>
|
||||
* @implements TraversableT<T>
|
||||
* @template T
|
||||
* @implements {Applicative<T>}
|
||||
*/
|
||||
export class Impure extends Algebra(Monad, Foldable, Traversable) {
|
||||
class Impure extends Interfaces {
|
||||
#value
|
||||
#next
|
||||
|
||||
/**
|
||||
* @param {T} value
|
||||
* @param {(value: T) => Free<N>} f
|
||||
* @param {T} value
|
||||
* @param {(value: T) => Free<any>} next
|
||||
*/
|
||||
constructor(value, f) {
|
||||
constructor(value, next) {
|
||||
super()
|
||||
this.#value = value
|
||||
this.#next = f
|
||||
this.#next = next
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value
|
||||
*/
|
||||
static of(value) {
|
||||
return liftF(value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {Chain<T>['chain']}
|
||||
* @template U
|
||||
* @param {(value: T) => Free<U>} f
|
||||
* @type {Chain<T>['chain']}
|
||||
* @param {Morphism<T, Free<U>>} f
|
||||
* @returns {Free<T>}
|
||||
*/
|
||||
chain(f) {
|
||||
return impure(this.#value, kleisli(this.#next, f))
|
||||
return /** @type {Free<T>} */ (impure(
|
||||
this.#value,
|
||||
x => /** @type {Free<U>} */(this.#next(x).chain(f))
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
* @template {Functor<T>} U
|
||||
* @template U
|
||||
* @type {Functor<T>['map']}
|
||||
* @param {Morphism<T, U>} f
|
||||
* @param {Morphism<T, U>} f
|
||||
* @returns {Free<T>}
|
||||
*/
|
||||
map(f) {
|
||||
return impure(
|
||||
return /** @type {Free<T>} */ (impure(
|
||||
this.#value,
|
||||
y => /** @type {Free<U>} */(f(y).map(f))
|
||||
)
|
||||
x => /** @type Free<U>} */(this.#next(x).map(f))
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @type {Apply<T>['ap']}
|
||||
* @this {Free<Morphism<U, T>>}
|
||||
* @param {Free<U>} b
|
||||
* @param {Free<Morphism<T, U>>} b
|
||||
* @returns {Free<T>}
|
||||
*/
|
||||
ap(b) {
|
||||
return /** @type {Free<T>} */ (this.chain(f =>
|
||||
/** @type {Free<T>} */(b.map(f))
|
||||
return /** @type {Free<T>} */ (impure(
|
||||
this.#value,
|
||||
x => /** @type {Free<U>} */(b.chain(f =>
|
||||
/** @type {Free<U>} */(this.#next(x).map(f)))
|
||||
)
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {FoldableT<T>['reduce']}
|
||||
*/
|
||||
reduce(f, acc) {
|
||||
const Const = Reducer(f, acc)
|
||||
// @ts-ignore
|
||||
return this.traverse(Const, x => new Const(x)).value
|
||||
interpret() {
|
||||
return this.#next(this.#value).interpret()
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {TraversableT<T>['traverse']}
|
||||
* @template U
|
||||
* @template {Applicative<U>} M
|
||||
* @template {ApplicativeTypeRef<U, M>} TypeRef
|
||||
* @this {Impure<T, N>}
|
||||
* @param {TypeRef} A
|
||||
* @param {(value: T | N) => M} f
|
||||
* @returns {Applicative<Free<U>>}
|
||||
*/
|
||||
traverse(A, f) {
|
||||
const next = this.#next(this.#value)
|
||||
|
||||
if (next instanceof Pure) {
|
||||
return next.traverse(A, f)
|
||||
} else {
|
||||
const fb = f(this.#value)
|
||||
const rest = next.traverse(A, f)
|
||||
|
||||
return /** @type {Applicative<Free<U>>} */ (rest.ap(
|
||||
/** @type {Apply<Morphism<TraversableT<U>, U>>} */(fb.map(b => next => impure(b, () =>
|
||||
/** @type {Free<U>} */(next)
|
||||
)))
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
run() {
|
||||
return this.#next(this.#value).run()
|
||||
toString() {
|
||||
return `Impure(${this.#value}, ${this.#next})`
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} x
|
||||
* @returns {Pure<T>}
|
||||
* @param {T} value
|
||||
*/
|
||||
const pure = x => new Pure(x)
|
||||
export const pure = value => new Pure(value)
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} x
|
||||
* @param {(value: T) => Free<any>} f
|
||||
* @returns {Impure<T>}
|
||||
*/
|
||||
const impure = (x, f) => new Impure(x, f)
|
||||
export const impure = (x, f) => new Impure(x, f)
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} x
|
||||
* @returns Impure<T>
|
||||
* @param {T} value
|
||||
*/
|
||||
export const liftF = x => impure(x, pure)
|
||||
export const liftF = value => impure(value, pure)
|
||||
|
||||
const TypeRef = () => { }
|
||||
TypeRef.constructor.of = pure
|
||||
|
||||
export const Free = TypeRef
|
||||
/**
|
||||
* @template T
|
||||
* @type {ApplicativeTypeRef<T, Free<T>>}
|
||||
*/
|
||||
export const Free = Pure
|
||||
|
||||
|
|
|
@ -1,74 +1,152 @@
|
|||
import { Algebra, Comonad, Foldable, Monoid, Semigroup } from './interfaces.js'
|
||||
|
||||
/** @import { InferredMorphism, Morphism } from './types.js' */
|
||||
/** @import { Apply, Chain, Foldable as FoldableT, Functor, Morphism, Semigroup as SemigroupT } from './types.js' */
|
||||
|
||||
class Empty extends Algebra(Semigroup, Foldable, Monoid, Comonad) {
|
||||
const Interfaces = Algebra(Semigroup, Foldable, Monoid, Comonad)
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @typedef {Element<T> | Empty<T>} List
|
||||
*/
|
||||
|
||||
/** @template T */
|
||||
class Empty extends Interfaces {
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @this {Empty}
|
||||
* @param {T} value
|
||||
* @returns {List<T>}
|
||||
* @template U
|
||||
* @type {Chain<T>['chain']}
|
||||
* @this {Empty<U>}
|
||||
* @param {Morphism<T, List<U>>} _f
|
||||
* @returns {List<U>}
|
||||
*/
|
||||
prepend(value) {
|
||||
return new Element(value, () => this)
|
||||
}
|
||||
|
||||
head() {
|
||||
return empty
|
||||
}
|
||||
|
||||
tail() {
|
||||
return empty
|
||||
chain(_f) {
|
||||
return /** @type {List<U>} */ (this)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {List<Element<T>>} other
|
||||
* @returns {List<Element<T>>}
|
||||
* @template U
|
||||
* @type {Functor<T>['map']}
|
||||
* @this {Empty<U>}
|
||||
* @param {Morphism<T, U>} _f
|
||||
* @returns {List<U>}
|
||||
*/
|
||||
concat(other) {
|
||||
return other
|
||||
}
|
||||
|
||||
chain(f) {
|
||||
map(_f) {
|
||||
return this
|
||||
}
|
||||
|
||||
map(f) {
|
||||
return this
|
||||
/**
|
||||
* @template U
|
||||
* @type {Apply<T>['ap']}
|
||||
* @this {Empty<U>}
|
||||
* @param {List<Morphism<T, U>>} _b
|
||||
* @returns {List<U>}
|
||||
*/
|
||||
ap(_b) {
|
||||
return /** @type {List<U>} */ (this)
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {SemigroupT<T>['concat']}
|
||||
*/
|
||||
concat(b) {
|
||||
return b
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {FoldableT<T>['reduce']}
|
||||
*/
|
||||
reduce(_f, acc) {
|
||||
return acc
|
||||
}
|
||||
|
||||
count() {
|
||||
return 0
|
||||
}
|
||||
|
||||
/** @returns {this is Empty<T>} */
|
||||
isEmpty() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
class Element extends Algebra(Semigroup, Foldable, Monoid, Comonad) {
|
||||
/** @template T */
|
||||
class Element extends Interfaces {
|
||||
#head
|
||||
#tail
|
||||
/** @type {List<T>} */
|
||||
#cache
|
||||
|
||||
/**
|
||||
* @param {T} head
|
||||
* @param {() => List<T>} tail
|
||||
* @param {() => List<T>} [tail]
|
||||
*/
|
||||
constructor(head, tail = List.empty) {
|
||||
super()
|
||||
this.#head = head
|
||||
this.#tail = tail
|
||||
this.#cache = null
|
||||
}
|
||||
/**
|
||||
* @template U
|
||||
* @type {Chain<T>['chain']}
|
||||
* @this {Element<T>}
|
||||
* @param {Morphism<T, List<U>>} f
|
||||
* @returns {List<U>}
|
||||
*/
|
||||
chain(f) {
|
||||
return /** @type {List<U>} */ (f(this.#head).concat(
|
||||
/** @type {never} */(this.tail().chain(f))
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @this {Empty}
|
||||
* @param {T} value
|
||||
* @returns {List<T>}
|
||||
* @template U
|
||||
* @type {Functor<T>['map']}
|
||||
* @param {Morphism<T, U>} f
|
||||
* @returns {List<U>}
|
||||
*/
|
||||
prepend(value) {
|
||||
return new Element(value, () => this)
|
||||
map(f) {
|
||||
return new Element(
|
||||
f(this.#head),
|
||||
() => /** @type {List<U>} */(this.tail().map(f))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @type {Apply<T>['ap']}
|
||||
* @this {Element<T>}
|
||||
* @param {List<Morphism<T, U>>} b
|
||||
* @returns {List<U>}
|
||||
*/
|
||||
ap(b) {
|
||||
if (b.isEmpty()) {
|
||||
return List.empty()
|
||||
}
|
||||
|
||||
const head = /** @type {List<U>} */ (this.map(b.head()))
|
||||
const rest = /** @type {List<U>} */ (this.ap(b.tail()))
|
||||
return /** @type {List<U>} */ (head.concat(rest))
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {SemigroupT<T>['concat']}
|
||||
*/
|
||||
concat(b) {
|
||||
return new Element(
|
||||
this.#head,
|
||||
() => /** @type {List<T>} */(this.tail().concat(b))
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @type {FoldableT<T>['reduce']}
|
||||
*/
|
||||
reduce(f, acc) {
|
||||
return this.tail().reduce(f, f(acc, this.#head))
|
||||
}
|
||||
|
||||
head() {
|
||||
|
@ -76,73 +154,34 @@ class Element extends Algebra(Semigroup, Foldable, Monoid, Comonad) {
|
|||
}
|
||||
|
||||
tail() {
|
||||
return this.#tail()
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {List<T>} other
|
||||
* @returns {List<T>}
|
||||
*/
|
||||
concat(other) {
|
||||
if (this.isEmpty()) {
|
||||
return other
|
||||
} else {
|
||||
return new Element(
|
||||
this.head(),
|
||||
() => this.tail().concat(other)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @param {Morphism<T, List<U>>} f
|
||||
* @returns {List<U>}
|
||||
*/
|
||||
chain(f) {
|
||||
return new Element(
|
||||
f(this.head()),
|
||||
this.tail().chain(f)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @param {(acc: U, value: T) => U} f
|
||||
* @param {U} init
|
||||
* @returns {U}
|
||||
*/
|
||||
reduce(f, init) {
|
||||
const acc = f(init, this._value)
|
||||
return this.tail().reduce(f, acc)
|
||||
}
|
||||
|
||||
extract() {
|
||||
return this.reduce(reduceArray, [])
|
||||
return this.#cache || (this.#cache = this.#tail())
|
||||
}
|
||||
|
||||
count() {
|
||||
return this.tail().count() + 1
|
||||
}
|
||||
|
||||
/** @returns {this is Empty} */
|
||||
/** @returns {this is Empty<T>} */
|
||||
isEmpty() {
|
||||
return false
|
||||
}
|
||||
|
||||
toArray() {
|
||||
return this.reduce(
|
||||
reduceArray,
|
||||
[]
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @type {Element<T> | Empty}
|
||||
*/
|
||||
export class List {
|
||||
class TypeRef {
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value
|
||||
* @returns {List<T>}
|
||||
*/
|
||||
static of(value) {
|
||||
return empty.prepend(value)
|
||||
return new Element(value)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -181,11 +220,25 @@ export class List {
|
|||
}
|
||||
|
||||
static empty() {
|
||||
return Empty
|
||||
return empty
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T[]} acc
|
||||
* @param {T} x
|
||||
* @returns {T[]}
|
||||
*/
|
||||
const reduceArray = (acc, x) => acc.concat(x)
|
||||
|
||||
export const List = TypeRef
|
||||
|
||||
const empty = new Empty()
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value
|
||||
*/
|
||||
export const list = value => List.of(value)
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { id } from '../../vendor/izuna/src/index.js'
|
||||
import { Algebra, Monad } from './interfaces.js'
|
||||
|
||||
/** @import { Apply, Chain, Functor, InferredMorphism, Morphism } from './types.js' */
|
||||
/** @import { ApplicativeTypeRef, Apply, Chain, Functor, InferredMorphism, Morphism } from './types.js' */
|
||||
|
||||
/**
|
||||
* @template T
|
||||
|
@ -21,6 +21,7 @@ export class Reader extends Algebra(Monad) {
|
|||
|
||||
/**
|
||||
* @template T
|
||||
* @type {ApplicativeTypeRef<T, Reader<T>>['of']}
|
||||
* @param {T} a
|
||||
* @returns {Reader<T>}
|
||||
*/
|
||||
|
@ -34,13 +35,12 @@ export class Reader extends Algebra(Monad) {
|
|||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @type {Functor<T>['map']}
|
||||
* @param {Morphism<T, U>} f
|
||||
* @returns {Reader<U>}
|
||||
* @param {InferredMorphism<T>} f
|
||||
* @returns {Reader<T>}
|
||||
*/
|
||||
map(f) {
|
||||
return new Reader(env => f(this.#run(env)))
|
||||
return this.chain(value => Reader.of(f(value)))
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -49,19 +49,23 @@ export class Reader extends Algebra(Monad) {
|
|||
* @returns {Reader<T>}
|
||||
*/
|
||||
chain(f) {
|
||||
return new Reader(env => f(this.#run(env)).run(env))
|
||||
return new Reader(env => {
|
||||
const result = this.#run(env)
|
||||
const next = f(result)
|
||||
return next.run(env)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @type {Apply<T>['ap']}
|
||||
* @param {Reader<Morphism<T, U>>} other
|
||||
* @param {Reader<Morphism<T, U>>} b
|
||||
* @returns {Reader<U>}
|
||||
*/
|
||||
ap(other) {
|
||||
return new Reader(
|
||||
env => other.run(env)(this.run(env))
|
||||
)
|
||||
ap(b) {
|
||||
return /** @type {Reader<U>} */ (b.chain(f =>
|
||||
/** @type {Reader<U>} */(this.map(f))
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -4,23 +4,14 @@
|
|||
* @param {U} acc
|
||||
*/
|
||||
export const Reducer = (f, acc) => {
|
||||
/** @template A */
|
||||
return class Const {
|
||||
/** @param {A} value */
|
||||
constructor(value) {
|
||||
this.value = value
|
||||
}
|
||||
|
||||
static of() { return new Const(acc) }
|
||||
|
||||
map() { return this }
|
||||
|
||||
/**
|
||||
* @this {Const<T>}
|
||||
* @param {Const<U>} b
|
||||
*/
|
||||
ap(b) {
|
||||
return new Const(f(b.value, this.value))
|
||||
}
|
||||
function Const(value) {
|
||||
this.value = value
|
||||
}
|
||||
|
||||
Const.of = function() { return new Const(acc) }
|
||||
Const.prototype.map = function() { return this }
|
||||
Const.prototype.ap = function(b) {
|
||||
return new Const(f(b.value, this.value))
|
||||
}
|
||||
return Const
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue