Declaring a variable
A variable is declared with a name, a colon, and a type. You can give it a value in the same line.
name: String = "John"
age: Int32 = 30
pi: Float32 = 3.14
ok: Bool = True
Or without a value. The variable then starts at zero (0, 0.0, or false). See Types.
count: Int32
You can't declare a variable without a type. The compiler needs to know what it is.
Literal values adapt to the variable
A plain integer literal is an Int32. But when you assign it to a variable of a different integer type, the literal is retyped to match.
small: Int8 = 5 # fine, the literal becomes Int8
This works for any integer literal, and for arithmetic on integer literals:
pair: Int8 = 2 * 3 # the whole expression becomes Int8
It does not apply to non-literal values. If a function returns an Int32, you can't stuff it into an Int8 variable without a cast.
Changing a variable
You can overwrite a declared variable with plain =. The new value must have the same type (integer literals adapt, just like at declaration):
def main() -> Int32
x: Int32 = 5
print(x)
x = 3
print(x)
x = x + 1
print(x)
return 0
Assigning before the variable exists is a compile error, and so is assigning a value of the wrong type:
y = 3 # Error: Unknown variable 'y'
x: Int32 = 5
x = "hi" # Error: Variable 'x' expects type Int32, got String
Threadon also gives you the update operators, which combine = with an arithmetic operator:
| Operator | Means |
|---|---|
+= | add to the current value |
-= | subtract from the current value |
*= | multiply the current value |
/= | divide the current value |
//= | floor-divide the current value |
%= | take the remainder |
So the counter example looks like this:
def main() -> Int32
x: Int32 = 5
x += 3
print(x)
x *= 2
print(x)
return 0
If you really need to set a new value outright, the pattern that works is to redeclare the name inside a fresh block, in every branch. It's clunky. It's a known limitation. It's on the Limits page.
One thing to remember: x += 3 needs x to already exist. You can't declare and add in one line.
Struct fields
You can write a struct field with this method
p: Point = Point(x=0, y=0)
p.x = 3 # fine
p.y = p.x + 1
So when you need to overwrite a value, it's often easiest to keep it in a struct. See Structs.
Scope
Variables live in their function. A variable declared inside a function is visible from its declaration onward, inside that function. It is not visible in other functions.
Inside a function you can have nested blocks (from if, elif, else). Here's the rule that matters:
This works:
def fee(n: Int32) -> Int32
if n >= 18:
price: Int32 = 20
else:
price: Int32 = 10
return price
def main() -> Int32
print(fee(15))
print(fee(21))
return 0
If any branch is missing its price, you get Unknown variable or type 'price' at the return line. The compiler merges the branch values together, but only for names declared in all of them.
You can't declare the same name twice in the same block:
x: Int32 = 1
x: Int32 = 2 # compile error: already declared
Practical tips
- Use
+=and friends to update a counter or an accumulator. - Use the
if-branch pattern (declare in every branch) when a value depends on a condition. - Keep state you need to overwrite freely in a struct. Field assignment with
=works. - Give a variable its value at declaration time. It makes the zero-initialization edge case irrelevant.