After a Starlark file is parsed, but before its execution begins, the
Starlark interpreter checks statically that the program is well formed.
continue statements may appear only within
a loop; a
return statement may appear only within a
load statements may appear only outside any function.
Name resolution is the static checking process that resolves names to variable bindings. During execution, names refer to variables. Statically, names denote places in the code where variables are created; these places are called bindings. A name may denote different bindings at different places in the program. The region of text in which a particular name refers to the same binding is called that binding’s scope.
Four Starlark constructs bind names, as illustrated in the example below:
load statements (
def statements (
function parameters (
and assignments (
h, including the augmented assignment
e += 1).
Variables may be assigned or re-assigned explicitly (
h), or implicitly, as
f) or comprehension (
load("lib.star", "a", b="B") def c(d): e = 0 for f in d: print([True for g in f]) e += 1 h = [2*i for i in a]
The environment of a Starlark program is structured as a tree of lexical blocks, each of which may contain name bindings. The tree of blocks is parallel to the syntax tree. Blocks are of five kinds.
At the root of the tree is the predeclared block,
which binds several names implicitly.
The set of predeclared names includes the universal
various built-in functions such as
these functions are immutable and stateless.
An application may pre-declare additional names
to provide domain-specific functions to that file, for example.
These additional functions may have side effects on the application.
Starlark programs cannot change the set of predeclared bindings
or assign new values to them.
Nested beneath the predeclared block is the module block,
which contains the bindings of the current module.
Bindings in the module block (such as
h in the
example) are called global and may be visible to other modules.
The module block is empty at the start of the file
and is populated by top-level binding statements.
Nested beneath the module block is the file block,
which contains bindings local to the current file.
Names in this block (such as
b in the example)
are bound only by
The sets of names bound in the file block and in the module block do not overlap:
it is an error for a load statement to bind the name of a global,
or for a top-level statement to assign to a name bound by a load statement.
A file block contains a function block for each top-level
function, and a comprehension block for each top-level comprehension.
Bindings in either of these kinds of block,
and in the file block itself, are called local.
(In the example, the bindings for
i are all local.)
Additional functions and comprehensions, and their blocks, may be
nested in any order, to any depth.
If name is bound anywhere within a block, all uses of the name within
the block are treated as references to that binding,
even if the use appears before the binding.
This is true even at the top level, unlike Python.
The binding of
y on the last line of the example below makes
local to the function
hello, so the use of
y in the print
statement also refers to the local
y, even though it appears
y = "goodbye" def hello(): for x in (1, 2): if x == 2: print(y) # prints "hello" if x == 1: y = "hello"
It is a dynamic error to evaluate a reference to a local variable before it has been bound:
def f(): print(x) # dynamic error: local variable x referenced before assignment x = "hello"
The same is true for global variables:
print(x) # dynamic error: global variable x referenced before assignment x = "hello"
It is a static error to bind a global variable already explicitly bound in the file:
x = 1 x = 2 # static error: cannot reassign global x declared on line 1
If a name was pre-bound by the application, the Starlark program may explicitly bind it, but only once.
An augmented assignment statement such as
x += y is considered both a
x and a binding use of
x, so it may not be used at
The Go implementation of Starlark permits augmented assignments to appear
at top level if the
-globalreassign flag is enabled.
A function may refer to variables defined in an enclosing function.
In this example, the inner function
f refers to a variable
that is local to the outer function
x is a free variable of
The function value (
f) created by a
def statement holds a
reference to each of its free variables so it may use
them even after the enclosing function has returned.
def squarer(): x =  def f(): x += 1 return x*x return f sq = squarer() print(sq(), sq(), sq(), sq()) # "1 4 9 16"
An inner function cannot assign to a variable bound in an enclosing
function, because the assignment would bind the variable in the
In the example below, the
x += 1 statement binds
hiding the outer
Execution fails because the inner
x has not been assigned before the
attempt to increment it.
def squarer(): x = 0 def f(): x += 1 # dynamic error: local variable x referenced before assignment return x*x return f sq = squarer()
(Starlark has no equivalent of Python’s
declarations, but as the first version of
squarer showed, this
omission can be worked around by using a list of a single element.)
A name appearing after a dot, such as
get_filename().split('/'), is not resolved statically.
The dot expression
.split is a dynamic operation
on the value returned by