add reduce derivation; add reduce to Free; fix traverse
This commit is contained in:
parent
cf3fe66f73
commit
619f24c7dc
3 changed files with 75 additions and 38 deletions
|
@ -1,7 +1,8 @@
|
||||||
import { Algebra, Foldable, Monad, Traversable } 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 { Reducer } from './utilities.js'
|
||||||
|
|
||||||
/** @import { Applicative, ApplicativeTypeRef, Apply, Chain, Foldable as FoldableT, Functor, Monad as MonadT, Morphism, Traversable as TraversableT } from './types.js' */
|
/** @import { Applicative, ApplicativeTypeRef, Apply, Chain, Comonad, Foldable as FoldableT, Functor, Monad as MonadT, Morphism, Traversable as TraversableT } from './types.js' */
|
||||||
|
|
||||||
|
|
||||||
/** @import { InferredMorphism } from './types.js' */
|
/** @import { InferredMorphism } from './types.js' */
|
||||||
|
@ -10,17 +11,9 @@ const kleisli = curry((f, g, x) => f(x).chain(g))
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template T
|
* @template T
|
||||||
* @type {Pure<T> | Impure<T>} Free
|
* @typedef {Pure<T> | Impure<T>} Free
|
||||||
*/
|
*/
|
||||||
export class Free {
|
const Free = {}
|
||||||
/**
|
|
||||||
* @template T
|
|
||||||
* @param {T} value
|
|
||||||
*/
|
|
||||||
static of(value) {
|
|
||||||
return new Pure(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @template T */
|
/** @template T */
|
||||||
export class Pure extends Algebra(Monad, Traversable) {
|
export class Pure extends Algebra(Monad, Traversable) {
|
||||||
|
@ -35,14 +28,14 @@ export class Pure extends Algebra(Monad, Traversable) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {InferredMorphism<T>} f
|
* @type {Chain<T>['chain']}
|
||||||
*/
|
*/
|
||||||
chain(f) {
|
chain(f) {
|
||||||
return f(this.#value)
|
return f(this.#value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {InferredMorphism<T>} f
|
* @type {Functor<T>['map']}
|
||||||
*/
|
*/
|
||||||
map(f) {
|
map(f) {
|
||||||
return pure(f(this.#value))
|
return pure(f(this.#value))
|
||||||
|
@ -50,11 +43,13 @@ export class Pure extends Algebra(Monad, Traversable) {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template U
|
* @template U
|
||||||
|
* @type {Apply<T>['ap']}
|
||||||
* @this {Pure<Morphism<U, T>>}
|
* @this {Pure<Morphism<U, T>>}
|
||||||
* @param {Free<T>} b
|
* @param {Pure<U>} b
|
||||||
|
* @returns {Free<T>}
|
||||||
*/
|
*/
|
||||||
ap(b) {
|
ap(b) {
|
||||||
return b.map(this.#value)
|
return /** @type {Free<T>} */ (b.map(this.#value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -105,7 +100,7 @@ export class Impure extends Algebra(Monad, Foldable, Traversable) {
|
||||||
* @type {Chain<T>['chain']}
|
* @type {Chain<T>['chain']}
|
||||||
* @template U
|
* @template U
|
||||||
* @param {(value: T) => Free<U>} f
|
* @param {(value: T) => Free<U>} f
|
||||||
* @returns {Chain<T>}
|
* @returns {Free<T>}
|
||||||
*/
|
*/
|
||||||
chain(f) {
|
chain(f) {
|
||||||
return impure(this.#value, kleisli(this.#next, f))
|
return impure(this.#value, kleisli(this.#next, f))
|
||||||
|
@ -114,33 +109,36 @@ export class Impure extends Algebra(Monad, Foldable, Traversable) {
|
||||||
/**
|
/**
|
||||||
* @template {Functor<T>} U
|
* @template {Functor<T>} U
|
||||||
* @type {Functor<T>['map']}
|
* @type {Functor<T>['map']}
|
||||||
* @param {(value: T) => U} f
|
* @param {Morphism<T, U>} f
|
||||||
* @returns {Functor<T>}
|
* @returns {Free<T>}
|
||||||
*/
|
*/
|
||||||
map(f) {
|
map(f) {
|
||||||
return impure(
|
return impure(
|
||||||
this.#value,
|
this.#value,
|
||||||
y => f(y).map(f)
|
y => /** @type {Free<U>} */(f(y).map(f))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @template U
|
||||||
* @type {Apply<T>['ap']}
|
* @type {Apply<T>['ap']}
|
||||||
|
* @this {Free<Morphism<U, T>>}
|
||||||
|
* @param {Free<U>} b
|
||||||
|
* @returns {Free<T>}
|
||||||
*/
|
*/
|
||||||
ap(b) {
|
ap(b) {
|
||||||
return this.chain(f => b.map(f))
|
return /** @type {Free<T>} */ (this.chain(f =>
|
||||||
|
/** @type {Free<T>} */(b.map(f))
|
||||||
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @type {FoldableT<T>['reduce']}
|
* @type {FoldableT<T>['reduce']}
|
||||||
*/
|
*/
|
||||||
reduce(f, acc) {
|
reduce(f, acc) {
|
||||||
const fb = f(acc, this.#value)
|
const Const = Reducer(f, acc)
|
||||||
const rest = this.#next(this.#value).reduce(f, acc)
|
// @ts-ignore
|
||||||
|
return this.traverse(Const, x => new Const(x)).value
|
||||||
return rest.ap(
|
|
||||||
fb.map(b => next => impure(b, () => next))
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -154,12 +152,20 @@ export class Impure extends Algebra(Monad, Foldable, Traversable) {
|
||||||
* @returns {Applicative<Free<U>>}
|
* @returns {Applicative<Free<U>>}
|
||||||
*/
|
*/
|
||||||
traverse(A, f) {
|
traverse(A, f) {
|
||||||
const fb = f(this.#value)
|
const next = this.#next(this.#value)
|
||||||
const rest = this.#next(this.#value).traverse(A, f)
|
|
||||||
|
|
||||||
return rest.ap(
|
if (next instanceof Pure) {
|
||||||
fb.map(b => next => impure(b, () => next))
|
return next.traverse(A, f)
|
||||||
)
|
} else {
|
||||||
|
const fb = f(this.#value)
|
||||||
|
const rest = next.traverse(A, f)
|
||||||
|
|
||||||
|
return /** @type {Applicative<Free<U>>} */ (rest.ap(
|
||||||
|
fb.map(b => next => impure(b, () =>
|
||||||
|
/** @type {Free<U>} */(next)
|
||||||
|
))
|
||||||
|
))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
run() {
|
run() {
|
||||||
|
@ -192,6 +198,6 @@ export const liftF = x => impure(x, pure)
|
||||||
import { id, Identity } from './identity.js'
|
import { id, Identity } from './identity.js'
|
||||||
import { Option, some } from './option.js'
|
import { Option, some } from './option.js'
|
||||||
|
|
||||||
const a = liftF(1).chain(() => liftF(2)).traverse(Option, x => some(1))
|
const a = liftF(1).chain(() => liftF(2)).reduce((acc, x) => acc + x, 0)
|
||||||
console.log(a.chain(x => x).run())
|
console.log(a)
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { Algebra, Comonad, Monad } from './interfaces.js'
|
import { Algebra, Comonad, Monad } from './interfaces.js'
|
||||||
|
|
||||||
/** @import { Morphism } from './types.js' */
|
/** @import { Apply, Morphism } from './types.js' */
|
||||||
|
|
||||||
/** @template T */
|
/** @template T */
|
||||||
export class Identity extends Algebra(Monad, Comonad) {
|
export class Identity extends Algebra(Monad, Comonad) {
|
||||||
|
@ -28,17 +28,18 @@ export class Identity extends Algebra(Monad, Comonad) {
|
||||||
* @param {Morphism<T, U>} f
|
* @param {Morphism<T, U>} f
|
||||||
*/
|
*/
|
||||||
map(f) {
|
map(f) {
|
||||||
|
console.log(this.toString(), f.toString())
|
||||||
return id(f(this.#value))
|
return id(f(this.#value))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @template U
|
* @template U
|
||||||
* @this {Identity<Morphism<U, T>>}
|
* @type {Apply<T>['ap']}
|
||||||
* @param {Identity<U>} b
|
* @param {Apply<Morphism<T, U>>} b
|
||||||
* @returns {Identity<T>}
|
* @returns {Apply<U>}
|
||||||
*/
|
*/
|
||||||
ap(b) {
|
ap(b) {
|
||||||
return b.map(this.#value)
|
return /** @type {Apply<U>} */ (b.map(f => f(this.#value)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -59,6 +60,10 @@ export class Identity extends Algebra(Monad, Comonad) {
|
||||||
extract() {
|
extract() {
|
||||||
return this.#value
|
return this.#value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toString() {
|
||||||
|
return `Identity(${this.#value})`
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const id = value => new Identity(value)
|
export const id = value => new Identity(value)
|
||||||
|
|
26
src/algebra/utilities.js
Normal file
26
src/algebra/utilities.js
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/**
|
||||||
|
* @template T, U
|
||||||
|
* @param {(acc: U, value: T) => U} f
|
||||||
|
* @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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue