Lesson 2 of 25 · Getting started

Variables and constants

Variables are names for values. In Salam a variable cannot be changed unless you say so, which heads off a whole class of quiet bugs.

Declaring a variable

:= introduces a new name and gives it a value. The type comes from the value, so you do not have to write it: "Maryam" makes a str and 85 makes an int. From then on the type is fixed, and putting text into a number variable later is an error. Names can be several words, such as book price.

variables.salam
const MAX := 100

func main:
    name := "Maryam"
    mut score := 85
    score = score + 10
    println name, "scored", score, "out of", MAX
end
OutputMaryam scored 95 out of 100

A variable always starts with a value. There is no way to declare a name now and fill it in later, so you never read a variable that holds leftover garbage.

Changeable variables

A variable declared with := never changes again. If you want to give it a new value later, write mut before the name and then assign with =. The shorthands +=, -= and ++ are there too.

var-mut.salam
func main:
    mut counter := 0
    counter = counter + 1
    counter += 5
    counter++
    println "counter:", counter
end
Outputcounter: 7

Salam is strict about this: if you declare something mut and never change it, or declare a variable and never use it, the program does not compile. The rule catches dead code and typos.

Where a variable lives

A variable exists from the line that declares it to the end of the block it was declared in. A name made inside an if is gone once the if closes, and using it afterwards is an "unknown identifier" error. Keep a variable in the smallest block that needs it.

var-scope.salam
func main:
    subtotal := 140
    if subtotal >= 100:
        points := subtotal / 20
        println "points earned:", points
    end
    label := "order"
    if subtotal > 120:
        label := "large order"
        println "inside the block:", label
    end
    println "after the block:", label
end
Outputpoints earned: 7 inside the block: large order after the block: order

The second if declares its own label. That is allowed in an inner block, and it hides the outer one until the block ends; the outer label is never touched, as the last line shows. This is called shadowing, and it is easy to misread, so use a new name unless you really mean it. Declaring the same name twice in the same block is an error.

Choosing a value without mut

A common reason to reach for mut is a value that depends on a condition: declare it empty, then set it in each branch of an if. Most of the time the ternary operator does the same job in one line and the variable can stay unchangeable:

var-choose.salam
func main:
    weight_kg := 3
    express := true
    shipping := weight_kg > 2 ? 15 : 8
    speed := express ? "express" : "standard"
    println speed, "shipping costs", shipping
end
Outputexpress shipping costs 15

With more than two cases, a match expression plays the same role. Fewer mut variables means fewer places where a value can change behind your back.

Choosing the type

When you want a type other than the default, convert the value with as. A whole number is int (32 bits) by default and a decimal one is f64.

var-typed.salam
func main:
    age := 17 as i64
    height := 1.75
    name := "Negar"
    student := true
    println name, age, height, student
end
OutputNegar 17 1.75 true

Older Salam code wrote the type before the value, as in age: int = 17. That form was removed and is now a parse error that tells you to write age := 17, or age := 17 as i64 when the type matters. The name: Type form lives on only for function parameters and struct fields.

Global variables

Outside any function you can declare global variables with mut and constants with const; every function in the program can reach them. Here a global counts how many times a function has been called:

var-global.salam
const RATE := 10

mut calls := 0

func tax(price: int): int:
    calls += 1
    ret price * RATE / 100
end

func main:
    println tax(2000)
    println tax(500)
    println "calls:", calls
end
Output200 50 calls: 2

Globals go near the top of the file: after the import lines and the constants, and before any type or function. Use them sparingly: a value passed in as a parameter is easier to follow than one that any function might change.

Constants

const declares a value that is fixed when the program is compiled. A constant can be computed from numbers and from other constants, so you can name the parts of a calculation instead of writing a bare 604800. Constants also work inside a function, where they are visible only in that function.

var-const.salam
const MINUTE := 60
const HOUR := 60 * MINUTE
const DAY := 24 * HOUR
const APP := "backup"

func main:
    const RETRIES := 3
    println APP, "keeps files for", 7 * DAY, "seconds"
    println "a failed upload is tried", RETRIES, "times"
end
Outputbackup keeps files for 604800 seconds a failed upload is tried 3 times

The name of a constant has to be a single word: MAX VALUE is a parse error, so write MAX_VALUE or MAXVALUE. Global variables and ordinary variables may still have names of several words.

There is a difference between := and const that matters once a variable holds several values, such as an array. := fixes the name: it cannot be pointed at a whole new value, but the elements inside can still be changed. const freezes everything, elements included.

var-contents.salam
const DAYS := ["Sat", "Sun", "Mon", "Tue", "Wed"]

func main:
    readings := [21, 23, 91, 24, 22]
    readings[2] = 19
    println readings
    println "reading for", DAYS[2], "fixed to", readings[2]
end
Output[21, 23, 19, 24, 22] reading for Mon fixed to 19

Here a wrong reading is corrected in place even though readings is not mut. Writing DAYS[2] = "Fri" instead is an error, because a const is fully immutable.

Naming rules

  • Names are made of English or Persian letters, digits and _, and may not start with a digit.
  • Upper and lower case are different: name and Name are two variables.
  • Keywords, such as if, and, to or in, cannot be used as names, and not as one word of a longer name either: max to go is rejected.
  • A variable cannot share its name with a function in the same file, so a name always means one thing.
  • Starting a name with _ silences the unused variable error. The compiler advises against it for dead code, since the code stays in the program; keep it for values you really mean to ignore.

These are the errors you will meet most often while getting used to variables:

CodeErrorWhat it means
x := 5 then x = 6E013cannot reassign immutable variable 'x'; add mut
mut x := 5, never changedE069declared 'mut' but never mutated; drop the mut
x := 5, never usedE059unused variable 'x'
x := 5 twice in one blockE001redefinition of 'x'
mut total := 0 then total = "ten"E002cannot assign 'str' to 'i32'
total := 5 next to func totalE090'total' is already the name of a function in this file