Skip to content
kata / a language workbench

Functions

Functions are values that accept positional arguments and evaluate a body. KataScript uses func for declarations and anonymous function expressions.

func name(parameters): ReturnType { body } defines a named function. Each parameter can optionally have a : Type annotation, and the return annotation is also optional. Call a function with name(arguments); the argument count must match.

func multiply(a: Int, b: Int): Int {
ret a * b
}
print(multiply(6, 7))

Annotations are checked at runtime. Named function declarations support forward references within their scope and mutual recursion; execute the definitions before entering a recursive cycle. Recursive evaluation uses the host stack, so keep recursive examples small.

ret expression immediately leaves the enclosing function. A final expression also supplies the function’s return value. A body ending without a value-producing expression returns nil. A trailing semicolon does not suppress a returned expression value.

func square(n: Int): Int { n * n }
func describe(n: Int): Str {
if n < 0 { ret "negative" }
ret "nonnegative"
}
print(square(7))
print(describe(-1))

Postfix ? is another early-return path: it returns an unsuccessful enum value from the current function. See unwrap and propagate.

func(parameters): ReturnType { body } without a name is an anonymous function expression. Bind it, pass it to another function, or return it. Func is the runtime type for function values; it does not encode a parameter list or return signature.

func make_counter(): Func {
let count = 0
ret func(): Int {
count = count + 1
ret count
}
}
let next = make_counter()
print(next())
print(next())

A closure captures lexical bindings by shared reference. Reassignment is visible to the closure and its enclosing scope. Shadowing creates a new binding and does not redirect an existing capture. Anonymous functions are ordinary expressions, so they do not receive named-declaration hoisting. See the counter tutorial for independent instances.

An impl Type { ... } block associates functions with a type. An instance method explicitly takes self as its first parameter; callers omit that argument in value.method(...). A method without self is static and can be called on the type. Self names the implementing type inside the block.

kind Counter { value: Int }
impl Counter {
func new(): Self { ret Counter { value: 0 } }
func add(self, amount: Int) { self.value = self.value + amount }
}
let counter = Counter.new()
counter.add(3)
print(counter.value)

Mutation copies the final self back to a direct receiver binding. Keep mutating calls in the form counter.add(3); nested receivers do not reliably retain updates. Generic implementations bind parameters using @, as in impl Box[@T]. Add as Interface to declare conformance. See interfaces.

These names are available without an import. They are ordinary identifiers rather than reserved keywords.

CallContract
print(values...)Display values separated by spaces, then a newline; return nil
typeof(value)Return the argument’s runtime type value; takes exactly one argument
panic(values...)Stop evaluation with a runtime error containing the displayed values
print("answer", 42)
print(typeof(42))

In the playground, print writes to the output panel. Running a script does not automatically print every expression’s result. Use Res for a failure your caller should handle; panic does not return a recoverable result. See standard library.