From Python
A Python-to-Vela migration guide for Stage-0 syntax and common idioms.
Vela is Python-adjacent: comments use #, blocks use significant indentation, and scripts run top to bottom.
The important differences in Stage-0 are immutability by default, explicit boolean conditions, and Option /
Result values instead of None sentinels or exceptions.
1. Variables and bindings
Python rebinds names freely. Vela requires mut for same-scope reassignment.
mut x = 10
x = x + 1
name = "Ada"
# name = "Grace" # error: immutable binding
Inner scopes may shadow immutable bindings. Reassigning an outer mutable binding updates the outer binding.
2. Collections
Lists are immutable values. push returns a new list. Use array() or array_from when mutation is the point.
xs = [1, 2, 3]
ys = push(xs, 4)
print(xs[0], xs[-1], xs.len)
a = array()
array_push(a, 10)
array_set(a, 0, 99)
print(to_list(a))
Records cover fixed fields and immutable string-keyed maps. Mutable dictionaries use dict().
person = { name: "Ada", age: 36 }
print(person.name)
mut counts = {}
counts = set(counts, "red", get_or(counts, "red", 0) + 1)
d = dict()
dict_set(d, "red", 1)
dict_set(d, "red", dict_get_or(d, "red", 0) + 1)
3. Comprehensions become functions or pipelines
Use map, filter, reduce, and |>.
squares = map([1, 2, 3, 4], x => x * x)
evens = filter([1, 2, 3, 4], x => x % 2 == 0)
result = [1, 2, 3, 4] |> filter(x => x % 2 == 0) |> map(x => x * x)
4. No truthiness
Conditions must be Bool. Use explicit checks.
xs = [1, 2, 3]
if xs.len > 0:
print("non-empty")
5. Exceptions become Result values
Stage-0 supports Ok, Err, and postfix ? propagation.
fn checked_div(a, b):
if b == 0:
return Err("divide by zero")
return Ok(a / b)
fn ratio(a, b, c):
x = checked_div(a, b)?
y = checked_div(x, c)?
return Ok(y)
6. Classes become records, functions, or tagged unions
Use records for plain data and type plus match for variant shapes.
type Shape:
Circle(r)
Rect(w, h)
fn area(shape):
match shape:
Circle(r) => 3.14 * r * r
Rect(w, h) => w * h