wip interfaces

This commit is contained in:
Rowan 2025-04-10 04:37:45 -05:00
parent 38343d7ec0
commit 1e09ca655f
3 changed files with 79 additions and 59 deletions

View file

@ -141,13 +141,14 @@ class Method {
* @param {Function} target * @param {Function} target
*/ */
implement(builder, target) { implement(builder, target) {
const impl = this.#implementation || this._defaultImplementation(builder.name) const impl = this.#implementation || this._defaultImplementation(builder.name)
this._getInstallationPoint(target)[this.#name] = impl this._getInstallationPoint(target)[this.#name] = impl
} }
} }
const Implementations = Symbol()
class Interface { class Interface {
#name #name
@ -166,6 +167,27 @@ class Interface {
return this.#name return this.#name
} }
findInterfaces() {
let interfaces = new Set()
let current = Object.getPrototypeOf(this)
console.log(current)
while (current != null) {
interfaces = new Set(current[Implementations])
current = Object.getPrototypeOf(this)
}
return interfaces
}
/**
* @param {{ name: string }} type
* @returns {boolean}
*/
implementedBy(type) {
return this.findInterfaces().has(type)
}
/** /**
* @param {...(PropertyKey | Method)} methods * @param {...(PropertyKey | Method)} methods
* @returns {this} * @returns {this}
@ -200,23 +222,24 @@ class Interface {
* @param {FunctionConstructor} base * @param {FunctionConstructor} base
*/ */
build(base) { build(base) {
const interfaces = [...this.#interfaces.values()].map(x => x.intoWrapper()) const interfaces = [...this.#interfaces.values()]
const wrappers = interfaces.map(x => x.intoWrapper())
const Interfaces = mix(base).with(...interfaces) const Interfaces = mix(base).with(...wrappers)
const Algebra = class extends Interfaces { } //const Algebra = class extends Interfaces { }
for (const method of this.#methods) { for (const method of this.#methods) {
method.implement(this, Algebra) method.implement(this, Interfaces)
} }
return Algebra Interfaces.prototype[Implementations] = new Set([...interfaces])
return Interfaces
} }
} }
/** @template T */ export class BaseSet { }
export class BaseSet {
}
/** /**
* @param {PropertyKey} key * @param {PropertyKey} key

View file

@ -19,6 +19,9 @@ export class Ok extends Algebra(Setoid, Monad, Foldable) {
this.#value = value this.#value = value
} }
/**
* @param {Ok<T, E>} other
*/
equals(other) { equals(other) {
return other instanceof Ok && return other instanceof Ok &&
other.chain(v => v === this.#value) other.chain(v => v === this.#value)

View file

@ -1,74 +1,68 @@
// @ts-nocheck
import { it, assert } from 'folktest' import { it, assert } from 'folktest'
import { Option, Result } from '../../src/index.js' import { Option, Result } from '../../src/index.js'
import { Chain, Ord, Setoid } from '../../src/algebra/interfaces.js'
const Monads = [Option, Result] const Algebra = [Option, Result]
const leftIdentity = (M, a, f) => { const inc = x => F.of(x + 1)
assert(M(a).chain(f).equals(f(a))) const dbl = x => F.of(x * 2)
}
const rightIdentity = (M, m) => { const impl = i => m => i.implementedBy(m)
assert(m.chain(M).equals(m))
}
const associativity = (m, f, g) => {
assert(m.chain(f).chain(g).equals(m.chain(x => f(x).chain(g))))
}
export const prove = (M, m, a, f, g) => {
leftIdentity(M, a, f)
rightIdentity(M, m)
associativity(m, f, g)
}
const inc = F => x => F.of(x + 1)
const dbl = F => x => F.of(x * 2)
export const Tests = [ export const Tests = [
it('unit is a left identity', () => { it('unit is a left identity', () => {
Monads.forEach(monad => { console.log(Chain.implementedBy(Result))
const f = inc(monad) Algebra.filter(impl(Chain)).forEach(algebra => {
// NOTE: okay but like, shut up assert(
// @ts-ignore algebra.of(1).chain(inc) == inc(1),
assert(monad.of(1).chain(f).equals(f(1))) `${algebra.name} is not a left identity`
)
}) })
//prove(
// Ctr, M
// Ctr(1), m
// 1, a
// x => Ctr(x + 1), f
// x => Ctr(x * 2) g
//)
}), }),
it('unit is a right identity', () => { it('unit is a right identity', () => {
Monads.forEach(monad => { Algebra.filter(impl(Chain)).forEach(algebra => {
const m = monad.of(1) const m = algebra.of(1)
assert( assert(
// NOTE: i heard this one already m.chain(algebra.of).equals(m),
// @ts-ignore `${algebra.name} is not a right identity`
m.chain(monad.of).equals(m)
) )
}) })
}), }),
it('unit is associative', () => { it('unit is associative', () => {
Monads.forEach(monad => { Algebra.filter(impl(Chain)).forEach(algebra => {
const m = monad.of(1) const a = algebra.of(1)
const f = inc(monad)
const g = dbl(monad)
console.log(m.chain(f))
// NOTE: it was better the first time const x = a.chain(inc).chain(dbl)
// @ts-ignore const y = a.chain(x => inc(x).chain(dbl))
const x = m.chain(f).chain(g)
// @ts-ignore
const y = m.chain(x => f(x).chain(g))
assert( assert(x == y, `${algebra.name} is not associative`)
x.equals(y) })
) }),
it('unit is reflexive', () => {
Algebra.filter(impl(Setoid)).forEach(algebra => {
const a = algebra.of(1)
assert(a.equals(a), `${algebra.name} is not reflexive`)
})
}),
it('unit is symmetrical', () => {
Algebra.filter(impl(Setoid)).forEach(algebra => {
const a = algebra.of(1)
const b = algebra.of(1)
assert(a.equals(b) && b.equals(a), `${algebra.name} is not symmetrical`)
})
}),
it('unit is antisymmetrical', () => {
Algebra.filter(impl(Ord)).forEach(algebra => {
const a = algebra.of(1)
const b = algebra.of(1)
assert(a.lte(b) && b.lte(a) && a.equals(b))
}) })
}) })
] ]