Skip to content

Flexible Parameters

Declaring a default value for a parameter makes that argument optional at the call site. The value is evaluated on each call that omits the argument, so dynamic expressions work correctly:

Single parameter with default, multiple defaults, and a method with a default in impl.

03-default-params.zolo
Playground
// Feature: Parameters with default values
// Syntax: `fn f(p: T = expr)` — caller may omit them.
// When to use: ergonomic API with optional config, avoid
// overloads and intermediate constructors.

// One default parameter at the end.
fn greet(name: str = "World") -> str {
  return "Hello, {name}!"
}

print(greet())  // Hello, World!
print(greet("Zolo"))  // Hello, Zolo!

// Multiple defaults — all positional.
fn connect(host: str = "localhost", port: int = 8080) -> str {
  return "{host}:{port}"
}

print(connect())  // localhost:8080
print(connect("api.example.com"))  // api.example.com:8080
print(connect("api.example.com", 443))  // api.example.com:443

// A default may use an expression — it is evaluated on EVERY
// call that omits the argument.
fn label(prefix: str, n: int = 0) -> str {
  return "{prefix}-{n}"
}

print(label("item"))  // item-0
print(label("item", 7))  // item-7

// Methods accept defaults too.
struct Counter {
  value: int,
}

impl Counter {
  fn new() -> Counter {
    return Counter { value: 0 }
  }

  fn add(self, n: int = 1) -> int {
    self.value = self.value + n
    return self.value
  }
}

let c = Counter::new()
print(c.add())  // 1
print(c.add())  // 2
print(c.add(10))  // 12

Varargs (args: ...T) allow receiving any number of arguments of the same type. Inside the function, the parameter behaves like a normal array — iterable with for or indexable:

sum, log with fixed level + variable parts, max_of and join_csv.

04-varargs.zolo
Playground
// Feature: Varargs (variadic parameters)

// Syntax: `fn f(args: ...T)` — `...` before the type.

// When to use: accept N arguments of the same type, in

// `printf` or `Array.of` style — without wrapping in a literal.


use std::Array

// Sum N integers.

fn sum(nums: ...int) -> int {
  var total = 0
  for n in nums {
    total += n
  }
  return total
}

print(sum())  // 0

print(sum(1))  // 1

print(sum(1, 2, 3))  // 6

print(sum(1, 2, 3, 4, 5, 6, 7))  // 28


// You can mix normal params before the vararg.

fn log(level: str, parts: ...str) {
  var buf = "[{level}] "
  for p in parts {
    buf = buf + p + " "
  }
  print(buf)
}

log("info", "user", "logged", "in")

// expected: [info] user logged in


// Inside the function, `nums` is a regular array.

fn max_of(xs: ...int) -> int {
  var hi = xs[0]
  for x in xs {
    if x > hi { hi = x }
  }
  return hi
}

print(max_of(3, 1, 4, 1, 5, 9, 2, 6))  // 9


// Varargs with strings: common in loggers and formatters.

fn join_csv(items: ...str) -> str {
  var out = ""
  var first = true
  for it in items {
    if !first { out = out + "," }
    out = out + it
    first = false
  }
  return out
}

print(join_csv("a", "b", "c"))  // a,b,c

Challenge

Combine both features: write repeat(msg: str, n: int = 3) that prints msg exactly n times. Call it without the second argument and then with 5.

enespt-br