Variables

Variables are named slots for storing values. You can define a new variable in Magpie using the var keyword, like so:

var a = 1 + 2

This creates a new variable a in the current scope and initializes it with the result of the expression following the =. Like everything, a variable definition is an expression in Magpie. It evaluates to the initialized value.

print(var a = "hi") // prints "hi"

Once a variable has been defined, it can be accessed by name as you would expect.

var name = "Abraham Lincoln"
print(name) // prints "Abraham Lincoln"

Patterns

In our previous examples, the part between var and the = has always been a simple name, but what you're seeing there is actually a pattern. That means any pattern is allowed in a declaration. A simple case is what other languages call "multiple assignment".

var x, y = 1, 2
print(x + ", " + y) // 1, 2

Here, the record pattern x, y is used to destructure the record 1, 2, pulling out the fields into separate variables. More complex nested records also work.

var coloredPoint = (position: (2, 3), color: "red")
var position: (x, y), color: c = coloredPoint
print(x + ", " + y + " " + c) // 2, 3 red

Here we're destructuring a nested record in the same way. Patterns that test values are supported as well.

var good is String = "a string"
var bad is String = 123

The first line of this will execute without a problem, but the second line will throw a NoMatchError because 123 is not a string.

(One way to think of variable definitions is as match expressions with one case whose body is the rest of the current scope.)

Scope

A variable in Magpie has true block scope: it exists from the point where it is defined until the end of the block where that definition appears.

do
    print(a) // ERROR! a doesn't exist yet
    var a = 123
    print(a) // "123"
end
print(a) // ERROR! a doesn't exist anymore

All variables are lexically scoped. There is no single global scope in Magpie. Instead, each module has its own top-level scope that isn't shared with other modules.

Declaring a variable in an inner scope with the same name as an outer one is called shadowing and is not an error (although it's not something you likely intend to do much).

var a = "outer"
if true then
    var a = "inner"
    print(a) // Prints "inner".
end
print(a) // Prints "outer".

Declaring a variable with the same name in the same scope is an error.

var a = "hi"
var a = "again" // ERROR!

Assignment

After a variable has been declared, you can assign to it using =.

var a = 123
a = 234

An assignment will walk up the scope stack to find where the named variable is declared. It's an error to assign to a variable that isn't defined. Magpie doesn't roll with implicit variable definition.

Like variable definition, an assignment expression returns the assigned value.

var a = "before"
print(a = "after") // Prints "after".

You can use can also use record pattern syntax to destructure when assigning to existing variables.

var a, b = 1, 2
a, b = 3, 4
print(a + ", " + b) // Prints "3, 4".

Single-Assignment Variables

The variables we've seen so far all allow assignment. You can prevent that by defining them using val instead of var.

val a = "before"
a = "after" // ERROR!

Single-assignment variables can make code easier to understand since you don't have to hunt around and see if they ever gets reassigned. Arguments to methods, variables bound in pattern match expressions, and loop iterators are always defined as single-assignment.