UNPKG

mcard-js

Version:

MCard - Content-addressable storage with cryptographic hashing, handle resolution, and vector search for Node.js and browsers

183 lines 7.96 kB
/** * Lambda Term - Algebraic Data Type for Lambda Calculus * * Represents Lambda Calculus terms as content-addressable MCards. * Each term variant is stored as JSON in MCard content, with sub-terms * referenced by their SHA-256 hashes for structural sharing. * * The three term constructors mirror the BNF grammar: * M, N ::= x | λx.M | M N * * @module mcard-js/ptr/lambda/LambdaTerm */ import { MCard } from '../../model/MCard'; // ───────────────────────────────────────────────────────────────────────────── // Constructors (Smart Constructors) // ───────────────────────────────────────────────────────────────────────────── /** * Create a variable term */ export function mkVar(name) { return { tag: 'Var', name }; } /** * Create an abstraction term */ export function mkAbs(param, bodyHash) { return { tag: 'Abs', param, body: bodyHash }; } /** * Create an application term */ export function mkApp(funcHash, argHash) { return { tag: 'App', func: funcHash, arg: argHash }; } // ───────────────────────────────────────────────────────────────────────────── // MCard Serialization // ───────────────────────────────────────────────────────────────────────────── /** * Serialize a Lambda term to MCard content (JSON string) */ export function serializeTerm(term) { return JSON.stringify(term); } /** * Deserialize MCard content to Lambda term */ export function deserializeTerm(content) { const parsed = JSON.parse(content); // Validate structure if (!parsed.tag) { throw new Error('Invalid Lambda term: missing tag'); } switch (parsed.tag) { case 'Var': if (typeof parsed.name !== 'string') { throw new Error('Invalid Var term: name must be string'); } return mkVar(parsed.name); case 'Abs': if (typeof parsed.param !== 'string' || typeof parsed.body !== 'string') { throw new Error('Invalid Abs term: param and body must be strings'); } return mkAbs(parsed.param, parsed.body); case 'App': if (typeof parsed.func !== 'string' || typeof parsed.arg !== 'string') { throw new Error('Invalid App term: func and arg must be strings'); } return mkApp(parsed.func, parsed.arg); default: throw new Error(`Unknown Lambda term tag: ${parsed.tag}`); } } /** * Create an MCard from a Lambda term */ export async function termToMCard(term) { return MCard.create(serializeTerm(term)); } /** * Extract Lambda term from MCard */ export function mcardToTerm(mcard) { return deserializeTerm(mcard.getContentAsText()); } // ───────────────────────────────────────────────────────────────────────────── // Term Cache (avoids redundant SQLite lookups during reduction) // ───────────────────────────────────────────────────────────────────────────── // Global term cache: hash -> term // This dramatically improves performance for lambda reduction which repeatedly // looks up the same sub-terms. Cache is shared across all collections. const termCache = new Map(); /** * Clear the term cache. Call this between unrelated reduction sessions * if memory is a concern. */ export function clearTermCache() { termCache.clear(); } // ───────────────────────────────────────────────────────────────────────────── // Collection Operations // ───────────────────────────────────────────────────────────────────────────── /** * Store a Lambda term in the collection and return its hash */ export async function storeTerm(collection, term) { const mcard = await termToMCard(term); // Cache the term before storing termCache.set(mcard.hash, term); await collection.add(mcard); return mcard.hash; } /** * Retrieve a Lambda term from the collection by hash */ export async function loadTerm(collection, hash) { // Check cache first const cached = termCache.get(hash); if (cached) return cached; // Fall back to collection lookup const mcard = await collection.get(hash); if (!mcard) return null; const term = mcardToTerm(mcard); // Cache for future lookups termCache.set(hash, term); return term; } /** * Check if a term exists in the collection */ export async function termExists(collection, hash) { const mcard = await collection.get(hash); return mcard !== null; } // ───────────────────────────────────────────────────────────────────────────── // Pretty Printing // ───────────────────────────────────────────────────────────────────────────── /** * Pretty-print a Lambda term (shallow - shows hashes for subterms) */ export function prettyPrintShallow(term) { switch (term.tag) { case 'Var': return term.name; case 'Abs': return ${term.param}.〈${term.body.substring(0, 8)}…〉`; case 'App': return `(〈${term.func.substring(0, 8)}…〉 〈${term.arg.substring(0, 8)}…〉)`; } } /** * Pretty-print a Lambda term (deep - resolves all subterms from collection) */ export async function prettyPrintDeep(collection, hash) { const term = await loadTerm(collection, hash); if (!term) return `〈missing:${hash.substring(0, 8)}…〉`; switch (term.tag) { case 'Var': return term.name; case 'Abs': const body = await prettyPrintDeep(collection, term.body); return `(λ${term.param}.${body})`; case 'App': const func = await prettyPrintDeep(collection, term.func); const arg = await prettyPrintDeep(collection, term.arg); return `(${func} ${arg})`; } } // ───────────────────────────────────────────────────────────────────────────── // Type Guards // ───────────────────────────────────────────────────────────────────────────── export function isVar(term) { return term.tag === 'Var'; } export function isAbs(term) { return term.tag === 'Abs'; } export function isApp(term) { return term.tag === 'App'; } //# sourceMappingURL=LambdaTerm.js.map