Documentation/Reference

Local functions, callable values and visibility

On this page

Local declarations and expression bodies — implemented

Noncapturing FUNCTION/SUB declarations may appear in an enclosing lexical block. They require declaration before use, can recurse and do not become module exports. Captures and generic-parent local specialization remain restricted. A local helper cannot read an outer local as an implicit capture.

PRINT Square(3.0)
FUNCTION Square(X AS DOUBLE) AS DOUBLE = X * X

Expression bodies use ordinary checking/ownership/error rules and need no END FUNCTION. SUB has no successful result and allows bare RETURN/fallthrough; returning a value is an error. FUNCTION must return a matching successful value on every reachable successful exit; bare RETURN/fallthrough fails. Both support FAILS and ordinary suspending calls. RETURN ERROR selects failure, not successful return.

Checked function values — implemented

FUNCTION Apply(handler AS CALLABLE(INTEGER) AS INTEGER, value AS INTEGER) AS INTEGER
    RETURN handler(value)
END FUNCTION
FUNCTION SquareInt(value AS INTEGER) AS INTEGER = value * value
PRINT Apply(SquareInt, 7)

A CALLABLE signature lists parameter types/modes, optional AS success type (no AS means SUB), and optional FAILS (ErrorType, ...). Named noncapturing functions produce callable values; indirect calls enforce access/result/error contracts. Owning copies have independent cleanup. Signatures cannot be compared or hashed. Read-only assignment may widen a failure set; BYREF signatures must match exactly. Callables use required positional arguments; declaration parameter defaults/names are not an unchecked indirect-call facility. See checked-call ABI and layout .

Generic declarations can use type parameters inside signatures. Earlier argument types supply context for overloaded function references; signatures must become concrete before invocation. Nested signatures, declared errors, BYREF/BYVAL modes and borrowed-result origins retain their ordinary checks. For example:

FUNCTION Apply[T](value AS T, transform AS CALLABLE(T) AS T) AS T
    RETURN transform(value)
END FUNCTION

This does not infer an arbitrary error set: a fallible generic callback needs an explicit checked FAILS contract. Generic CALLABLE(TAKE T) remains rejected when T is unresolved; concrete resource-consuming signatures work. Inferring T from an overloaded callback before other arguments provide context remains ambiguous.

Noncapturing foreign handlers

CALLBACK(...) selects a generated C ABI entry; CALLABLE NOSUSPEND(...) keeps the BASIL ABI for reviewed binding adapters. C CALLBACK must be infallible; BASIL NOSUSPEND may retain declared errors. Both check nonsuspending targets and transitive calls. Neither captures locals. C callback creation binds its coordinator thread before C can invoke it. Unsafe functions cannot become safe delegates. Full contracts and GTK example .

PUBLIC and PRIVATE — implemented scope

Module FUNCTION/SUB, TYPE, ERROR, ENUM and CONST declarations support PUBLIC/PRIVATE. Unmodified declarations remain public for compatibility. PRIVATE is enforced for qualified imports; public signatures cannot leak private nominal types/error sets. Local declarations cannot be exported with PUBLIC. PUBLIC is not C EXPORT or permission for unsynchronized mutation. Imported modules execute no initialization. Mutable module variables remain unimplemented; opaque types can expose explicitly PUBLIC readonly fields under the record rules . PUBLIC DIM does not reinterpret entry locals as globals. Imports defines current name resolution.

Lambdas and captures — implemented subset

minimum=10
matches=Filter([1,10,20],(n) => n>=minimum)
square=(x AS DOUBLE) => x*x
PRINT LEN(matches)
PRINT square(3.0)

The expression form is (parameters) => expression; () has no parameters. Use FUNCTION(parameters) [AS result] [FAILS …] through END FUNCTION for a block, or SUB(parameters) through END SUB for a procedure. Parameter types need AS or a concrete expected CALLABLE. Results and escaping errors infer from ordinary body checking unless declared; inconsistent/missing successful returns are rejected. An inferred lambda can specialize inside a generic parent.

Copyable free locals become independent readonly snapshots at construction. Captured strings/collections/callables own their copies and clean up on ordinary, failure and cancellation exits. Copies of closures are independent values. A later mutation of the original does not change a capture. Explicit local DIM and nested lambda parameters bind local names, rather than capturing matching outer resource variables. Named local helpers still do not capture implicitly.

Resources and task/channel capabilities cannot be captured. Pass an owned context explicitly to a library API, using TAKE for named resources, or pass ordinary checked function arguments. Mutable shared capture cells, borrowed/owning resource captures, anonymous CALLBACK/NOSUSPEND adaptation and nested named declarations inside anonymous bodies are not implemented. No GC/reference counting is added.

From the BASIL documentation. View source ↗

Find your way around BASIL

Type to search the manual.