matchOne
Match one query shape against one definition shape: in order, with arguments reordered, or with the result awaited.
import { permutations, unify } from "#codebase/tcmts/core/shape"; import type { Shape, ShapeMatch, Subst } from "#codebase/tcmts/core/shape"; export function matchOne(q: Shape, d: Shape): ShapeMatch | undefined { let best: ShapeMatch | undefined; const attempt = (qs: Shape, ds: Shape, extra: number, note?: string) => { const s: Subst = { q: new Map(), d: new Map(), cost: extra, notes: new Set(note ? [note] : []) }; if (unify(qs, ds, s) && (!best || s.cost < best.cost)) best = { cost: s.cost, notes: [...s.notes] }; }; if (q.k !== "fn" || d.k !== "fn") { attempt(q, d, 0); return best; } // an async function answers a query for its result type, a step away const rets: [Shape, number, string?][] = [[d.ret, 0]]; if (d.ret.k === "named" && d.ret.name === "Promise" && d.ret.args.length === 1 && !(q.ret.k === "named" && q.ret.name === "Promise")) { rets.push([d.ret.args[0]!, 1, "async"]); } const orders = q.params.length <= 5 ? permutations(q.params.map((_, i) => i)) : [q.params.map((_, i) => i)]; for (const [ret, cost, note] of rets) { for (const order of orders) { const reordered = order.some((j, i) => j !== i); const qp: Shape = { ...q, params: order.map((j) => q.params[j]!) }; const dp: Shape = { ...d, ret }; attempt(qp, dp, cost + (reordered ? 1 : 0), reordered ? "arguments reordered" : note); if (reordered && note && best) best.notes = [...new Set([...best.notes, note])]; } } return best; }
uses
- tcmts.core.shape.Shape
- tcmts.core.shape.ShapeMatch
- tcmts.core.shape.Subst
- tcmts.core.shape.permutations
- tcmts.core.shape.unify