-
-
Notifications
You must be signed in to change notification settings - Fork 186
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Mutually recursive arbitrary builder #377
Conversation
Tasks to be tackle before merging this PR:
I made it work with arbitraries defined as: const { node, leaf } = fc.letrec(tie => ({
node: fc.tuple(
fc.frequency({ arbitrary: tie('leaf'), weight: 5 }, { arbitrary: tie('node'), weight: 1 }),
fc.frequency({ arbitrary: tie('leaf'), weight: 5 }, { arbitrary: tie('node'), weight: 1 })
),
leaf: fc.nat()
})); Or even: const { node, leaf } = fc.letrec(tie => ({
node: fc.tuple(
fc.oneof(tie('leaf'), tie('node')),
fc.oneof(tie('leaf'), tie('node'))
),
leaf: fc.nat()
})); But it failed (sometimes) with definitions like: const { node, leaf } = fc.letrec(tie => ({
node: fc.tuple(
fc.oneof(tie('leaf'), tie('node')),
fc.oneof(tie('leaf'), tie('node')),
fc.oneof(tie('leaf'), tie('node'))
),
leaf: fc.nat()
})); With |
I also tried with another signature for declare const Int: Arbitrary<number>;
declare const Str: Arbitrary<string>;
declare function MyArr<T>(a: Arbitrary<T>): Arbitrary<T[]>;
declare function letrecA<T>(
def: { [K in keyof T]: (d: { [KK in keyof T]: Arbitrary<T[KK]> }) => Arbitrary<T[K]> }
): { [K in keyof T]: Arbitrary<T[K]> };
const outA = letrecA({
a: () => Int,
b: ({ a }) => MyArr(a),
c: () => Str,
d: ({ c }) => c
});
declare function letrecB<T>(
def: { [K in keyof T]: (tie: <KK extends keyof T>(k: KK) => Arbitrary<T[KK]>) => Arbitrary<T[K]> }
): { [K in keyof T]: Arbitrary<T[K]> };
const outB = letrecB({
a: () => Int,
b: tie => MyArr(tie('a')),
c: () => Str,
d: tie => tie('c')
});
declare function letrecC<T>(
builder: (tie: (key: string) => Arbitrary<any>) => { [K in keyof T]: Arbitrary<T[K]> }
): { [K in keyof T]: Arbitrary<T[K]> };
const outC = letrecC(tie => ({
a: Int,
b: MyArr(tie('a')),
c: Str,
d: tie('c')
}));
declare function letrecD<T>(
def: { [K in keyof T]: ((tie: <KK extends keyof T>(k: KK) => Arbitrary<T[KK]>) => Arbitrary<T[K]>) | Arbitrary<T[K]> }
): { [K in keyof T]: Arbitrary<T[K]> };
const outD = letrecD({
a: Int,
b: tie => MyArr(tie('a')),
c: () => Str,
d: tie => tie('c')
}); None of A, B an D is compatible with TypeScript < 3.5 where |
Here are various tries to implement ❌ Not compiling // Not compiling because node is used before being initialized
const leaf = fc.nat();
const node = fc.tuple(fc.oneof(node, leaf), fc.oneof(node, leaf)); ❌ Not running // Unable to instantiate node because it instantiates another one which does the same..
const leaf = () => fc.nat();
const node = () => fc.tuple(fc.oneof(node(), leaf()), fc.oneof(node(), leaf())); ✔️ Working (see #378) // (+) Explicit terminal case
// (+) Ability to use depth to parametrize the sub arbitraries
// (+) No LazyArbitrary
// (-) Has to pass the n parameter to sub arbitraries
// (-) Typings
const lazy = (def, f) => {
let previous = {};
return cur => {
const n = (cur || 0) + 1;
if (cur >= 5) return def;
if (!previous.hasOwnProperty(n)) previous[n] = f(n);
return previous[n];
};
};
const leaf = () => fc.nat();
const node = lazy(leaf(), n => fc.tuple(fc.oneof(node(n), leaf()), fc.oneof(node(n), leaf()))); |
Fixes #374