thi-ng/umbrella

View on GitHub
packages/defmulti/src/impls.ts

Summary

Maintainability
A
45 mins
Test Coverage
import type { IObjectOf } from "@thi.ng/api";
import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
import type { Implementation, MultiFn } from "./api.js";

/**
 * Syntax-sugar intended for sets of multi-methods sharing same dispatch
 * values / logic. Takes a dispatch value, an object of "is-a"
 * relationships and a number of multi-methods, each with an
 * implementation for the given dispatch value.
 *
 * @remarks
 * The relations object has dispatch values (parents) as keys and arrays
 * of multi-methods as their values. For each multi-method associates
 * the given `type` with the related parent dispatch value to delegate
 * to its implementation.
 *
 * The remaining implementations are associated with their related
 * multi-method and the given `type` dispatch value.
 *
 * @example
 * ```ts tangle:../export/implementations.ts
 * import { defmulti, implementations } from "@thi.ng/defmulti";
 *
 * const foo = defmulti((x) => x.id);
 * const bar = defmulti((x) => x.id);
 * const bax = defmulti((x) => x.id);
 * const baz = defmulti((x) => x.id);
 *
 * // define impls for dispatch value `a`
 * implementations(
 *   "a",
 *
 *   // delegate bax & baz impls to dispatch val `b`
 *   {
 *      b: [bax, baz]
 *   },
 *
 *   // concrete multi-fn impls
 *   foo,
 *   (x) => `foo: ${x.val}`,
 *
 *   bar,
 *   (x) => `bar: ${x.val.toUpperCase()}`
 * );
 *
 * // add parent impls
 * bax.add("b", (x) => `bax: ${x.id}`);
 * baz.add("c", (x) => `baz: ${x.id}`);
 *
 * // use "c" impl for "b"
 * baz.isa("b", "c");
 *
 * console.log(foo({ id: "a", val: "alice" }));
 * // "foo: alice"
 *
 * console.log(bar({ id: "a", val: "alice" }));
 * // "bar: ALICE"
 *
 * console.log(bax({ id: "a", val: "alice" }));
 * // "bax: a"
 *
 * console.log(baz({ id: "a", val: "alice" }));
 * // "baz: a"
 *
 * console.log([...baz.impls().keys()]);
 * // Set { "c", "a", "b" }
 * ```
 *
 * @param id - dispatch value / implementation ID
 * @param impls - implementations
 */
export const implementations = (
    id: PropertyKey,
    rels: IObjectOf<MultiFn<any>[]>,
    ...impls: (MultiFn<any> | Implementation<any>)[]
) => {
    impls.length & 1 &&
        illegalArgs("expected an even number of implementation items");
    if (rels) {
        for (let parent in rels) {
            for (let fn of rels[parent]) {
                fn.isa(id, parent);
            }
        }
    }
    for (let i = 0; i < impls.length; i += 2) {
        (<MultiFn<any>>impls[i]).add(id, impls[i + 1]);
    }
};