From 1e09ca655f087a2773c24ddc8cc26f80fabd21bb Mon Sep 17 00:00:00 2001 From: rowan Date: Thu, 10 Apr 2025 04:37:45 -0500 Subject: [PATCH] wip interfaces --- src/algebra/interfaces.js | 41 +++++++++++++---- src/algebra/result.js | 3 ++ tests/units/monad.js | 94 ++++++++++++++++++--------------------- 3 files changed, 79 insertions(+), 59 deletions(-) diff --git a/src/algebra/interfaces.js b/src/algebra/interfaces.js index 37c71a1..08b6b9d 100644 --- a/src/algebra/interfaces.js +++ b/src/algebra/interfaces.js @@ -141,13 +141,14 @@ class Method { * @param {Function} target */ implement(builder, target) { - const impl = this.#implementation || this._defaultImplementation(builder.name) this._getInstallationPoint(target)[this.#name] = impl } } +const Implementations = Symbol() + class Interface { #name @@ -166,6 +167,27 @@ class Interface { 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 * @returns {this} @@ -200,23 +222,24 @@ class Interface { * @param {FunctionConstructor} 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) { - 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 diff --git a/src/algebra/result.js b/src/algebra/result.js index 16ce285..aef2458 100644 --- a/src/algebra/result.js +++ b/src/algebra/result.js @@ -19,6 +19,9 @@ export class Ok extends Algebra(Setoid, Monad, Foldable) { this.#value = value } + /** + * @param {Ok} other + */ equals(other) { return other instanceof Ok && other.chain(v => v === this.#value) diff --git a/tests/units/monad.js b/tests/units/monad.js index e052733..6ce7ed4 100644 --- a/tests/units/monad.js +++ b/tests/units/monad.js @@ -1,74 +1,68 @@ +// @ts-nocheck import { it, assert } from 'folktest' 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) => { - assert(M(a).chain(f).equals(f(a))) -} +const inc = x => F.of(x + 1) +const dbl = x => F.of(x * 2) -const rightIdentity = (M, 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) +const impl = i => m => i.implementedBy(m) export const Tests = [ it('unit is a left identity', () => { - Monads.forEach(monad => { - const f = inc(monad) - // NOTE: okay but like, shut up - // @ts-ignore - assert(monad.of(1).chain(f).equals(f(1))) + console.log(Chain.implementedBy(Result)) + Algebra.filter(impl(Chain)).forEach(algebra => { + assert( + algebra.of(1).chain(inc) == inc(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', () => { - Monads.forEach(monad => { - const m = monad.of(1) + Algebra.filter(impl(Chain)).forEach(algebra => { + const m = algebra.of(1) assert( - // NOTE: i heard this one already - // @ts-ignore - m.chain(monad.of).equals(m) + m.chain(algebra.of).equals(m), + `${algebra.name} is not a right identity` ) }) }), it('unit is associative', () => { - Monads.forEach(monad => { - const m = monad.of(1) - const f = inc(monad) - const g = dbl(monad) - console.log(m.chain(f)) + Algebra.filter(impl(Chain)).forEach(algebra => { + const a = algebra.of(1) - // NOTE: it was better the first time - // @ts-ignore - const x = m.chain(f).chain(g) - // @ts-ignore - const y = m.chain(x => f(x).chain(g)) + const x = a.chain(inc).chain(dbl) + const y = a.chain(x => inc(x).chain(dbl)) - assert( - x.equals(y) - ) + assert(x == y, `${algebra.name} is not associative`) + }) + }), + + 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)) }) }) ]