Skip to main content
Functions in OCat group a sequence of statements under a name so they can be executed on demand. A function is defined once and may be called any number of times. Functions have their own isolated scope — variables declared inside a function body do not leak into the caller’s context.

Defining a function

Use the func keyword followed by the function name, a parameter list (which may be empty), and a body enclosed in braces:
func <name>(<params>) {
    <statements>
}
A function with no parameters:
func greet() {
    print("Hello!")
}
The parser accepts a parameter list in the definition (type and name pairs separated by commas), but the current runner does not pass arguments into the function scope — parameters are parsed syntactically but are not yet bound at call time. See limitations below.

Calling a function

Use the call keyword followed by the function name and an argument list (which may be empty):
call greet()
call looks up the function by name in the current context. If no function with that name has been defined, a UndeclaredFunctionError is raised at runtime.
You must use call to invoke a function. Writing the function name without call is not valid syntax.

A complete example

number width = 1920
number height = 1080
string label = "resolution"

func printDimensions() {
    print(label)
    print(width)
    print(height)
}

func printHeader() {
    print("--- Display Info ---")
}

call printHeader()
call printDimensions()

Function scope

When a function is defined, the runtime creates a fresh CoreContext (containing empty variables and functions maps) and attaches it as the function’s scope. When the function is called, its body executes inside that isolated scope.
1

Function is defined

The func statement registers the function name in the current context’s functions map and stores the body AST together with a new empty scope.
2

Function is called

The call statement retrieves the function from the context and runs its body statements using the function’s own scope — not the caller’s scope.
3

Variables stay isolated

Any number, string, or bool declarations inside the function body are written to the function’s scope only. They are not visible to the caller after the function returns.
Because functions execute in their own scope, they do not have access to variables declared in the calling context. If you need a value inside a function, declare that variable inside the function body.
string appName = "MyApp"

func printName() {
    // appName is in the outer context, not this function's scope.
    // To print it here, redeclare it inside the function:
    string name = "MyApp"
    print(name)
}

call printName()

Redeclaration error

Defining the same function name twice raises an AlreadyDeclaredFunctionError at runtime:
func doWork() {
    print("working")
}

// This will cause an AlreadyDeclaredFunctionError:
// func doWork() {
//     print("again")
// }

Current limitations

The parser grammar accepts parameters in function definitions and argument expressions in call statements, but the runner does not currently bind parameter values to names inside the function scope. Defining parameters in the signature is valid syntax but has no effect at runtime.
// Syntactically valid, but 'name' is not available inside the body
func sayHello(string name) {
    print("hello")
}

call sayHello("Alice")
Functions do not return values. There is no return keyword. A function can produce output using print, but cannot pass a computed value back to the caller.
Because each function runs in its own isolated scope, calling another function from inside a function requires that function to be present in the inner scope. In the current implementation, functions defined at the top level are not automatically available inside another function’s scope.

Variables

Variable declarations and scope rules.

Imports

Share functions and variables across multiple files.

Build docs developers (and LLMs) love