DisplayScript

Bindings and Scope

Bindings

A binding is a named value. Create a binding using var.

var speed = 9
var time = 0.4

After a binding is created, you can use it by name in expressions.

var distance = speed * time

To update an existing binding to a new value, leave out the var and assign to it using =.

var y = 0
var height = 17
draw at(0, y, @width, height, fill(red))
y = y + height
draw at(0, y, @width, height, fill(black))

Scope

You can only use bindings that are in scope. Once a list of statements ends with a closing brace "}", all the bindings created by those statements are out of scope and no longer visible. In this example, the backgroundColor binding in the if statement is out of scope by the time we try to pass it to fill:

var controlState = #default
if controlState == #highlighted {
    var backgroundColor = red
}
// This doesn't work because backgroundColor is out of scope.
draw fill(backgroundColor)

Instead of creating a new backgroundColor binding that will go out of scope, we can create the binding earlier and assign our new value into it.

var controlState = #default
var backgroundColor = white
if controlState == #highlighted {
    backgroundColor = red
}
// Now backgroundColor is still in scope.
draw fill(backgroundColor)

If two bindings have the same name, the most recent binding that's in scope will be used:

var x = 1
if true {
    // This is a new binding.
    var x = 2
    show(x) // 2
    x = 3
    show(x) // 3
}
show(x) // 1

Note that the inner and outer x are two different bindings. When the inner x goes out of scope, the outer x (still with a value of 1) becomes visible again.

Recursive Bindings

This self-referential binding doesn't make sense: you can't use x to define itself.

var x = x + 1

But sometimes you want to define a function which refers to itself:

// triangular can't be used since it's currently being defined.
var triangular = function (x) {
    if x > 0 {
        return x + triangular(x - 1)
    }
    return 0
}
show(triangular(10))

function statements create recursive bindings, where the binding can be used in its own definition:

function triangular(x) {
    if x > 0 {
        return x + triangular(x - 1)
    }
    return 0
}
show(triangular(10))

drawable statements also create recursive bindings:

drawable rects {
    draw fill(black)
    draw inset(1, fill(white))
    if @width > 20 {
        draw inset(10, rects)
    }
}
draw rects

You can assign to a recursive binding just like a normal binding:

function f() { return 1 }
f = function () { return 2 }
show(f())

Parameter Bindings

Functions create bindings for their parameters inside the function body. You can assign to them just like any other binding:

function f(x) {
    x = x + 1
    return x * 2
}
show(f(7))

Captured Bindings

Both functions and drawables capture the values of bindings from the scope where they are created. You can use captured bindings in expressions, but you can't assign to them:

var fillColor = black
function assignFillColor() {
    // Using the captured variable works...
    show(fillColor)
    // ...but assigning to it doesn't.
    fillColor = red
}
assignFillColor()
draw fill(fillColor)

Once a binding is captured, assigning to it doesn't change the captured value:

var x = 1
function f() {
    return x
}
x = x + 1
// Shows "2", the current value for x.
show(x)
// Shows "1", the value for x captured by f.
show(f())

There are two more kinds of bindings: dynamic bindings and state variables, which you can read about on their respective pages.