Improvements to Typed Records
This commit is contained in:
parent
993689356b
commit
481f866ada
|
@ -2,7 +2,7 @@ import { Encoder } from "./codec";
|
|||
import { Tag } from "./constants";
|
||||
import { AsPreserve, PreserveOn } from "./symbols";
|
||||
import { DefaultPointer, is, Value } from "./values";
|
||||
import { Record } from './record';
|
||||
import { Record, Tuple } from './record';
|
||||
import { Dictionary, Set } from './dictionary';
|
||||
|
||||
export const IsPreservesAnnotated = Symbol.for('IsPreservesAnnotated');
|
||||
|
@ -72,7 +72,7 @@ export function strip<T extends object = DefaultPointer>(v: Value<T>, depth: num
|
|||
const nextDepth = depth - 1;
|
||||
function walk(v: Value<T>): Value<T> { return step(v, nextDepth); }
|
||||
|
||||
if (Record.isRecord<Value<T>, T>(v.item)) {
|
||||
if (Record.isRecord<Value<T>, Tuple<Value<T>>, T>(v.item)) {
|
||||
return Record(step(v.item.label, depth), v.item.map(walk));
|
||||
} else if (Array.isArray(v.item)) {
|
||||
return (v.item as Value<T>[]).map(walk);
|
||||
|
|
|
@ -6,6 +6,7 @@ import {
|
|||
Dictionary, Set, Bytes, Record, SingleFloat, DoubleFloat,
|
||||
BytesLike,
|
||||
Value,
|
||||
Tuple,
|
||||
} from './values';
|
||||
import { Tag } from './constants';
|
||||
|
||||
|
@ -379,7 +380,7 @@ export class Encoder<T extends object> {
|
|||
this.encodebytes(Tag.ByteString, bs);
|
||||
}
|
||||
}
|
||||
else if (Record.isRecord<Value<T>, T>(v)) {
|
||||
else if (Record.isRecord<Value<T>, Tuple<Value<T>>, T>(v)) {
|
||||
this.emitbyte(Tag.Record);
|
||||
this.push(v.label);
|
||||
for (let i of v) { this.push(i); }
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Bytes, Value, Record, Set, Dictionary, Single, Double, Annotated, annotate, Float } from "./values";
|
||||
import { Bytes, Value, Record, Set, Dictionary, Single, Double, Annotated, annotate, Float, Tuple } from "./values";
|
||||
|
||||
export type Fold<T extends object, R = Value<T>> = (v: Value<T>) => R;
|
||||
|
||||
|
@ -11,7 +11,7 @@ export interface FoldMethods<T extends object, R> {
|
|||
bytes(b: Bytes): R;
|
||||
symbol(s: symbol): R;
|
||||
|
||||
record(r: Record<Value<T>, T>, k: Fold<T, R>): R;
|
||||
record(r: Record<Value<T>, Tuple<Value<T>>, T>, k: Fold<T, R>): R;
|
||||
array(a: Array<Value<T>>, k: Fold<T, R>): R;
|
||||
set(s: Set<T>, k: Fold<T, R>): R;
|
||||
dictionary(d: Dictionary<Value<T>, T>, k: Fold<T, R>): R;
|
||||
|
@ -43,7 +43,7 @@ export abstract class ValueFold<T extends object, R extends object = T> implemen
|
|||
symbol(s: symbol): Value<R> {
|
||||
return s;
|
||||
}
|
||||
record(r: Record<Value<T>, T>, k: Fold<T, Value<R>>): Value<R> {
|
||||
record(r: Record<Value<T>, Tuple<Value<T>>, T>, k: Fold<T, Value<R>>): Value<R> {
|
||||
return Record(k(r.label), r.map(k));
|
||||
}
|
||||
array(a: Value<T>[], k: Fold<T, Value<R>>): Value<R> {
|
||||
|
@ -99,7 +99,7 @@ export function fold<T extends object, R>(v: Value<T>, o: FoldMethods<T, R>): R
|
|||
case 'symbol':
|
||||
return o.symbol(v);
|
||||
case 'object':
|
||||
if (Record.isRecord<Value<T>, T>(v)) {
|
||||
if (Record.isRecord<Value<T>, Tuple<Value<T>>, T>(v)) {
|
||||
return o.record(v, walk);
|
||||
} else if (Array.isArray(v)) {
|
||||
return o.array(v, walk);
|
||||
|
|
|
@ -2,21 +2,21 @@ import { DefaultPointer, is, Value } from "./values";
|
|||
|
||||
export type Tuple<T> = Array<T> | [T];
|
||||
|
||||
export type Record<LabelType extends Value<T>, T extends object = DefaultPointer>
|
||||
= Array<Value<T>> & { label: LabelType };
|
||||
export type Record<LabelType extends Value<T>, FieldsType extends Tuple<Value<T>>, T extends object = DefaultPointer>
|
||||
= FieldsType & { label: LabelType };
|
||||
|
||||
export type RecordGetters<L extends Value<T>, T extends object, Fs> = {
|
||||
[K in string & keyof Fs]: (r: Record<L, T>) => Fs[K];
|
||||
export type RecordGetters<Fs, R> = {
|
||||
[K in string & keyof Fs]: (r: R) => Fs[K];
|
||||
};
|
||||
|
||||
export type CtorTypes<Fs, Names extends Tuple<keyof Fs>, T extends object> =
|
||||
export type CtorTypes<Fs, Names extends Tuple<keyof Fs>> =
|
||||
{ [K in keyof Names]: Fs[keyof Fs & Names[K]] } & any[];
|
||||
|
||||
export interface RecordConstructor<L extends Value<T>, Fs, Names extends Tuple<keyof Fs>, T extends object = DefaultPointer> {
|
||||
(...fields: CtorTypes<Fs, Names, T>): Record<L, T>;
|
||||
(...fields: CtorTypes<Fs, Names>): Record<L, CtorTypes<Fs, Names>, T>;
|
||||
constructorInfo: RecordConstructorInfo<L, T>;
|
||||
isClassOf(v: any): v is Record<L, T>;
|
||||
_: RecordGetters<L, T, Fs>;
|
||||
isClassOf(v: any): v is Record<L, CtorTypes<Fs, Names>, T>;
|
||||
_: RecordGetters<Fs, Record<L, CtorTypes<Fs, Names>, T>>;
|
||||
};
|
||||
|
||||
export interface RecordConstructorInfo<L extends Value<T>, T extends object = DefaultPointer> {
|
||||
|
@ -24,15 +24,15 @@ export interface RecordConstructorInfo<L extends Value<T>, T extends object = De
|
|||
arity: number;
|
||||
}
|
||||
|
||||
export function Record<L extends Value<T>, T extends object = DefaultPointer>(
|
||||
label: L, fields: Array<Value<T>>): Record<L, T>
|
||||
export function Record<L extends Value<T>, FieldsType extends Tuple<Value<T>>, T extends object = DefaultPointer>(
|
||||
label: L, fields: FieldsType): Record<L, FieldsType, T>
|
||||
{
|
||||
(fields as any).label = label;
|
||||
return fields as Record<L, T>;
|
||||
return fields as Record<L, FieldsType, T>;
|
||||
}
|
||||
|
||||
export namespace Record {
|
||||
export function isRecord<L extends Value<T>, T extends object = DefaultPointer>(x: any): x is Record<L, T> {
|
||||
export function isRecord<L extends Value<T>, FieldsType extends Tuple<Value<T>>, T extends object = DefaultPointer>(x: any): x is Record<L, FieldsType, T> {
|
||||
return Array.isArray(x) && 'label' in x;
|
||||
}
|
||||
|
||||
|
@ -40,14 +40,14 @@ export namespace Record {
|
|||
return '<unprintable_preserves_field_value>';
|
||||
}
|
||||
|
||||
export function constructorInfo<L extends Value<T>, T extends object = DefaultPointer>(
|
||||
r: Record<L, T>): RecordConstructorInfo<L, T>
|
||||
export function constructorInfo<L extends Value<T>, FieldsType extends Tuple<Value<T>>, T extends object = DefaultPointer>(
|
||||
r: Record<L, FieldsType, T>): RecordConstructorInfo<L, T>
|
||||
{
|
||||
return { label: r.label, arity: r.length };
|
||||
}
|
||||
|
||||
export function isClassOf<L extends Value<T>, T extends object = DefaultPointer>(
|
||||
ci: RecordConstructorInfo<L, T>, v: any): v is Record<L, T>
|
||||
export function isClassOf<L extends Value<T>, FieldsType extends Tuple<Value<T>>, T extends object = DefaultPointer>(
|
||||
ci: RecordConstructorInfo<L, T>, v: any): v is Record<L, FieldsType, T>
|
||||
{
|
||||
return (Record.isRecord(v)) && is(ci.label, v.label) && (ci.arity === v.length);
|
||||
}
|
||||
|
@ -58,13 +58,13 @@ export namespace Record {
|
|||
{
|
||||
return <L extends Value<T>, Names extends Tuple<keyof Fs>>(label: L, fieldNames: Names) => {
|
||||
const ctor: RecordConstructor<L, Fs, Names, T> =
|
||||
((...fields: CtorTypes<Fs, Names, T>) =>
|
||||
((...fields: CtorTypes<Fs, Names>) =>
|
||||
Record(label, fields)) as RecordConstructor<L, Fs, Names, T>;
|
||||
const constructorInfo = { label, arity: fieldNames.length };
|
||||
ctor.constructorInfo = constructorInfo;
|
||||
ctor.isClassOf = (v: any): v is Record<L, T> => Record.isClassOf(constructorInfo, v);
|
||||
ctor.isClassOf = (v: any): v is Record<L, CtorTypes<Fs, Names>, T> => Record.isClassOf(constructorInfo, v);
|
||||
(ctor as any)._ = {};
|
||||
fieldNames.forEach((name, i) => (ctor._ as any)[name] = (r: Record<L, T>) => r[i]);
|
||||
fieldNames.forEach((name, i) => (ctor._ as any)[name] = (r: Record<L, CtorTypes<Fs, Names>, T>) => r[i]);
|
||||
return ctor;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
import { AsPreserve } from './symbols';
|
||||
import { Bytes } from './bytes';
|
||||
import { DoubleFloat, SingleFloat } from './float';
|
||||
import { Record } from './record';
|
||||
import { Record, Tuple } from './record';
|
||||
import { Annotated } from './annotated';
|
||||
import { Set, Dictionary } from './dictionary';
|
||||
|
||||
|
@ -17,7 +17,7 @@ export type DefaultPointer = object;
|
|||
|
||||
export type Value<T extends object = DefaultPointer> = Atom | Compound<T> | T | Annotated<T>;
|
||||
export type Atom = boolean | SingleFloat | DoubleFloat | number | string | Bytes | symbol;
|
||||
export type Compound<T extends object = DefaultPointer> = Record<any, T> | Array<Value<T>> | Set<T> | Dictionary<Value<T>, T>;
|
||||
export type Compound<T extends object = DefaultPointer> = Record<any, any, T> | Array<Value<T>> | Set<T> | Dictionary<Value<T>, T>;
|
||||
|
||||
export function fromJS<T extends object = DefaultPointer>(x: any): Value<T> {
|
||||
switch (typeof x) {
|
||||
|
@ -44,7 +44,7 @@ export function fromJS<T extends object = DefaultPointer>(x: any): Value<T> {
|
|||
if (typeof x[AsPreserve] === 'function') {
|
||||
return x[AsPreserve]();
|
||||
}
|
||||
if (Record.isRecord<Value<T>, T>(x)) {
|
||||
if (Record.isRecord<Value<T>, Tuple<Value<T>>, T>(x)) {
|
||||
return x;
|
||||
}
|
||||
if (Array.isArray(x)) {
|
||||
|
@ -112,7 +112,7 @@ Symbol.prototype.asPreservesText = function (): string {
|
|||
|
||||
Array.prototype.asPreservesText = function (): string {
|
||||
if ('label' in (this as any)) {
|
||||
const r = this as Record<any, any>;
|
||||
const r = this as Record<Value, Tuple<Value>, DefaultPointer>;
|
||||
return r.label.asPreservesText() +
|
||||
'(' + r.map(f => {
|
||||
try {
|
||||
|
|
|
@ -198,18 +198,19 @@ describe('common test suite', () => {
|
|||
back: 5 },
|
||||
annotation5: {
|
||||
forward: annotate<Pointer>(
|
||||
Record<symbol, Pointer>(Symbol.for('R'),
|
||||
[annotate<Pointer>(Symbol.for('f'),
|
||||
Symbol.for('af'))]),
|
||||
Record<symbol, any, Pointer>(Symbol.for('R'),
|
||||
[annotate<Pointer>(Symbol.for('f'),
|
||||
Symbol.for('af'))]),
|
||||
Symbol.for('ar')),
|
||||
back: Record<Value<Pointer>, Pointer>(Symbol.for('R'), [Symbol.for('f')])
|
||||
back: Record<Value<Pointer>, any, Pointer>(Symbol.for('R'), [Symbol.for('f')])
|
||||
},
|
||||
annotation6: {
|
||||
forward: Record<Value<Pointer>, Pointer>(annotate<Pointer>(Symbol.for('R'),
|
||||
Symbol.for('ar')),
|
||||
[annotate<Pointer>(Symbol.for('f'),
|
||||
Symbol.for('af'))]),
|
||||
back: Record<symbol, Pointer>(Symbol.for('R'), [Symbol.for('f')])
|
||||
forward: Record<Value<Pointer>, any, Pointer>(
|
||||
annotate<Pointer>(Symbol.for('R'),
|
||||
Symbol.for('ar')),
|
||||
[annotate<Pointer>(Symbol.for('f'),
|
||||
Symbol.for('af'))]),
|
||||
back: Record<symbol, any, Pointer>(Symbol.for('R'), [Symbol.for('f')])
|
||||
},
|
||||
annotation7: {
|
||||
forward: annotate<Pointer>([], Symbol.for('a'), Symbol.for('b'), Symbol.for('c')),
|
||||
|
@ -259,7 +260,7 @@ describe('common test suite', () => {
|
|||
const tests = peel(TestCases._.cases(peel(samples) as TestCases)) as Dictionary<Value<Pointer>, Pointer>;
|
||||
tests.forEach((t0: Value<Pointer>, tName0: Value<Pointer>) => {
|
||||
const tName = Symbol.keyFor(strip(tName0) as symbol)!;
|
||||
const t = peel(t0) as Record<symbol, Pointer>;
|
||||
const t = peel(t0) as Record<symbol, any, Pointer>;
|
||||
switch (t.label) {
|
||||
case Symbol.for('Test'):
|
||||
runTestCase('normal', tName, strip(t[0]) as Bytes, t[1]);
|
||||
|
|
Loading…
Reference in New Issue