Functions
Implementation status: func declarations / named parameters / default parameters / async / throws / requires Pure / requires IO are all Implemented; @escaping / @autoclosure / generic type-erasure reflection are Not implemented
Functions are the basic building blocks of SYX programs. This chapter covers declaration syntax, parameters, returns, effect annotations (requires), async/await, throws, and cross-module visibility.
Definition and Call
func greet(name: String) {
print("Hello, \(name)")
}
func sumInts(a: Int, b: Int) -> Int {
return a + b
}
func main() {
greet(name: "SYX") // "Hello, SYX"
print(sumInts(a: 1, b: 2)) // 3
}Named parameters: when calling a labeled parameter you must write
label: value. A formal parameter may use_ nameto drop the external label (then calls can only pass it positionally); it may uselabel nameto give an external label different from the internal name (then calls must writelabel:and cannot write the internal name).
func greet(name: String) { print("Hello, \(name)") }
func move(to destination: String) { }
func join(_ task: String) -> String { return task }
greet(name: "SYX") // ✅ The label name: is required
move(to: "home") // ✅ External label to:, internal name destination
join("worker") // ✅ _ drops the label → positional argument
join(task: "worker") // ❌ S042: a `_` parameter cannot carry a label| Formal parameter form | External label strategy | Call form |
|---|---|---|
name: Type | Same as the internal name name | name: value (required) |
label name: Type | External name label | label: value (required) |
_ name: Type | Omitted | Only positionally |
| Element | Description |
|---|---|
func | Keyword |
name | Function name (lowerCamelCase) |
(param: Type, ...) | Formal parameter list |
-> ReturnType | Return type (defaults to Void) |
block | Function body |
Multiple Return Values via Tuples
SYX tuples can serve as "multiple return values":
func divmod(a: Int, b: Int) -> (Int, Int) {
return (a / b, a % b)
}
let (q, r) = divmod(a: 17, b: 5)
print("\(q) rem \(r)") // 3 rem 2Tuple destructuring (q, r) = … is legal in let / var bindings (MVP GA; fixtures/lang/tuple.sy). Named elements (x: 1, y: 2) and members .x / .0 are usable; this is not the complete Swift tuple protocol.
Default Parameters
func greet(name: String, greeting: String = "Hello") {
print("\(greeting), \(name)")
}
greet(name: "SYX") // "Hello, SYX"
greet(name: "SYX", greeting: "Hi") // "Hi, SYX"Limit: a default value must be a compile-time constant or a bare literal; it cannot reference runtime variables or call functions. Parameters with defaults may be placed anywhere (there is no Swift-style "put default parameters last" requirement).
Access Control
public func exported() { } // Importable across modules
internal func moduleOnly() { } // Within the module (default)
private func hidden() { } // Within the file / scopeSee Chapter 28, Access Control.
// Single-file stand-in for cross-module `import math` (see Ch28/Ch29).
public func mathAdd(a: Int, b: Int) -> Int {
return a + b
}
func main() {
print(mathAdd(a: 1, b: 2)) // ✅ public
// math.internal_helper() // ❌ S005: accessing internal across modules
}async Functions
async func httpGet(_ url: String) -> String {
return "ok"
}
async func fetch() -> String {
return await httpGet("https://example.com")
}The async keyword marks a function as a coroutine; inside its body you may use await:
async func fetchA() -> String { return "a" }
async func fetchB() -> String { return "b" }
async func fetchTwo() -> (String, String) {
let a = await fetchA()
let b = await fetchB()
return (a, b)
}awaitis legal only inside anasyncfunction body- Using
awaitin a non-asyncfunction → compile error S020 async func main()is a legal program entry point
Implementation: multiple-
awaitstate machines (Implemented, testruntime/async_native.rs). Underneath this is a generator-based state machine; see Chapter 18, Concurrency.
async let
Start multiple subtasks in parallel and wait for all of them:
async func fetchA() -> Int { return 1 }
async func fetchB() -> Int { return 2 }
async func parallel() -> Int {
async let a = fetchA() // Start a subtask
async let b = fetchB() // Start a subtask
let x = await a // Wait for a
let y = await b // Wait for b
return x + y
}Semantics:
async letdesugars into detach + join (behavior frozen in SYX 132).ais anAsyncValue<T>, andawait ablocks the current task until the subtask completes.
throws Functions
import System
func loadFile(path: String) throws -> String {
if (!File.exists(path)) {
throw "not found: \(path)"
}
return File.read(path)
}| Clause | Meaning |
|---|---|
throws | The function may throw (the exception type is not specified; the MVP uses String or Result) |
throws T | Throws type T (typed throws Partially implemented) |
throw expr | Throws inside a throws function |
try expr | Calls a throws expression |
do { } catch { } | Block-level catching |
try? | Returns nil on error or propagates |
try! | Forced (panics on error) |
Calling:
import System
func loadFile(path: String) throws -> String {
if (!File.exists(path)) {
throw "not found: \(path)"
}
return File.read(path)
}
func main() {
let s = try loadFile(path: "/tmp/x") // Expression-level try
let s2 = try? loadFile(path: "/tmp/x") // String? (nil means error)
let s3 = try! loadFile(path: "/tmp/x") // String (panics on error)
do {
let t = loadFile(path: "/tmp/x")
print(t)
} catch (err) {
print("failed: \(err)")
}
}See Chapter 17, Error Handling.
Effect Annotations (requires)
Mark a function as "pure" (no side effects) or "IO":
import System
func pure(x: Int) -> Int requires Pure {
return x + 1
}
func load() -> String requires IO {
return File.read("data.txt")
}| Annotation | Meaning | Compile-time checking |
|---|---|---|
requires Pure | No I/O / global state dependency | Phase 50+ checks call chains (syx-meta) |
requires IO | I/O allowed | Marker only |
| No annotation | Default (equivalent to IO) | No checking |
MVP status:
syx-metais partially implemented (mentioned in Chapter 32, Operators vs Core Host). The complete effect system → later Phases. Application-level code can omit these; service layers / SDKs may use them.
Generic Functions
func id<T>(x: T) -> T { return x }
func swap<T>(a: T, b: T) -> (T, T) { return (b, a) }
let n = id(x: 42) // Int
let s = id(x: "hi") // StringGeneric parameters come after the function name; a where constraint may be added:
func eq<T>(a: T, b: T) -> Bool where T: Equatable {
return a == b
}See Chapter 24, Generics and Language Reference: Generic Parameters and Arguments.
Operator Functions
Custom operators (GA):
infix operator |> { precedence: 50, associativity: left }
func |> <T>(value: T, f: (T) -> T) -> T { return f(value) }
let r = 5 |> { $0 * 2 } // 10See Chapter 30, Advanced Operators.
Functions as Values (First Class)
let f: (Int) -> Int = { x in x * 2 }
let r = f(3) // 6
func apply(g: (Int) -> Int, x: Int) -> Int {
return g(x)
}
print(apply(g: f, x: 4)) // 8The function type (P) -> R is first class: it may serve as a variable, parameter, return value, or Array element.
$0/$1: closure shorthand ({ $0 + $1 }), corresponding to the first / second parameters; consistent with Swift's$0. Partially implemented.
Nested Functions
func outer() -> Int {
func inner(x: Int) -> Int { return x * 2 }
return inner(x: 21) // 42
}Inner functions may capture outer let/var bindings (see Chapter 8, Closures).
Recursion
func fib(n: Int) -> Int {
if (n < 2) { return n }
return fib(n: n - 1) + fib(n: n - 2)
}SYX recursion uses the VM call stack; deep recursion triggers R014 stack overflow.
Diagnostics
| Code | Trigger | Fix |
|---|---|---|
| S001 | Duplicate function name | Rename the function |
| S004 | Parameter/return type mismatch | Fix the types |
| S005 | Accessing an internal symbol across modules | Change it to public |
| S042 | Call argument label missing, wrong, or a _ parameter carrying a label | Supply the correct label: or change the parameter to _ |
| S020 | Using await in a non-async function | Add async or remove await |
| R007 | ! unwrapping nil | Change to ? or if let |
| R014 | Recursion too deep | Switch to iteration or increase the stack size |
Differences from Swift
| Swift | SYX |
|---|---|
_ to drop argument labels | Implemented (_ name); fully positional calls work only when all formal parameters are _ |
| Enforced label matching | Implemented (S042); there is no "fully positional escape" |
| Trailing default parameters may be omitted | In SYX they may be omitted in any position |
@escaping | Not implemented |
@autoclosure | Not implemented |
rethrows | Not implemented |
inout | Not implemented; borrow with &mut T (partially implemented) |
async throws | Implemented |
async let semantics | Desugars to detach+join (Swift uses structured concurrency) |
Closure shorthand $0/$1 | Partially implemented |
Generic where clauses | Implemented |
Implementation Status
- Named parameters: Implemented
- Default parameters: Implemented
async/await/async let: Implemented (multiple-await state machine GA)throws/try/try?/try!: Implementedrequires Pure/requires IO: Partially implemented (syx-metaMVP)- Operator functions: GA (three-back-end parity)
- Nested functions: Implemented
- Closure shorthand
$0: Partially implemented
Next chapter → Closures
See also → Language Reference: Declarations · Chapter 17, Error Handling · Chapter 18, Concurrency · Chapter 30, Advanced Operators