Compare commits

...

2 commits

Author SHA1 Message Date
55be1efee1 io wip 2025-04-07 03:03:40 -05:00
50f3cff3d9 List.count 2025-04-07 02:30:49 -05:00
3 changed files with 139 additions and 23 deletions

View file

@ -42,3 +42,10 @@ export const liftF = x => suspend(
/** @type {InferredMorphism<T>} */(pure)
)
/**
* @param {number} a
* @param {number} b
* @returns {number}
*/
export const sum = (a, b) => a + b

53
src/algebra/io.js Normal file
View file

@ -0,0 +1,53 @@
import { Algebra, Monad } from './index.js'
/** @import { Fn, InferredMorphism, Morphism } from './types.js' */
/** @template {Fn} T */
export class IO extends Algebra(Monad) {
_effect
/**
* @type {T} effect
*/
constructor(effect) {
super()
this._effect = effect
}
/**
* @template {Fn} T
* @param {T} a
*/
static of(a) {
return new IO(() => a)
}
/**
* @param {InferredMorphism<T>} f
*/
chain(f) {
return IO.of(() => f(this.run()).run())
}
/**
* @param {InferredMorphism<T>} f
*/
map(f) {
return IO.of(() => f(this.run()))
}
/**
* @template {Fn} U
* @this {IO<T>}
* @param {IO<Morphism<T, U>>} other
* @returns {IO<U>}
*/
ap(other) {
return IO.of(() => other.run()(this.run()))
}
run() {
return this._effect()
}
}

View file

@ -1,9 +1,7 @@
import { concat } from '../iter.js'
import { liftF } from './fn.js'
import { Pure, Suspend } from './free.js'
import { AlgebraWithBase, Category, Comonad, Foldable, Monoid } from './index.js'
/** @import { InferredMorphism } from './types.js' */
/** @import { InferredMorphism, Morphism } from './types.js' */
const Nil = Symbol('Nil')
@ -28,6 +26,27 @@ class ListPure extends AlgebraWithBase(Pure)(Category, Foldable, Monoid) {
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
@ -37,6 +56,18 @@ class ListPure extends AlgebraWithBase(Pure)(Category, Foldable, Monoid) {
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
}
}
/**
@ -45,24 +76,29 @@ class ListPure extends AlgebraWithBase(Pure)(Category, Foldable, Monoid) {
*/
class ListSuspend extends AlgebraWithBase(Suspend)(Foldable, Monoid, Comonad) {
/**
* @param {Iterator<T>} iter
* @param {T} head
* @param {() => List<T>} tail
*/
constructor(iter) {
const next = iter.next()
const value = /** @type {T} */ (next.value)
constructor(head, tail) {
super(
head,
/** @type {InferredMorphism<T>} */(tail)
)
}
const fn = /** @type {InferredMorphism<T>} */ (next.done ? List.empty : () => new ListSuspend(iter))
super(value, fn)
static empty() {
return List.empty()
}
/**
* @template {Iterable<T>} T
* @this {ListSuspend<Iterator<T>>}
* @param {Iterator<T>} value
* @template T
* @this {ListSuspend<T>}
* @param {T} value
* @returns {ListSuspend<T>}
*/
cons(value) {
return new ListSuspend(concat(value, this._value))
return new ListSuspend(value, () => this)
}
head() {
@ -88,8 +124,13 @@ class ListSuspend extends AlgebraWithBase(Suspend)(Foldable, Monoid, Comonad) {
return this.reduce(reduceArray, [])
}
static empty() {
return List.empty()
count() {
return this.tail().count() + 1
}
/** @returns {this is Empty} */
isEmpty() {
return false
}
}
@ -99,22 +140,37 @@ class ListSuspend extends AlgebraWithBase(Suspend)(Foldable, Monoid, Comonad) {
*/
export class List {
/**
* @template {Iterable<T>} T
* @template T
* @param {T} value
* @returns {ListSuspend<T>}
* @returns {List<T>}
*/
static of(value) {
// @ts-ignore
return new ListSuspend(liftF(value))
return new ListSuspend(value, List.empty)
}
/**
* @template T
* @param {Iterable<T>} iterator
* @returns {ListSuspend<T>}
* @param {Iterable<T>} iterable
* @returns {List<T>}
*/
static from(iterator) {
return new ListSuspend(Iterator.from(iterator))
static from(iterable) {
const iterator = Iterator.from(iterable)
return List.fromIterator(iterator)
}
/**
* @template T
* @param {Iterator<T>} iterator
* @returns {List<T>}
*/
static fromIterator(iterator) {
const next = iterator.next()
if (next.done) {
return List.empty()
} else {
return new ListSuspend(next.value, () => List.fromIterator(iterator))
}
}
static empty() {