types & free reduce
This commit is contained in:
parent
853601f2c5
commit
cf3fe66f73
3 changed files with 74 additions and 20 deletions
|
@ -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 { 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' */
|
/** @import { InferredMorphism } from './types.js' */
|
||||||
|
@ -12,7 +12,7 @@ const kleisli = curry((f, g, x) => f(x).chain(g))
|
||||||
* @template T
|
* @template T
|
||||||
* @type {Pure<T> | Impure<T>} Free
|
* @type {Pure<T> | Impure<T>} Free
|
||||||
*/
|
*/
|
||||||
export class Free extends Algebra(Monad) {
|
export class Free {
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @param {T} value
|
* @param {T} value
|
||||||
|
@ -23,7 +23,7 @@ export class Free extends Algebra(Monad) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @template T */
|
/** @template T */
|
||||||
export class Pure extends Free {
|
export class Pure extends Algebra(Monad, Traversable) {
|
||||||
#value
|
#value
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -48,13 +48,29 @@ export class Pure extends Free {
|
||||||
return pure(f(this.#value))
|
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 U
|
||||||
* @template {Applicative<U>} M
|
* @template {Applicative<U>} M
|
||||||
* @template {ApplicativeTypeRef<U, M>} TypeRef
|
* @template {ApplicativeTypeRef<U, M>} TypeRef
|
||||||
* @param {TypeRef} _A
|
* @param {TypeRef} _A
|
||||||
* @param {*} f
|
* @param {(value: T) => M} f
|
||||||
* @returns {M}
|
* @returns {Applicative<Free<U>>}
|
||||||
*/
|
*/
|
||||||
traverse(_A, f) {
|
traverse(_A, f) {
|
||||||
return f(this.#value).map(pure)
|
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
|
#value
|
||||||
#next
|
#next
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {T} value
|
* @param {T} value
|
||||||
* @param {(value: T) => Free<any>} f
|
* @param {(value: T) => Free<N>} f
|
||||||
*/
|
*/
|
||||||
constructor(value, f) {
|
constructor(value, f) {
|
||||||
super()
|
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) {
|
chain(f) {
|
||||||
return impure(this.#value, kleisli(this.#next, 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) {
|
map(f) {
|
||||||
return impure(
|
return impure(
|
||||||
this.#value,
|
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 U
|
||||||
* @template {Applicative<U>} M
|
* @template {Applicative<U>} M
|
||||||
* @template {ApplicativeTypeRef<U, M>} TypeRef
|
* @template {ApplicativeTypeRef<U, M>} TypeRef
|
||||||
|
* @this {Impure<T, N>}
|
||||||
* @param {TypeRef} A
|
* @param {TypeRef} A
|
||||||
* @param {(value: T) => M} f
|
* @param {(value: T | N) => M} f
|
||||||
* @returns {M}
|
* @returns {Applicative<Free<U>>}
|
||||||
*/
|
*/
|
||||||
traverse(A, f) {
|
traverse(A, f) {
|
||||||
const fb = f(this.#value)
|
const fb = f(this.#value)
|
||||||
|
@ -131,6 +182,7 @@ const pure = x => new Pure(x)
|
||||||
*/
|
*/
|
||||||
const impure = (x, f) => new Impure(x, f)
|
const impure = (x, f) => new Impure(x, f)
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @param {T} x
|
* @param {T} x
|
||||||
|
|
|
@ -100,9 +100,10 @@ export default {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @typedef {{
|
* @typedef {
|
||||||
ap: <U>(f: Apply<Morphism<T, U>>) => Apply<U>
|
Functor<T> &
|
||||||
* }} Apply
|
{ ap: <U>(f: Apply<Morphism<T, U>>) => Apply<U> }
|
||||||
|
* } Apply
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,7 +158,7 @@ export default {}
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @typedef {{
|
* @typedef {{
|
||||||
filter: <U>(f: (acc: U, val: T) => U, init: U) => U
|
reduce: <U>(f: (acc: U, val: T) => U, init: U) => U
|
||||||
* }} Foldable
|
* }} Foldable
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
|
@ -77,15 +77,16 @@ function is(other) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template {PropertyKey} const T
|
* @template {string} const T
|
||||||
* @template {Array<PropertyKey>} const U
|
* @template {Array<PropertyKey>} const U
|
||||||
* @param {T} typeName
|
* @param {T} typeName
|
||||||
* @param {...U} variantNames
|
* @param {...U} variantNames
|
||||||
* @returns {Union<T, U>}
|
* @returns {Union<T, U>}
|
||||||
*/
|
*/
|
||||||
export const Union = (typeName, variantNames) => {
|
export const Union = (typeName, variantNames) => {
|
||||||
const tag = { [Tag]: typeName, is }
|
const typeTag = Symbol(typeName)
|
||||||
const variants = Object.fromEntries(variantNames.map(v => [v, Variant(typeName, v)]))
|
const tag = { [Tag]: typeTag, is }
|
||||||
|
const variants = Object.fromEntries(variantNames.map(v => [v, Variant(typeTag, v)]))
|
||||||
const result = Object.assign(tag, variants)
|
const result = Object.assign(tag, variants)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue