types & free reduce

This commit is contained in:
Rowan 2025-04-11 04:03:09 -05:00
parent 853601f2c5
commit cf3fe66f73
3 changed files with 74 additions and 20 deletions

View file

@ -1,7 +1,7 @@
import { Algebra, Monad } from './interfaces.js'
import { Algebra, Foldable, Monad, Traversable } from './interfaces.js'
import { curry } from '../..//vendor/izuna/src/index.js'
/** @import { Applicative, ApplicativeTypeRef, Morphism } from './types.js' */
/** @import { Applicative, ApplicativeTypeRef, Apply, Chain, Foldable as FoldableT, Functor, Monad as MonadT, Morphism, Traversable as TraversableT } from './types.js' */
/** @import { InferredMorphism } from './types.js' */
@ -12,7 +12,7 @@ const kleisli = curry((f, g, x) => f(x).chain(g))
* @template T
* @type {Pure<T> | Impure<T>} Free
*/
export class Free extends Algebra(Monad) {
export class Free {
/**
* @template T
* @param {T} value
@ -23,7 +23,7 @@ export class Free extends Algebra(Monad) {
}
/** @template T */
export class Pure extends Free {
export class Pure extends Algebra(Monad, Traversable) {
#value
/**
@ -48,13 +48,29 @@ export class Pure extends Free {
return pure(f(this.#value))
}
/**
* @template U
* @this {Pure<Morphism<U, T>>}
* @param {Free<T>} b
*/
ap(b) {
return b.map(this.#value)
}
/**
* @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 {*} f
* @returns {M}
* @param {(value: T) => M} f
* @returns {Applicative<Free<U>>}
*/
traverse(_A, f) {
return f(this.#value).map(pure)
@ -65,14 +81,19 @@ export class Pure extends Free {
}
}
/** @template T */
export class Impure extends Free {
/**
* @template T, [N=any]
* @implements Functor<T>
* @implements Chain<T>
* @implements TraversableT<T>
*/
export class Impure extends Algebra(Monad, Foldable, Traversable) {
#value
#next
/**
* @param {T} value
* @param {(value: T) => Free<any>} f
* @param {(value: T) => Free<N>} f
*/
constructor(value, f) {
super()
@ -81,26 +102,56 @@ export class Impure extends Free {
}
/**
* @param {<U>(value: T) => Free<U>} f
* @type {Chain<T>['chain']}
* @template U
* @param {(value: T) => Free<U>} f
* @returns {Chain<T>}
*/
chain(f) {
return impure(this.#value, kleisli(this.#next, f))
}
/**
* @template {Functor<T>} U
* @type {Functor<T>['map']}
* @param {(value: T) => U} f
* @returns {Functor<T>}
*/
map(f) {
return impure(
this.#value,
y => f(y).chain(f)
y => f(y).map(f)
)
}
/**
* @type {Apply<T>['ap']}
*/
ap(b) {
return this.chain(f => b.map(f))
}
/**
* @type {FoldableT<T>['reduce']}
*/
reduce(f, acc) {
const fb = f(acc, this.#value)
const rest = this.#next(this.#value).reduce(f, acc)
return rest.ap(
fb.map(b => next => impure(b, () => next))
)
}
/**
* @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) => M} f
* @returns {M}
* @param {(value: T | N) => M} f
* @returns {Applicative<Free<U>>}
*/
traverse(A, f) {
const fb = f(this.#value)
@ -131,6 +182,7 @@ const pure = x => new Pure(x)
*/
const impure = (x, f) => new Impure(x, f)
/**
* @template T
* @param {T} x

View file

@ -100,9 +100,10 @@ export default {}
/**
* @template T
* @typedef {{
ap: <U>(f: Apply<Morphism<T, U>>) => Apply<U>
* }} Apply
* @typedef {
Functor<T> &
{ ap: <U>(f: Apply<Morphism<T, U>>) => Apply<U> }
* } Apply
*/
/**
@ -157,7 +158,7 @@ export default {}
/**
* @template T
* @typedef {{
filter: <U>(f: (acc: U, val: T) => U, init: U) => U
reduce: <U>(f: (acc: U, val: T) => U, init: U) => U
* }} Foldable
*/

View file

@ -77,15 +77,16 @@ function is(other) {
}
/**
* @template {PropertyKey} const T
* @template {string} const T
* @template {Array<PropertyKey>} const U
* @param {T} typeName
* @param {...U} variantNames
* @returns {Union<T, U>}
*/
export const Union = (typeName, variantNames) => {
const tag = { [Tag]: typeName, is }
const variants = Object.fromEntries(variantNames.map(v => [v, Variant(typeName, v)]))
const typeTag = Symbol(typeName)
const tag = { [Tag]: typeTag, is }
const variants = Object.fromEntries(variantNames.map(v => [v, Variant(typeTag, v)]))
const result = Object.assign(tag, variants)