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.
What the model sees
Section titled “What the model sees”| 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.
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.
Several compose into one block
Section titled “Several compose into one block”You can declare several contextual parameters. They appear together in the context block shared by the invocation’s asks:
export infer function nextQuery(.question: string, .notes: string[]) { return ask `the single best search query to advance the research`: string;}Derivable types
Section titled “Derivable types”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.
When a type is not derivable
Section titled “When a type is not derivable”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:
// 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.
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.
Contextual bindings
Section titled “Contextual bindings”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:
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 aletchanges 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 sameunderivableContextTypepolicy; 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 withask, 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 .xis not a binding form (NOLA1014); a pattern after the dot isNOLA1011; a dotted binding inside a plain function isNOLA1010.
Errors
Section titled “Errors”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 isconst .xorlet .x.
Next: Context statements