solvers asyncGen - uhop/yopl GitHub Wiki
Asynchronous, pull-based solver. Combines the laziness of solvers-gen with the await-friendliness of solvers-async.
This is the corner of the four-solver matrix you reach when you need both:
-
awaitinside the proof loop — because some goal predicate has to do I/O, hit a remote service, or otherwise wait. Sync solvers cannot host such predicates. - Lazy, caller-driven enumeration — because you want to stop after the first solution, take only the first N, interleave solutions with other work, or compose them through async iterator utilities. Callback solvers cannot give you that without contortions.
The proof loop is async, so inline-function goal predicates may be async and the engine awaits them exactly like solvers-async. On top of that it yields each solution back to the caller, so the engine pauses at every successful proof until the consumer asks for the next one — no callback ever runs ahead of (or behind) the consumer.
This is the most powerful driver, and also the most expensive one: every step goes through both an async-function microtask and a generator suspension. Use it when you actually need both axes.
- You need
awaitinside goal predicates and you want lazy, take-N-style enumeration. - You want to feed solutions into an async pipeline (
for await, async iterator utilities, streaming response, etc.). - You want clean early termination (
break) without throwing.
If you only need one of the two axes, pick the cheaper specialised driver:
- async predicates, but happy with a callback → solvers-async
- lazy enumeration, but pure-sync predicates → solvers-gen
- neither → solve
import asyncGen from 'yopl/solvers/asyncGen.js';asyncGen(rules, name, args): AsyncGenerator<Env>Same rules / name / args semantics as solve. Each yielded value is the live unification environment for one solution; read what you need from it before resuming the iterator, since the next step will backtrack and reuse it.
import {variable} from 'deep6/env.js';
import assemble from 'deep6/traverse/assemble.js';
import asyncGen from 'yopl/solvers/asyncGen.js';
const X = variable('X');
for await (const env of asyncGen(rules, 'lookup', [42, X])) {
console.log(assemble(X, env));
break; // stops the proof loop cleanly, even mid-await
}async function* take(n, source) {
let i = 0;
for await (const v of source) {
if (i++ >= n) return;
yield v;
}
}
for await (const env of take(5, asyncGen(rules, 'fetchUser', [Id, Name]))) {
await respond(assemble(Name, env));
}