Skip to content

Contextual parameters

A parameter with a leading dot is a contextual parameter. Its value is included in the prompt for every ask in that invocation, as an <input name="…"> block. A plain parameter is not rendered at all.

Parameter In the prompt
.issue: Issue (contextual) <input name="issue"> with the value
fallback: string (plain) nothing
${fallback} in the instruction the value spliced into your words, where it stands

See The prompt for the full rendering. An interpolated value is part of the instruction text, which the model follows; a contextual parameter is data in its own <input> block, which the system turn tells the model not to follow. Text you did not write yourself, such as a customer’s message or a document, belongs in a contextual parameter, never in a ${} hole.

classify-issue.tsi
export type Issue = { id: string; description: string };
// `issue` is visible to the model; `fallback` is a normal JS value only.
export infer function classifyIssue(.issue: Issue, fallback: string) {
const kind = ask `the kind of this issue`: string;
return kind || fallback;
}

Use the leading dot for input you want the model to see. Use an extractor, such as ask `instruction`: T, to request an answer.

You can declare several contextual parameters. They appear together in the context block shared by the invocation’s asks:

research.tsi
export infer function nextQuery(.question: string, .notes: string[]) {
return ask `the single best search query to advance the research`: string;
}

Nola derives an inference schema for each contextual parameter. Supported types include JSON-shaped objects, unions, Partial<T>, interface … extends, Record<string, T> and types imported from packages. See Schema derivation for the full list.

Map, Set, Promise, RegExp, functions and a generic type used without arguments are not derivable. Under the default policy a contextual parameter with such a type is a compile error, NOLA2008.

If a type cannot be derived, you can convert the value to a supported shape, keep it as a plain parameter, or change the derivation policy. Prefer converting it when the model needs the data:

top-label.tsi
// 1. Pass a JSON-shaped view as the contextual parameter.
export infer function topLabel(.index: { label: string; count: number }[]) {
return ask `the label with the highest count`: string;
}
// 2. Keep the exotic value, but as a PLAIN parameter — the model never sees
// its value, so nothing needs deriving.
export infer function topLabelFrom(.summary: string, index: Map<string, number>) {
const label = ask `the label with the highest count`: string;
return { label, count: index.get(label) ?? 0 };
}

To change the policy in nola.config.ts, use "prune" to drop only unsupported members or "omit" to omit the entire type from the context schema.

nola.config.ts
import { openai } from "@nola-lang/providers";
import { defineConfig } from "@nola-lang/runtime";
export default defineConfig({
model: openai({ model: "gpt-5-mini" }),
compiler: { underivableContextType: "prune" }, // "error" (default) | "prune" | "omit"
});

Write this setting as a literal. The editor reads the config without executing it, so it cannot use a computed value.

You can also add a leading dot to a const or let declaration inside an infer function or at module level. This contextual binding is visible to later asks in the same block and its nested blocks:

support.tsi
type Customer = { id: string; tier: "free" | "pro" };
export infer function reply(.mail: string) {
const .tone = "brief, friendly";
const .customer: Customer = { id: "c1", tier: "pro" };
return ask `a reply to the mail`: string;
}
  • Bindings appear after parameters in the function’s <context> block, as <input name="tone"> blocks. The value is read when the ask runs, so reassigning a let changes what later asks see.
  • Visibility is lexical: an ask sees the bindings declared before it, in its block or an enclosing block of the same body. const .bio = ask … never lists itself, and nothing declared after an ask reaches it.
  • An annotated binding (const .customer: Customer) derives its type exactly as a contextual parameter does, under the same underivableContextType policy; an unannotated one carries the value alone.
  • ask fn() passes the bindings visible at the call site as part of the caller’s context.
  • At module level, bindings populate the implicit <module> context. They reach an infer function when you call it with ask, not merely because it is declared in the same file. A module context statement is different: it applies to every ask below it, including inside the infer functions declared below it. See ask at module level.
  • var .x is not a binding form (NOLA1014); a pattern after the dot is NOLA1011; a dotted binding inside a plain function is NOLA1010.
  • NOLA1010 — . on a parameter of a plain (non-infer) function, or on a binding outside an infer body / the module body. Drop the dot; it is an ordinary argument or variable.
  • NOLA1013 — ..name: contextual parameters take one dot; two dots is the extractor sigil.
  • NOLA1012 — an incomplete . with no name after it.
  • NOLA1011 — . on a destructuring pattern or a parameter with a default value; reserved — use a plain identifier parameter.
  • NOLA1014 — var .x: a contextual binding is const .x or let .x.

Next: Context statements