Skip to content

Commit

Permalink
feat(associative): re-add support for nodejs REPL inspection
Browse files Browse the repository at this point in the history
- custom Set/Map impls were showing up as empty in the REPL
  in recent Node versions (13.x)
- adding @Inspectable decorator mixin for all impls to provide hook
  for Node's inspection mechanism
- Ref: nodejs/node#32529
  • Loading branch information
postspectacular committed Mar 28, 2020
1 parent 9017761 commit 49024f7
Show file tree
Hide file tree
Showing 10 changed files with 90 additions and 40 deletions.
3 changes: 3 additions & 0 deletions packages/associative/package.json
Expand Up @@ -70,6 +70,9 @@
"publishConfig": {
"access": "public"
},
"browser": {
"util": false
},
"sideEffects": false,
"thi.ng": {
"year": 2017
Expand Down
2 changes: 1 addition & 1 deletion packages/associative/src/api.ts
Expand Up @@ -7,7 +7,7 @@ import type {
IEquiv,
IGet,
IInto,
Predicate2
Predicate2,
} from "@thi.ng/api";

export interface IEquivSet<T>
Expand Down
5 changes: 5 additions & 0 deletions packages/associative/src/array-set.ts
Expand Up @@ -2,6 +2,7 @@ import { SEMAPHORE } from "@thi.ng/api";
import { equiv } from "@thi.ng/equiv";
import { dissoc } from "./dissoc";
import { equivSet } from "./internal/equiv";
import { inspectable } from "./internal/inspect";
import { into } from "./into";
import type { Fn3, Pair, Predicate2 } from "@thi.ng/api";
import type { EquivSetOpts, IEquivSet } from "./api";
Expand All @@ -25,6 +26,7 @@ const __vals = (inst: ArraySet<any>) => __private.get(inst)!.vals;
* Additionally, the type also implements the {@link @thi.ng/api#ICopy}, {@link @thi.ng/api#IEmpty} and
* {@link @thi.ng/api#IEquiv} interfaces itself.
*/
@inspectable
export class ArraySet<T> extends Set<T> implements IEquivSet<T> {
constructor(
vals?: Iterable<T> | null,
Expand Down Expand Up @@ -150,4 +152,7 @@ export class ArraySet<T> extends Set<T> implements IEquivSet<T> {
opts(): EquivSetOpts<T> {
return { equiv: __private.get(this)!.equiv };
}
// [INSPECT](depth: number, opts: any) {
// return inspectSet(this, depth, opts);
// }
}
17 changes: 6 additions & 11 deletions packages/associative/src/equiv-map.ts
Expand Up @@ -3,16 +3,10 @@ import { equiv } from "@thi.ng/equiv";
import { ArraySet } from "./array-set";
import { dissoc } from "./dissoc";
import { equivMap } from "./internal/equiv";
import { inspectable } from "./internal/inspect";
import { into } from "./into";
import type { Fn3, ICopy, IEmpty, IEquiv, IObjectOf, Pair } from "@thi.ng/api";
import type { EquivMapOpts, IEquivSet } from "./api";
import type {
Fn3,
ICopy,
IEmpty,
IEquiv,
IObjectOf,
Pair,
} from "@thi.ng/api";

interface MapProps<K, V> {
keys: IEquivSet<K>;
Expand All @@ -24,6 +18,7 @@ const __private = new WeakMap<EquivMap<any, any>, MapProps<any, any>>();

const __map = (map: EquivMap<any, any>) => __private.get(map)!.map;

@inspectable
export class EquivMap<K, V> extends Map<K, V>
implements
Iterable<Pair<K, V>>,
Expand All @@ -44,7 +39,7 @@ export class EquivMap<K, V> extends Map<K, V>
): EquivMap<string, T> {
const m = new EquivMap<string, T>(null, {
equiv: (a, b) => a === b,
...opts
...opts,
});
for (let k in obj) {
obj.hasOwnProperty(k) && m.set(k, obj[k]);
Expand All @@ -70,7 +65,7 @@ export class EquivMap<K, V> extends Map<K, V>
__private.set(this, {
keys: new _opts.keys(null, { equiv: _opts.equiv }),
map: new Map<K, V>(),
opts: _opts
opts: _opts,
});
if (pairs) {
this.into(pairs);
Expand Down Expand Up @@ -109,7 +104,7 @@ export class EquivMap<K, V> extends Map<K, V>
__private.set(m, {
keys: $this.keys.copy(),
map: new Map<K, V>($this.map),
opts: $this.opts
opts: $this.opts,
});
return m;
}
Expand Down
12 changes: 7 additions & 5 deletions packages/associative/src/hash-map.ts
Expand Up @@ -2,6 +2,7 @@ import { ceilPow2 } from "@thi.ng/binary";
import { equiv } from "@thi.ng/equiv";
import { dissoc } from "./dissoc";
import { equivMap } from "./internal/equiv";
import { inspectable } from "./internal/inspect";
import { into } from "./into";
import type {
Fn,
Expand All @@ -10,7 +11,7 @@ import type {
IEmpty,
IEquiv,
Pair,
Predicate2
Predicate2,
} from "@thi.ng/api";
import type { HashMapOpts } from "./api";

Expand All @@ -26,7 +27,7 @@ interface HashMapState<K, V> {
const __private = new WeakMap<HashMap<any, any>, HashMapState<any, any>>();

const __iterator = <K, V>(map: HashMap<K, V>, id: 0 | 1) =>
function*() {
function* () {
for (let p of __private.get(map)!.bins) {
if (p) yield p[id];
}
Expand Down Expand Up @@ -54,6 +55,7 @@ const DEFAULT_CAP = 16;
* ```
*
*/
@inspectable
export class HashMap<K, V> extends Map<K, V>
implements
Iterable<Pair<K, V>>,
Expand All @@ -69,7 +71,7 @@ export class HashMap<K, V> extends Map<K, V>
load: opts.load || 0.75,
mask: m,
bins: new Array(m + 1),
size: 0
size: 0,
});
if (pairs) {
this.into(pairs);
Expand Down Expand Up @@ -129,7 +131,7 @@ export class HashMap<K, V> extends Map<K, V>
Object.assign(__private.get(m), {
bins: $this.bins.slice(),
mask: $this.mask,
size: $this.size
size: $this.size,
});
return m;
}
Expand Down Expand Up @@ -204,7 +206,7 @@ export class HashMap<K, V> extends Map<K, V>
equiv: $this.equiv,
load: $this.load,
cap: $this.mask + 1,
...overrides
...overrides,
};
}

Expand Down
45 changes: 45 additions & 0 deletions packages/associative/src/internal/inspect.ts
@@ -0,0 +1,45 @@
import { mixin } from "@thi.ng/api";
import { isNode } from "@thi.ng/checks";
import { map } from "@thi.ng/transducers";

const inspect = isNode() ? require("util").inspect : null;

const inspectSet = (coll: Set<any>, opts: any) =>
[...map((x) => inspect(x, opts), coll)].join(", ");

const inspectMap = (coll: Map<any, any>, opts: any) =>
[
...map(([k, v]) => `${inspect(k, opts)} => ${inspect(v, opts)}`, coll),
].join(", ");

/**
* NodeJS inspection mixin
*
* @remarks
* Reference:
* https://nodejs.org/api/util.html#util_custom_inspection_functions_on_objects
*
* @internal
*/
export const inspectable = mixin({
[Symbol.for("nodejs.util.inspect.custom")](depth: number, opts: any) {
const name = this[Symbol.toStringTag];
const childOpts = {
...opts,
depth: opts.depth === null ? null : opts.depth - 1,
};
return depth >= 0
? [
`${name}(${this.size || 0}) {`,
inspect
? this instanceof Set
? inspectSet(this, childOpts)
: this instanceof Map
? inspectMap(this, childOpts)
: ""
: "",
"}",
].join(" ")
: opts.stylize(`[${name}]`, "special");
},
});
13 changes: 7 additions & 6 deletions packages/associative/src/ll-set.ts
Expand Up @@ -3,12 +3,9 @@ import { DCons } from "@thi.ng/dcons";
import { equiv } from "@thi.ng/equiv";
import { dissoc } from "./dissoc";
import { equivSet } from "./internal/equiv";
import { inspectable } from "./internal/inspect";
import { into } from "./into";
import type {
Fn3,
Pair,
Predicate2,
} from "@thi.ng/api";
import type { Fn3, Pair, Predicate2 } from "@thi.ng/api";
import type { EquivSetOpts, IEquivSet } from "./api";

interface SetProps<T> {
Expand All @@ -30,6 +27,7 @@ const __vals = (inst: LLSet<any>) => __private.get(inst)!.vals;
* Additionally, the type also implements the {@link @thi.ng/api#ICopy}, {@link @thi.ng/api#IEmpty} and
* {@link @thi.ng/api#IEquiv} interfaces itself.
*/
@inspectable
export class LLSet<T> extends Set<T> implements IEquivSet<T> {
constructor(
vals?: Iterable<T> | null,
Expand All @@ -38,7 +36,7 @@ export class LLSet<T> extends Set<T> implements IEquivSet<T> {
super();
__private.set(this, {
equiv: opts.equiv || equiv,
vals: new DCons<T>()
vals: new DCons<T>(),
});
vals && this.into(vals);
}
Expand Down Expand Up @@ -160,4 +158,7 @@ export class LLSet<T> extends Set<T> implements IEquivSet<T> {
opts(): EquivSetOpts<T> {
return { equiv: __private.get(this)!.equiv };
}
// [INSPECT](depth: number, opts: any) {
// return inspectSet(this, depth, opts);
// }
}
17 changes: 7 additions & 10 deletions packages/associative/src/sorted-map.ts
Expand Up @@ -3,13 +3,9 @@ import { compare } from "@thi.ng/compare";
import { isReduced, map } from "@thi.ng/transducers";
import { dissoc } from "./dissoc";
import { equivMap } from "./internal/equiv";
import { inspectable } from "./internal/inspect";
import { into } from "./into";
import type {
Comparator,
Fn3,
IObjectOf,
Pair,
} from "@thi.ng/api";
import type { Comparator, Fn3, IObjectOf, Pair } from "@thi.ng/api";
import type { ReductionFn } from "@thi.ng/transducers";
import type { SortedMapOpts } from "./api";

Expand Down Expand Up @@ -39,6 +35,7 @@ class Node<K, V> {
// http://fitzgeraldnick.com/2014/01/13/hiding-implementation-details-with-e6-weakmaps.html
const __private = new WeakMap<SortedMap<any, any>, SortedMapState<any, any>>();

@inspectable
export class SortedMap<K, V> extends Map<K, V> {
/**
* Creates new {@link SortedMap} instance from given object's key-value
Expand All @@ -53,7 +50,7 @@ export class SortedMap<K, V> extends Map<K, V> {
): SortedMap<string, T> {
const m = new SortedMap<string, T>(null, {
capacity: Object.keys(obj).length,
...opts
...opts,
});
for (let k in obj) {
obj.hasOwnProperty(k) && m.set(k, obj[k]);
Expand Down Expand Up @@ -85,7 +82,7 @@ export class SortedMap<K, V> extends Map<K, V> {
p: opts.probability || SortedMap.DEFAULT_P,
maxh,
length: 0,
h: 0
h: 0,
});
if (pairs) {
this.into(pairs);
Expand Down Expand Up @@ -146,7 +143,7 @@ export class SortedMap<K, V> extends Map<K, V> {
empty(): SortedMap<K, V> {
return new SortedMap<K, V>(null, {
...this.opts(),
capacity: SortedMap.DEFAULT_CAP
capacity: SortedMap.DEFAULT_CAP,
});
}

Expand Down Expand Up @@ -287,7 +284,7 @@ export class SortedMap<K, V> extends Map<K, V> {
return {
capacity: $this.cap,
compare: $this.cmp,
probability: $this.p
probability: $this.p,
};
}

Expand Down
7 changes: 6 additions & 1 deletion packages/associative/src/sorted-set.ts
Expand Up @@ -2,6 +2,7 @@ import { compare } from "@thi.ng/compare";
import { map } from "@thi.ng/transducers";
import { dissoc } from "./dissoc";
import { equivSet } from "./internal/equiv";
import { inspectable } from "./internal/inspect";
import { into } from "./into";
import { SortedMap } from "./sorted-map";
import type { Fn3, ICompare, Pair } from "@thi.ng/api";
Expand All @@ -28,6 +29,7 @@ const __private = new WeakMap<SortedSet<any>, SortedMap<any, any>>();
* This set uses a {@link SortedMap} as backing store and therefore has
* the same resizing characteristics.
*/
@inspectable
export class SortedSet<T> extends Set<T>
implements IEquivSet<T>, ICompare<Set<T>>, IReducible<any, T> {
/**
Expand Down Expand Up @@ -72,7 +74,7 @@ export class SortedSet<T> extends Set<T>
empty() {
return new SortedSet<T>(null, {
...this.opts(),
capacity: SortedMap.DEFAULT_CAP
capacity: SortedMap.DEFAULT_CAP,
});
}

Expand Down Expand Up @@ -159,4 +161,7 @@ export class SortedSet<T> extends Set<T>
opts(): SortedSetOpts<T> {
return __private.get(this)!.opts();
}
// [INSPECT](depth: number, opts: any) {
// return inspectSet(this, depth, opts);
// }
}
9 changes: 3 additions & 6 deletions packages/associative/src/sparse-set.ts
@@ -1,13 +1,9 @@
import { isNumber } from "@thi.ng/checks";
import { illegalArgs } from "@thi.ng/errors";
import { dissoc } from "./dissoc";
import { inspectable } from "./internal/inspect";
import { into } from "./into";
import type {
Fn3,
IEquiv,
Pair,
UIntArray
} from "@thi.ng/api";
import type { Fn3, IEquiv, Pair, UIntArray } from "@thi.ng/api";
import type { IEquivSet } from "./api";

interface SparseSetProps {
Expand All @@ -28,6 +24,7 @@ const fail = () => illegalArgs(`dense & sparse arrays must be of same size`);
* - {@link https://programmingpraxis.com/2012/03/09/sparse-sets/}
* - {@link https://blog.molecular-matters.com/2013/07/24/adventures-in-data-oriented-design-part-3c-external-references/}
*/
@inspectable
export abstract class ASparseSet<T extends UIntArray> extends Set<number>
implements IEquiv {
protected constructor(dense: T, sparse: T) {
Expand Down

0 comments on commit 49024f7

Please sign in to comment.