@thi.ng/defmulti
Advanced tools
Comparing version 0.4.1 to 0.5.0
@@ -6,2 +6,13 @@ # Change Log | ||
# [0.5.0](https://github.com/thi-ng/umbrella/compare/@thi.ng/defmulti@0.4.1...@thi.ng/defmulti@0.5.0) (2018-10-24) | ||
### Features | ||
* **defmulti:** add support for dispatch value relationships / hierarchy ([a8c3898](https://github.com/thi-ng/umbrella/commit/a8c3898)) | ||
## [0.4.1](https://github.com/thi-ng/umbrella/compare/@thi.ng/defmulti@0.4.0...@thi.ng/defmulti@0.4.1) (2018-10-21) | ||
@@ -8,0 +19,0 @@ |
@@ -0,1 +1,2 @@ | ||
import { IObjectOf } from "@thi.ng/api/api"; | ||
export declare const DEFAULT: unique symbol; | ||
@@ -20,38 +21,29 @@ export declare type DispatchFn = (...args: any[]) => PropertyKey; | ||
export declare type Implementation8<A, B, C, D, E, F, G, H, T> = (a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H, ...xs: any[]) => T; | ||
export interface MultiFn<T> extends Implementation<T> { | ||
add: (id: PropertyKey, g: Implementation<T>) => boolean; | ||
remove: (id: PropertyKey) => boolean; | ||
export interface MultiFnBase<I> { | ||
add(id: PropertyKey, g: I): boolean; | ||
remove(id: PropertyKey): boolean; | ||
isa(id: PropertyKey, parent: PropertyKey): any; | ||
rels(): IObjectOf<Set<PropertyKey>>; | ||
parents(id: PropertyKey): Set<PropertyKey>; | ||
ancestors(id: PropertyKey): Set<PropertyKey>; | ||
} | ||
export interface MultiFn1<A, T> extends Implementation1<A, T> { | ||
add: (id: PropertyKey, g: Implementation1<A, T>) => boolean; | ||
remove: (id: PropertyKey) => boolean; | ||
export interface MultiFn<T> extends Implementation<T>, MultiFnBase<Implementation<T>> { | ||
} | ||
export interface MultiFn2<A, B, T> extends Implementation2<A, B, T> { | ||
add: (id: PropertyKey, g: Implementation2<A, B, T>) => boolean; | ||
remove: (id: PropertyKey) => boolean; | ||
export interface MultiFn1<A, T> extends Implementation1<A, T>, MultiFnBase<Implementation1<A, T>> { | ||
} | ||
export interface MultiFn3<A, B, C, T> extends Implementation3<A, B, C, T> { | ||
add: (id: PropertyKey, g: Implementation3<A, B, C, T>) => boolean; | ||
remove: (id: PropertyKey) => boolean; | ||
export interface MultiFn2<A, B, T> extends Implementation2<A, B, T>, MultiFnBase<Implementation2<A, B, T>> { | ||
} | ||
export interface MultiFn4<A, B, C, D, T> extends Implementation4<A, B, C, D, T> { | ||
add: (id: PropertyKey, g: Implementation4<A, B, C, D, T>) => boolean; | ||
remove: (id: PropertyKey) => boolean; | ||
export interface MultiFn3<A, B, C, T> extends Implementation3<A, B, C, T>, MultiFnBase<Implementation3<A, B, C, T>> { | ||
} | ||
export interface MultiFn5<A, B, C, D, E, T> extends Implementation5<A, B, C, D, E, T> { | ||
add: (id: PropertyKey, g: Implementation5<A, B, C, D, E, T>) => boolean; | ||
remove: (id: PropertyKey) => boolean; | ||
export interface MultiFn4<A, B, C, D, T> extends Implementation4<A, B, C, D, T>, MultiFnBase<Implementation4<A, B, C, D, T>> { | ||
} | ||
export interface MultiFn6<A, B, C, D, E, F, T> extends Implementation6<A, B, C, D, E, F, T> { | ||
add: (id: PropertyKey, g: Implementation6<A, B, C, D, E, F, T>) => boolean; | ||
remove: (id: PropertyKey) => boolean; | ||
export interface MultiFn5<A, B, C, D, E, T> extends Implementation5<A, B, C, D, E, T>, MultiFnBase<Implementation5<A, B, C, D, E, T>> { | ||
} | ||
export interface MultiFn7<A, B, C, D, E, F, G, T> extends Implementation7<A, B, C, D, E, F, G, T> { | ||
add: (id: PropertyKey, g: Implementation7<A, B, C, D, E, F, G, T>) => boolean; | ||
remove: (id: PropertyKey) => boolean; | ||
export interface MultiFn6<A, B, C, D, E, F, T> extends Implementation6<A, B, C, D, E, F, T>, MultiFnBase<Implementation6<A, B, C, D, E, F, T>> { | ||
} | ||
export interface MultiFn8<A, B, C, D, E, F, G, H, T> extends Implementation8<A, B, C, D, E, F, G, H, T> { | ||
add: (id: PropertyKey, g: Implementation8<A, B, C, D, E, F, G, H, T>) => boolean; | ||
remove: (id: PropertyKey) => boolean; | ||
export interface MultiFn7<A, B, C, D, E, F, G, T> extends Implementation7<A, B, C, D, E, F, G, T>, MultiFnBase<Implementation7<A, B, C, D, E, F, G, T>> { | ||
} | ||
export interface MultiFn8<A, B, C, D, E, F, G, H, T> extends Implementation8<A, B, C, D, E, F, G, H, T>, MultiFnBase<Implementation8<A, B, C, D, E, F, G, H, T>> { | ||
} | ||
export declare type AncestorDefs = IObjectOf<Iterable<PropertyKey>>; | ||
/** | ||
@@ -76,11 +68,11 @@ * Returns a new multi-dispatch function using the provided dispatcher. | ||
*/ | ||
export declare function defmulti<T>(f: DispatchFn): MultiFn<T>; | ||
export declare function defmulti<A, T>(f: DispatchFn1<A>): MultiFn1<A, T>; | ||
export declare function defmulti<A, B, T>(f: DispatchFn2<A, B>): MultiFn2<A, B, T>; | ||
export declare function defmulti<A, B, C, T>(f: DispatchFn3<A, B, C>): MultiFn3<A, B, C, T>; | ||
export declare function defmulti<A, B, C, D, T>(f: DispatchFn4<A, B, C, D>): MultiFn4<A, B, C, D, T>; | ||
export declare function defmulti<A, B, C, D, E, T>(f: DispatchFn5<A, B, C, D, E>): MultiFn5<A, B, C, D, E, T>; | ||
export declare function defmulti<A, B, C, D, E, F, T>(f: DispatchFn6<A, B, C, D, E, F>): MultiFn6<A, B, C, D, E, F, T>; | ||
export declare function defmulti<A, B, C, D, E, F, G, T>(f: DispatchFn7<A, B, C, D, E, F, G>): MultiFn7<A, B, C, D, E, F, G, T>; | ||
export declare function defmulti<A, B, C, D, E, F, G, H, T>(f: DispatchFn8<A, B, C, D, E, F, G, H>): MultiFn8<A, B, C, D, E, F, G, H, T>; | ||
export declare function defmulti<T>(f: DispatchFn, rels?: AncestorDefs): MultiFn<T>; | ||
export declare function defmulti<A, T>(f: DispatchFn1<A>, rels?: AncestorDefs): MultiFn1<A, T>; | ||
export declare function defmulti<A, B, T>(f: DispatchFn2<A, B>, rels?: AncestorDefs): MultiFn2<A, B, T>; | ||
export declare function defmulti<A, B, C, T>(f: DispatchFn3<A, B, C>, rels?: AncestorDefs): MultiFn3<A, B, C, T>; | ||
export declare function defmulti<A, B, C, D, T>(f: DispatchFn4<A, B, C, D>, rels?: AncestorDefs): MultiFn4<A, B, C, D, T>; | ||
export declare function defmulti<A, B, C, D, E, T>(f: DispatchFn5<A, B, C, D, E>, rels?: AncestorDefs): MultiFn5<A, B, C, D, E, T>; | ||
export declare function defmulti<A, B, C, D, E, F, T>(f: DispatchFn6<A, B, C, D, E, F>, rels?: AncestorDefs): MultiFn6<A, B, C, D, E, F, T>; | ||
export declare function defmulti<A, B, C, D, E, F, G, T>(f: DispatchFn7<A, B, C, D, E, F, G>, rels?: AncestorDefs): MultiFn7<A, B, C, D, E, F, G, T>; | ||
export declare function defmulti<A, B, C, D, E, F, G, H, T>(f: DispatchFn8<A, B, C, D, E, F, G, H>, rels?: AncestorDefs): MultiFn8<A, B, C, D, E, F, G, H, T>; | ||
/** | ||
@@ -87,0 +79,0 @@ * Returns a multi-dispatch function which delegates to one of the |
45
index.js
"use strict"; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const illegal_arguments_1 = require("@thi.ng/errors/illegal-arguments"); | ||
const unsupported_1 = require("@thi.ng/errors/unsupported"); | ||
const illegal_arity_1 = require("@thi.ng/errors/illegal-arity"); | ||
exports.DEFAULT = Symbol(); | ||
function defmulti(f) { | ||
function defmulti(f, ancestors) { | ||
let impls = {}; | ||
let rels = ancestors ? makeRels(ancestors) : {}; | ||
let fn = (...args) => { | ||
const id = f(...args); | ||
const g = impls[id] || impls[exports.DEFAULT]; | ||
return g ? g(...args) : illegal_arguments_1.illegalArgs(`missing implementation for: "${id.toString()}"`); | ||
const g = impls[id] || findImpl(impls, rels, id) || impls[exports.DEFAULT]; | ||
return g ? g(...args) : unsupported_1.unsupported(`missing implementation for: "${id.toString()}"`); | ||
}; | ||
@@ -25,2 +26,10 @@ fn.add = (id, g) => { | ||
}; | ||
fn.isa = (id, parent) => { | ||
let val = rels[id]; | ||
!val && (rels[id] = val = new Set()); | ||
val.add(parent); | ||
}; | ||
fn.rels = () => rels; | ||
fn.parents = (id) => rels[id]; | ||
fn.ancestors = (id) => new Set(findAncestors([], rels, id)); | ||
return fn; | ||
@@ -30,2 +39,30 @@ } | ||
; | ||
const findImpl = (impls, rels, id) => { | ||
const parents = rels[id]; | ||
if (!parents) | ||
return; | ||
for (let p of parents) { | ||
let impl = impls[p] || findImpl(impls, rels, p); | ||
if (impl) | ||
return impl; | ||
} | ||
}; | ||
const findAncestors = (acc, rels, id) => { | ||
const parents = rels[id]; | ||
if (parents) { | ||
for (let p of parents) { | ||
acc.push(p); | ||
findAncestors(acc, rels, p); | ||
} | ||
} | ||
return acc; | ||
}; | ||
const makeRels = (spec) => { | ||
const rels = {}; | ||
for (let k in spec) { | ||
const val = spec[k]; | ||
rels[k] = val instanceof Set ? val : new Set(val); | ||
} | ||
return rels; | ||
}; | ||
/** | ||
@@ -32,0 +69,0 @@ * Returns a multi-dispatch function which delegates to one of the |
{ | ||
"name": "@thi.ng/defmulti", | ||
"version": "0.4.1", | ||
"version": "0.5.0", | ||
"description": "Dynamically extensible multiple dispatch via user supplied dispatch function.", | ||
@@ -41,3 +41,3 @@ "main": "./index.js", | ||
}, | ||
"gitHead": "5bb513915cb3c533bd4278f6f365389b3664f4d1" | ||
"gitHead": "673bf50ff571fc65bd984d1f83929bcc69a8b394" | ||
} |
129
README.md
@@ -14,10 +14,6 @@ # @thi.ng/defmulti | ||
dispatch](https://en.wikipedia.org/wiki/Multiple_dispatch) via user | ||
supplied dispatch function, with minimal overhead. Provides generics for | ||
type checking up to 8 args, but generally works with any number of | ||
arguments. Why "only" 8? | ||
supplied dispatch function, with minimal overhead and support for | ||
dispatch value inheritance hierarchies (more flexible and independent of | ||
any actual JS type relationships). | ||
> "If you have a procedure with ten parameters, you probably missed some." | ||
> | ||
> -- Alan Perlis | ||
## Installation | ||
@@ -34,3 +30,3 @@ | ||
## Usage examples | ||
## API | ||
@@ -56,3 +52,92 @@ ### defmulti | ||
#### Dispatch value hierarchies | ||
To avoid code duplication, dispatch values can be associated in | ||
child-parent relationships and implementations only defined for some | ||
ancestors. Iff no implementation exists for a concrete dispatch value, | ||
`defmulti` first attempts to find an implementation for any ancestor | ||
dispatch value before using the `DEFAULT` implementation. | ||
These relationships can be defined via an additional (optional) object | ||
arg to `defmulti` and/or dynamically extended via the `.isa(child, | ||
parent)` call to the multi-function. Relationships can also be queried | ||
via `.parents(id)` and `.ancestors(id)`. | ||
Note: If multiple direct parents are defined for a dispatch value, then | ||
it's currently undefined which implementation will be picked. If this | ||
causes issues to people, parents could be implemented as sorted list | ||
(each parent with weight) instead of Sets, but this will have perf | ||
impact... please open an issue if you run into problems! | ||
```ts | ||
const foo = defmulti((x )=> x); | ||
foo.isa(23, "odd"); | ||
foo.isa(42, "even"); | ||
foo.isa("odd", "number"); | ||
foo.isa("even", "number"); | ||
foo.parents(23); // Set { "odd" } | ||
foo.ancestors(23); // Set { "odd", "number" } | ||
foo.parents(1); // undefined | ||
foo.ancestors(1); // Set { } | ||
// add some implementations | ||
foo.add("odd", (x) => `${x} is odd`); | ||
foo.add("number", (x) => `${x} is a number`); | ||
foo(23); // "23 is odd" | ||
foo(42); // "42 is a number" | ||
foo(1); // error (missing impl & no default) | ||
``` | ||
Same example, but with relationships provided as argument to `defmulti`: | ||
```ts | ||
const foo = defmulti((x) => x, { | ||
23: ["odd"], | ||
42: ["even"], | ||
"odd": ["number"], | ||
"even": ["number"], | ||
}); | ||
foo.rels(); | ||
// { "23": Set { "odd" }, | ||
// "42": Set { "even" }, | ||
// odd: Set { "number" }, | ||
// even: Set { "number" } } | ||
``` | ||
### defmultiN | ||
Returns a multi-dispatch function which delegates to one of the provided | ||
implementations, based on the arity (number of args) when the function | ||
is called. Internally uses `defmulti`, so new arities can be dynamically | ||
added (or removed) at a later time. `defmultiN` also registers a | ||
`DEFAULT` implementation which simply throws an `IllegalArityError` when | ||
invoked. | ||
**Note:** Unlike `defmulti` no argument type checking is supported, | ||
however you can specify the return type for the generated function. | ||
```ts | ||
const foo = defmultiN<string>({ | ||
0: () => "zero", | ||
1: (x) => `one: ${x}`, | ||
3: (x, y, z) => `three: ${x}, ${y}, ${z}` | ||
}); | ||
foo(); | ||
// zero | ||
foo(23); | ||
// one: 23 | ||
foo(1, 2, 3); | ||
// three: 1, 2, 3 | ||
foo(1, 2); | ||
// Error: illegal arity: 2 | ||
``` | ||
## Usage examples | ||
```ts | ||
import { defmulti, DEFAULT } from "@thi.ng/defmulti"; | ||
@@ -85,2 +170,3 @@ | ||
#### Dynamic dispatch: Simple S-expression interpreter | ||
@@ -127,31 +213,4 @@ | ||
### defmultiN | ||
Returns a multi-dispatch function which delegates to one of the provided | ||
implementations, based on the arity (number of args) when the function | ||
is called. Internally uses `defmulti`, so new arities can be dynamically | ||
added (or removed) at a later time. `defmultiN` also registers a | ||
`DEFAULT` implementation which simply throws an `IllegalArityError` when | ||
invoked. | ||
**Note:** Unlike `defmulti` no argument type checking is supported, | ||
however you can specify the return type for the generated function. | ||
```ts | ||
const foo = defmultiN<string>({ | ||
0: () => "zero", | ||
1: (x) => `one: ${x}`, | ||
3: (x, y, z) => `three: ${x}, ${y}, ${z}` | ||
}); | ||
foo(); | ||
// zero | ||
foo(23); | ||
// one: 23 | ||
foo(1, 2, 3); | ||
// three: 1, 2, 3 | ||
foo(1, 2); | ||
// Error: illegal arity: 2 | ||
``` | ||
## Authors | ||
@@ -158,0 +217,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
32629
220
218