Skip to content

Expressions & Control Flow

Handlers have a body of statements whose values are real, type-checked expressions.

Expressions

Weld supports + - * / %, comparisons, && || !, ?? (coalesce), string concatenation with +, and parenthesization — all with precedence. Integer arithmetic wraps.

weld
route GET "/add/:a/:b" (a: int, b: int) {
    respond "sum={a + b} diff={a - b} prod={a * b} quot={a / b} mod={a % b}\n"
}

route GET "/cmp/:a/:b" (a: int, b: int) {
    let bigger = a > b
    respond "a>b: {bigger}, equal: {a == b}, in_range: {a > 0 && a < 100}\n"
}

Bindings

  • let x = e — an immutable binding (optionally let x: T = e).
  • var x = e — a mutable binding; assign with x = e2 for accumulation.
weld
route GET "/greet/:name" (name: string) {
    let greeting = "Hello, " + name
    respond "{greeting}\n"
}

Conditionals

weld
route GET "/classify/:n" (n: int) {
    if n < 0 {
        respond "negative"
    } else if n == 0 {
        respond "zero"
    } else {
        respond "positive"
    }
}

Loops

for x in list { … } iterates a list; for k, v in map { … } iterates a map. Any type with a next(mut self) -> T? method is also iterable — see Methods & Iterators.

weld
route GET "/sum/:n" (n: int) {
    var total = 0
    var xs = push([1, 2, 3], n)
    for x in xs { total = total + x }
    respond "total={total}\n"
}

Two bindings over a list give a 0-based int index and the element — for i, x in xs:

weld
route GET "/names" {
    var names = ["ann", "bo", "cy"]
    var out = ""
    for i, name in names {
        out = out + "{i}: {name}\n"
    }
    respond out
}

Match

match subject { pattern => expr, _ => expr } works over enums, ints, or strings, and needs a _ arm (except when matching an exhaustive sum type). It is an expression, so it yields a value:

weld
route GET "/label/:n" (n: int) {
    let label = match n {
        0 => "zero",
        _ => "nonzero",
    }
    respond "n={label}\n"
}

List builtins

Beyond push, the list library offers:

  • slice(list, start, end) -> [T] — the sub-range [start, end), clamped to bounds.
  • pop(list) -> [T] — the list without its last element (empty stays empty).
  • concat(a, b) -> [T] — two lists of the same element type joined.
  • reverse(list) -> [T] — the list reversed.
  • contains(list, elem) -> bool — membership, for primitive/enum element types (in addition to contains(string, substring)).
weld
route GET "/tags" -> string {
    var tags = ["intro", "weld", "demo", "lists"]
    tags = concat(tags, ["extra"])       # join two lists
    tags = slice(tags, 1, 4)             # sub-range [1, 4)
    var recent = reverse(tags)           # reversed
    recent = pop(recent)                 # drop the last

    var out = ""
    for i, t in recent {                 # 0-based index + element
        out = out + "{i}:{t} "
    }
    respond "has-weld={contains(tags, "weld")} list={out}"
}

An empty list literal takes its element type from context, and that inference now flows through ??optionalList ?? [] gives the [] the element type of the optional:

weld
route GET "/merge" (query extra: string?) -> string {
    var base: [string]? = null
    var items = base ?? []               # [] is inferred as [string]
    items = push(items, extra ?? "none")
    respond "count={len(items)}"
}

Functions & builtins

weld
fn greet(name: string) -> string {
    return "Hello, " + upper(name)
}

route GET "/hi/:name" (name: string) {
    respond "{greet(name)}\n"
}

The builtin library covers strings (len, upper, lower, trim, trimPrefix, contains, startsWith, endsWith, parseInt), conversion (str, int, hash), collections (push, filled, has, remove), environment and time (env, now_ms), request context (request_header, client_ip), and JWT (jwt_verify, jwt_decode).

For the complete list — every builtin function, statement, and global, with exact signatures — see the Builtins & Globals reference. For the full grammar, keywords, and operator precedence, see the Language Reference.