update free/list

This commit is contained in:
Rowan 2025-04-13 04:31:29 -05:00
parent cafa3a8fa7
commit 5d7d810d8d
4 changed files with 245 additions and 220 deletions

View file

@ -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

View file

@ -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)

View file

@ -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))
))
}
/**

View file

@ -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
}