Skip to content

infer functions

An infer function defines inference work that you can call from Nola or TypeScript. Calling it returns a lazy Intent<T> without running the body. Resolve the intent with ask from Nola or await from plain TypeScript to run it.

forms.tsi
infer function summarize(.text: string) {
return ask `a one-sentence summary`: string;
}
export infer function classify(.message: string) {
return ask `the category of the message`: string;
}

A template literal on its own line in the body is a context statement. Its text applies to every ask written after it in the invocation:

triage.tsi
export infer function triage(.ticket: string) {
`triage the ticket like a support lead`
return ask `the severity: low, medium or high`: "low" | "medium" | "high";
}

It can continue with values — `Page` oncall `when the ticket is an outage.` — and you can write several, anywhere in the body; each is read at each ask, so a statement placed before a loop shows the current state on every pass. The rules, the line rules and what the model sees are on Context statements.

Two spellings put a value in front of the model, and they mean different things. A contextual parameter, .ticket, is data: it renders as an <input name="ticket"> block after the text, and the system turn tells the model that input blocks are data, not instructions. A value in a context statement, or a ${team} hole, is part of your words, rendered where it stands, and the model follows it as instructions. So interpolate only text you control, and pass anything you did not write yourself as a contextual parameter. See Contextual parameters and The prompt.

The module body takes the same statements — see ask at module level. A backtick literal between the function name and its parameters (infer function f`…`(…)) is reserved for a later version (NOLA1019).

  • Top-level function declarations only. infer is a keyword only directly before function at statement or export position, so infer on a method, an arrow function or a function expression is a plain parse error (NOLA1001); an infer generator is reserved for a later version (NOLA1004); a declaration nested in another function is NOLA2003.

  • Never async. async infer function is a parse error — an infer function is implicitly awaitable through Intent.

  • await is legal in the body, for ordinary promises: fetch, a database, any library.

    enrich.tsi
    export infer function enrich(.handle: string, fetchProfile: (h: string) => Promise<string>) {
    const profile = await fetchProfile(handle); // ordinary promise
    return ask `the person's job title from: ${profile}`: string;
    }
  • ask only directly inside the body (or directly in the module body — see ask) — not inside a nested closure, not even one written inside the infer function (NOLA2001).

  • Export by name. export default infer function does not parse; write export infer function f(…) and let consumers import the name.

Nola can infer the return type from the body. If you want an explicit annotation, use Intent<T> where the body returns T, just as an async function uses Promise<T>.

get-user.tsi
import type { Intent } from "@nola-lang/runtime";
interface User {
name: string;
}
export infer function getUser(.message: string): Intent<User> {
const user = ask `the user described in the message`: User;
return user;
}

Do not annotate it Promise<T>: Intent<T> is PromiseLike<T>, not a Promise, so nola check reports TS2739 (missing catch, finally, [Symbol.toStringTag]).

main.ts
import { classify } from "./forms.tsi";
import { triage } from "./triage.tsi";
const category = await classify("My card was charged twice.");
const severity = await triage("Checkout is down for every customer in the EU.");
console.log(category, severity);

Awaiting the intent starts an invocation. Before resolving it, you can configure it with methods such as .withTimeout(ms), .detached() and .withModel(…). See Intent methods.

Next: Contextual parameters