Question parameters
Question parameters are named inputs written in a trailing ?{...} group.
They complement positional parameters when call-site labels, defaults,
presence information, or compiler-resolved capabilities are part of the API.
Required named answers
Section titled “Required named answers”func make_range( ?{start: Int, stop: Int},) -> (Int, Int) do (start, stop)end
func main() -> Unit do range = make_range(?{stop: 10, start: 2}); Debug.trace(range);endAnswers are matched by name and may appear in any source order. Duplicate, unknown, missing, or incorrectly typed answers are compile-time errors.
Default answers
Section titled “Default answers”A default is evaluated by the callee only when its answer is omitted:
func scale( value : Int, ?{factor: Int default 2},) -> Int do value * factorend
func main() -> Unit do Debug.trace(scale(6)); Debug.trace(scale(6, ?{factor: 3}));endDefaults are processed in declaration order. A default may refer to positional parameters and earlier question parameters, but not later ones.
Presence questions
Section titled “Presence questions”A presence parameter uses name?: T. Inside the function its type is
Option[T]: omission becomes None, and an explicit answer becomes Some.
func choose_label( fallback : String, ?{label?: String},) -> String do switch label case Option::Some(value) then value case Option::None then fallback endend
func main() -> Unit do Console.println(choose_label("default")); Console.println(choose_label("default", ?{label: "custom"}));endPresence questions depend on the standard library’s canonical Option; see
Option and Result.
Question parameters in function types
Section titled “Question parameters in function types”Question inputs are part of a function’s type. A defaulted or presence parameter is represented as optional in that type, while the concrete default expression remains an implementation detail.
Explicit answers are evaluated after positional arguments, in the order written by the caller. Matching them by name does not reorder effects.
Contextual extension questions
Section titled “Contextual extension questions”The special form ?{extension Name: Interface for T} asks the compiler to
resolve an interface provider when the caller omits the answer:
func equivalent[T]( left : T, right : T, ?{extension Equal: IEqual for T},) -> Bool do Equal.equal(left, right)endThe uppercase binding is the selected immutable provider dictionary. Provider selection, association, activation, and ambiguity are explained in Interfaces and extensions.
Autofilled source locations
Section titled “Autofilled source locations”An autofilled question asks the compiler to attach a source range to a direct
function call. The answer type must be the opaque Meta.SourceLocation type.
It is an immutable description of a file and a source range; you normally do
not construct one yourself. You can pass it to another API, or turn it into a
human-readable string with its to_string() method.
There are two recipes:
autofill $loc_of($callsite)records the complete call expression;autofill $loc_of(parameter)records the expression supplied for that positional parameter.
For example, this diagnostic helper records the range of its value argument:
func describe_value( value : String, ?{location: Meta.SourceLocation autofill $loc_of(value)},) -> String do _ = value; "value at " ++ location.to_string()end
func main() -> Unit do Console.println(describe_value("ready"));endThe compiler fills location at describe_value("ready"); the range points
to the string expression, not to the function declaration. A call-site recipe
uses the whole call instead:
func mark_call( ?{location: Meta.SourceLocation autofill $loc_of($callsite)},) -> String do location.to_string()end
func main() -> Unit do Console.println(mark_call());endAutofill is only available on top-level ordinary functions. The function must be called directly by name (or through a module-qualified name); an autofilled function cannot be stored in a binding, returned, captured by a closure, or passed as an argument. Its function type still has an optional question parameter, but the autofill recipe is metadata used by direct-call checking.
An explicit answer suppresses autofill. This is how a wrapper preserves the original caller’s location instead of recording the wrapper’s own call:
func show_at( value : String, ?{location: Meta.SourceLocation autofill $loc_of(value)},) -> String do value ++ " at " ++ location.to_string()end
func forward_location( value : String, ?{location: Meta.SourceLocation},) -> String do show_at(value, ?{location: location})endThe wrapper’s incoming location is intentionally not constructed in user
code: a source location is opaque, so an application normally receives it from
another location-aware API. The important part is
show_at(value, ?{location: location}): an explicit question answer wins over
show_at’s autofill. If a wrapper omits that answer, the compiler records the
wrapper’s own call to show_at, not the original caller’s argument.
The standard testing helpers use these rules in normal test code:
test "reports the checked expressions" do Testing.assert(2 + 2 == 4)!; Testing.assert_equal(2 + 2, 4)!;endTesting.assert declares condition_loc with
$loc_of(condition). Testing.assert_equal declares actual_loc and
expected_loc with the corresponding recipes. When an assertion fails,
Testing.assert_equal keeps the rendered actual and expected values together
with both source locations, so a diagnostic can identify both expressions.
This is different from an ordinary default question. A default is an expression evaluated by the callee when the caller omits an answer; autofill is compiler metadata derived from the caller’s syntax, and it has the direct-call restriction described above.
Next: interfaces and extensions.