free refactor
This commit is contained in:
parent
55be1efee1
commit
f79a2ca462
4 changed files with 256 additions and 230 deletions
|
@ -1,5 +1,6 @@
|
|||
import { curry } from '../curry.js'
|
||||
import { Suspend, suspend, pure } from './free.js'
|
||||
import { concat } from '../iter.js'
|
||||
import { Impure, pure, impure } from './free.js'
|
||||
|
||||
/** @import {InferredMorphism, Morphism, ChainConstructor} from './types.js' */
|
||||
|
||||
|
@ -32,12 +33,14 @@ export const kleisliCompose = curry(
|
|||
(f, g, x) => f(x).chain(g)
|
||||
)
|
||||
|
||||
export const lift = (...args) => args
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} x
|
||||
* @returns {Suspend<T>}
|
||||
* @returns {Impure<T>}
|
||||
*/
|
||||
export const liftF = x => suspend(
|
||||
export const liftF = x => impure(
|
||||
x,
|
||||
/** @type {InferredMorphism<T>} */(pure)
|
||||
)
|
||||
|
@ -47,5 +50,18 @@ export const liftF = x => suspend(
|
|||
* @param {number} b
|
||||
* @returns {number}
|
||||
*/
|
||||
export const sum = (a, b) => a + b
|
||||
export const add = (a, b) => a + b
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} x
|
||||
* @returns {() => T}
|
||||
*/
|
||||
export const thunk = x => () => x
|
||||
|
||||
export const prepend = (x, xs) => concat([x], xs)
|
||||
|
||||
export const type = x => typeof x
|
||||
export const is = (ctr, x) => x.constructor === ctr
|
||||
|
||||
|
||||
|
|
|
@ -1,164 +1,163 @@
|
|||
import { liftF } from './fn.js'
|
||||
import { Algebra, BaseSet, Monad, Semigroupoid } from './index.js'
|
||||
import { Option } from './option.js'
|
||||
import { thunk } from './fn.js'
|
||||
/** @import { Morphism } from './types.js' */
|
||||
|
||||
/** @import { InferredMorphism, Morphism } from './types.js' */
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @extends BaseSet
|
||||
* @mixes Monad
|
||||
* @template A
|
||||
* @typedef {Pure<A> | Impure<A>} Free
|
||||
*/
|
||||
export class Suspend extends Algebra(Semigroupoid, Monad) {
|
||||
_value
|
||||
#fn
|
||||
|
||||
/** @template A */
|
||||
export class Pure {
|
||||
#value
|
||||
|
||||
/**
|
||||
* @param {T} value
|
||||
* @param {InferredMorphism<T>} fn
|
||||
* @param {A} value
|
||||
*/
|
||||
constructor(value, fn) {
|
||||
super()
|
||||
this._value = value
|
||||
this.#fn = fn
|
||||
}
|
||||
|
||||
/** @returns {this is Pure<T>} */
|
||||
isPure() { return false }
|
||||
|
||||
/** @returns {this is Suspend<T>} */
|
||||
isSuspend() { return true }
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @param {Morphism<T, Free<U>>} f
|
||||
* @returns {Suspend<T>}
|
||||
*/
|
||||
chain(f) {
|
||||
return new Suspend(this._value, x => this.#fn(x).chain(f))
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @param {Morphism<T, U>} g
|
||||
* @returns {Suspend<T>}
|
||||
*/
|
||||
map(g) {
|
||||
return new Suspend(this._value, x => this.#fn(x).map(g))
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @param {Morphism<T, U>} g
|
||||
* @returns {Suspend<T>}
|
||||
*/
|
||||
then(g) {
|
||||
return this.map(g)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {InferredMorphism<T>} other
|
||||
*/
|
||||
compose(other) {
|
||||
return this.chain(() => other)
|
||||
}
|
||||
|
||||
step() {
|
||||
return this.#fn(this._value)
|
||||
}
|
||||
|
||||
run() {
|
||||
return this.step().run()
|
||||
}
|
||||
}
|
||||
|
||||
/** @template T */
|
||||
export class Pure extends Algebra(Monad) {
|
||||
_value
|
||||
|
||||
/** @param {T} value */
|
||||
constructor(value) {
|
||||
super()
|
||||
this._value = value
|
||||
this.#value = value
|
||||
}
|
||||
|
||||
/** @returns {this is Pure<T>} */
|
||||
isPure() { return true }
|
||||
|
||||
/** @returns {this is Suspend<T>} */
|
||||
isSuspend() { return false }
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @param {Morphism<T, Pure<U>>} f
|
||||
* @returns {Pure<U>}
|
||||
* @template {Free<B>} B
|
||||
* @param {Morphism<A, B>} f
|
||||
* @returns {B}
|
||||
*/
|
||||
chain(f) {
|
||||
return f(this._value)
|
||||
console.log('Pure.chain', this.#value)
|
||||
return f(this.#value)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @param {Morphism<T, U>} f
|
||||
* @returns {Pure<U>}
|
||||
* @template B
|
||||
* @param {Morphism<A, B>} f
|
||||
* @returns {Free<B>}
|
||||
*/
|
||||
map(f) {
|
||||
return this.chain(x => pure(f(x)))
|
||||
console.log(`Pure.map ${f} ${this.#value}`)
|
||||
// @ts-ignore
|
||||
return pure(f(this.#value))
|
||||
}
|
||||
|
||||
/**
|
||||
* @template R
|
||||
* @param {Morphism<T, R>} f
|
||||
* @returns {Pure<R>}
|
||||
* @template B, C
|
||||
* @param {Free<B>} b
|
||||
* @returns {Free<C>}
|
||||
*/
|
||||
then(f) {
|
||||
return this.map(f)
|
||||
ap(b) {
|
||||
console.log('Pure.ap', b, this.#value)
|
||||
return b.map(this.#value)
|
||||
}
|
||||
|
||||
traverse(f, A) {
|
||||
return A.of(this.map(f))
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {InferredMorphism<T>} other
|
||||
* @template B
|
||||
* @param {(acc: B, value: A) => B} f
|
||||
* @param {B} init
|
||||
* @returns {B}
|
||||
*/
|
||||
compose(other) {
|
||||
return this.chain(() => pure(other))
|
||||
reduce(f, init) {
|
||||
console.log('Pure.reduce', init, this.#value)
|
||||
return f(init, this.#value)
|
||||
}
|
||||
|
||||
step() {
|
||||
return this._value
|
||||
}
|
||||
|
||||
/**
|
||||
* @template A
|
||||
* @typedef {<B>(value: A) => Free<B>} Computation
|
||||
*/
|
||||
|
||||
/** @template A */
|
||||
export class Impure {
|
||||
#next
|
||||
|
||||
/**
|
||||
* @param {() => Free<any>} next
|
||||
*/
|
||||
constructor(next) {
|
||||
this.#next = next
|
||||
}
|
||||
|
||||
run() {
|
||||
return this._value
|
||||
/**
|
||||
* @template {Free<B>} B
|
||||
* @param {Morphism<A, B>} f
|
||||
* @returns {B}
|
||||
*/
|
||||
chain(f) {
|
||||
console.log('Impure.chain')
|
||||
// @ts-ignore
|
||||
return new Impure(
|
||||
// @ts-ignore
|
||||
() => {
|
||||
console.log(`Impure.chain<${f}>`, this.#next())
|
||||
this.#next().chain(f)
|
||||
}
|
||||
//() => this.#next().chain(f)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template B
|
||||
* @param {Morphism<A, B>} f
|
||||
* @returns {Free<B>}
|
||||
*/
|
||||
map(f) {
|
||||
console.log(`Impure.map ${f}`)
|
||||
return new Impure(
|
||||
// @ts-ignore
|
||||
() => {
|
||||
console.log(`Impure.map<${f}>`, this.#next())
|
||||
return this.#next().map(f)
|
||||
}
|
||||
//() => this.#next().map(f)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* @template B
|
||||
* @param {Free<any>} b
|
||||
* @returns {Free<B>}
|
||||
*/
|
||||
ap(b) {
|
||||
console.log('Impure.ap', b)
|
||||
return new Impure(
|
||||
// @ts-ignore
|
||||
() => b.map(this.#next())
|
||||
)
|
||||
}
|
||||
|
||||
traverse(f, A) {
|
||||
return A.of(liftF(
|
||||
this.#next().traverse(f, A)
|
||||
))
|
||||
}
|
||||
|
||||
/**
|
||||
* @template B
|
||||
* @param {(acc: B, value: A) => B} f
|
||||
* @param {B} acc
|
||||
* @returns {B}
|
||||
*/
|
||||
reduce(f, acc) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @type {Pure<T> | Suspend<T>} Free
|
||||
* @template A
|
||||
* @param {A} x
|
||||
* @returns {Free<A, A>}
|
||||
*/
|
||||
export class Free {
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value
|
||||
* @returns {Free<T>}
|
||||
*/
|
||||
static of(value) {
|
||||
return liftF(value)
|
||||
}
|
||||
}
|
||||
export const pure = x => new Pure(x)
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value
|
||||
* @returns {Pure<T>}
|
||||
* @template A, B
|
||||
* @param {Computation<A, B>} f
|
||||
*/
|
||||
export const pure = value => new Pure(value)
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} value
|
||||
* @param {InferredMorphism<T>} f
|
||||
* @returns {Suspend<T>}
|
||||
*/
|
||||
export const suspend = (value, f) => new Suspend(value, f)
|
||||
export const impure = f => new Impure(f)
|
||||
export const liftF = effect => impure(thunk(effect))
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ export class IO extends Algebra(Monad) {
|
|||
_effect
|
||||
|
||||
/**
|
||||
* @type {T} effect
|
||||
* @param {T} effect
|
||||
*/
|
||||
constructor(effect) {
|
||||
super()
|
||||
|
@ -38,12 +38,11 @@ export class IO extends Algebra(Monad) {
|
|||
|
||||
/**
|
||||
* @template {Fn} U
|
||||
* @this {IO<T>}
|
||||
* @param {IO<Morphism<T, U>>} other
|
||||
* @returns {IO<U>}
|
||||
* @returns {IO<T>}
|
||||
*/
|
||||
ap(other) {
|
||||
return IO.of(() => other.run()(this.run()))
|
||||
return /** @type {IO<T>} */ (IO.of((() => other.run()(this.run()))))
|
||||
}
|
||||
|
||||
run() {
|
||||
|
|
|
@ -1,112 +1,110 @@
|
|||
import { Pure, Suspend } from './free.js'
|
||||
import { AlgebraWithBase, Category, Comonad, Foldable, Monoid } from './index.js'
|
||||
import { Algebra, AlgebraWithBase, Comonad, Foldable, Monoid, Semigroup } from './index.js'
|
||||
|
||||
/** @import { InferredMorphism, Morphism } from './types.js' */
|
||||
|
||||
const Nil = Symbol('Nil')
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @extends {Pure<T>}
|
||||
*/
|
||||
class ListPure extends AlgebraWithBase(Pure)(Category, Foldable, Monoid) {
|
||||
static empty() {
|
||||
return List.empty()
|
||||
}
|
||||
|
||||
static id() {
|
||||
return List.empty()
|
||||
}
|
||||
|
||||
head() {
|
||||
return this._value
|
||||
}
|
||||
|
||||
tail() {
|
||||
return Empty
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @this {ListPure<T>}
|
||||
* @param {Morphism<T, ListPure<U>>} f
|
||||
* @returns {ListPure<U>}
|
||||
*/
|
||||
chain(f) {
|
||||
return f(this.head())
|
||||
}
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @this {ListPure<T>}
|
||||
* @param {Morphism<T, U>} f
|
||||
* @returns {ListPure<U>}
|
||||
*/
|
||||
map(f) {
|
||||
return new ListPure(f(this._value))
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @template U
|
||||
* @param {(acc: U, value: T) => U} f
|
||||
* @param {U} init
|
||||
* @returns {U}
|
||||
*/
|
||||
reduce(f, init) {
|
||||
return f(init, this._value)
|
||||
}
|
||||
|
||||
count() {
|
||||
return this.isEmpty() ? 0 : 1
|
||||
}
|
||||
|
||||
/**
|
||||
* @this {List<any>}
|
||||
* @returns {this is Empty}
|
||||
*/
|
||||
isEmpty() {
|
||||
return this === Empty
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @extends {Suspend<T>}
|
||||
*/
|
||||
class ListSuspend extends AlgebraWithBase(Suspend)(Foldable, Monoid, Comonad) {
|
||||
/**
|
||||
* @param {T} head
|
||||
* @param {() => List<T>} tail
|
||||
*/
|
||||
constructor(head, tail) {
|
||||
super(
|
||||
head,
|
||||
/** @type {InferredMorphism<T>} */(tail)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
static empty() {
|
||||
return List.empty()
|
||||
class Empty extends Algebra(Semigroup, Foldable, Monoid, Comonad) {
|
||||
constructor() {
|
||||
super()
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @this {ListSuspend<T>}
|
||||
* @this {Empty}
|
||||
* @param {T} value
|
||||
* @returns {ListSuspend<T>}
|
||||
* @returns {List<T>}
|
||||
*/
|
||||
cons(value) {
|
||||
return new ListSuspend(value, () => this)
|
||||
prepend(value) {
|
||||
return new Element(value, () => this)
|
||||
}
|
||||
|
||||
head() {
|
||||
return this._value
|
||||
return empty
|
||||
}
|
||||
|
||||
tail() {
|
||||
return this.step()
|
||||
return empty
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {List<Element<T>>} other
|
||||
* @returns {List<Element<T>>}
|
||||
*/
|
||||
concat(other) {
|
||||
return other
|
||||
}
|
||||
|
||||
chain(f) {
|
||||
return this
|
||||
}
|
||||
|
||||
map(f) {
|
||||
return this
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
*/
|
||||
class Element extends Algebra(Semigroup, Foldable, Monoid, Comonad) {
|
||||
#head
|
||||
#tail
|
||||
|
||||
/**
|
||||
* @param {T} head
|
||||
* @param {() => List<T>} tail
|
||||
*/
|
||||
constructor(head, tail = List.empty) {
|
||||
super()
|
||||
this.#head = head
|
||||
this.#tail = tail
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @this {Empty}
|
||||
* @param {T} value
|
||||
* @returns {List<T>}
|
||||
*/
|
||||
prepend(value) {
|
||||
return new Element(value, () => this)
|
||||
}
|
||||
|
||||
head() {
|
||||
return this.#head
|
||||
}
|
||||
|
||||
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)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -136,7 +134,7 @@ class ListSuspend extends AlgebraWithBase(Suspend)(Foldable, Monoid, Comonad) {
|
|||
|
||||
/**
|
||||
* @template T
|
||||
* @type {ListPure<T> | ListSuspend<T>} List
|
||||
* @type {Element<T> | Empty}
|
||||
*/
|
||||
export class List {
|
||||
/**
|
||||
|
@ -145,7 +143,7 @@ export class List {
|
|||
* @returns {List<T>}
|
||||
*/
|
||||
static of(value) {
|
||||
return new ListSuspend(value, List.empty)
|
||||
return empty.prepend(value)
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -169,16 +167,26 @@ export class List {
|
|||
if (next.done) {
|
||||
return List.empty()
|
||||
} else {
|
||||
return new ListSuspend(next.value, () => List.fromIterator(iterator))
|
||||
return new Element(next.value, () => List.fromIterator(iterator))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T
|
||||
* @param {T} head
|
||||
* @param {List<T>} tail
|
||||
* @returns {List<T>}
|
||||
*/
|
||||
static cons(head, tail) {
|
||||
return new Element(head, () => tail)
|
||||
}
|
||||
|
||||
static empty() {
|
||||
return Empty
|
||||
}
|
||||
}
|
||||
|
||||
const Empty = new ListPure(Nil)
|
||||
const empty = new Empty()
|
||||
|
||||
/**
|
||||
* @template T
|
||||
|
@ -188,3 +196,7 @@ const Empty = new ListPure(Nil)
|
|||
*/
|
||||
const reduceArray = (acc, value) => acc.concat(value)
|
||||
|
||||
const arr = Array.from({ length: 10 })
|
||||
const list = List.from(arr)
|
||||
list.map(x => x * 2)
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue