Skip to content

Commit

Permalink
fewer words, easier reading
Browse files Browse the repository at this point in the history
incorporating feedback
  • Loading branch information
gnarled-cipher committed Sep 3, 2019
1 parent d190bb1 commit 4df6a07
Showing 1 changed file with 53 additions and 106 deletions.
159 changes: 53 additions & 106 deletions site/docs/skylark/language.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,13 @@ title: Starlark Language

<!-- [TOC] -->

The page is an overview of [Starlark](https://github.com/bazelbuild/starlark)
(formerly known as Skylark), the language used in Bazel. This should be
enough to get you started. Please refer to the
[Starlark repository on GitHub](https://github.com/bazelbuild/starlark/)
for more information about the language.
For a complete list of functions and types, please check the
[API reference](lib/skylark-overview.html).
The page is an overview of [Starlark](https://github.com/bazelbuild/starlark), formerly known as Skylark, the language used in Bazel. For a complete list of functions and types, check [Starlark's API reference](lib/skylark-overview.html).

## Syntax
For more information about the language, see [Starlark's GitHub repo](https://github.com/bazelbuild/starlark/).

Starlark is designed to be small, simple, and thread-safe. Although it is
inspired from Python, it is not a general-purpose language and most Python
features are not included.
## Syntax

Here is an example to show the syntax:
Starlark's syntax is inspired by Python3. This is valid syntax in Starlark:

```python
def fizz_buzz(n):
Expand All @@ -37,142 +29,97 @@ def fizz_buzz(n):
fizz_buzz(20)
```

The following basic types are supported: [None](lib/globals.html#None),
[bool](lib/bool.html), [dict](lib/dict.html), function, [int](lib/int.html),
[list](lib/list.html), [string](lib/string.html). On top of that, two new
types are specific to Bazel: [depset](lib/depset.html) and
[struct](lib/struct.html).

Starlark is syntactically a subset of Python 3, and will remain so through at
least the 1.x release lifecycle. This ensures that Python-based tooling can at
least parse Starlark code. Although Starlark is not *semantically* a subset of
Python, behavioral differences are rare (excluding cases where Starlark raises
an error).
Starlark's semantics can differ from Python, and behavioral differences are rare, except for cases where Starlark raises an error. The following Python types are supported:

* [None](lib/globals.html#None)
* [bool](lib/bool.html)
* [dict](lib/dict.html)
* function
* [int](lib/int.html)
* [list](lib/list.html)
* [string](lib/string.html)

## Mutability

Because evaluation of BUILD and .bzl files is performed in parallel, there are
some restrictions in order to guarantee thread-safety and determinism. Two
mutable data structures are available: [lists](lib/list.html) and
[dicts](lib/dict.html).
Starlark favors immutability. Two mutable data structures are available: [lists](lib/list.html) and [dicts](lib/dict.html). Changes to mutable data-structures, such as appending a value to a list or deleting an entry in a dictionary are valid only for objects created in the current context. After a context finishes, its values become immutable.

In a build, there are many "evaluation contexts": each `.bzl` file and each
`BUILD` file is loaded in a different context. Each rule is also analyzed in a
separate context. We allow side-effects (e.g. appending a value to a list or
deleting an entry in a dictionary) only on objects created during the current
evaluation context. Once the code in that context is done executing, all of its
values are frozen.
This is because Bazel builds use parallel execution. During a build, each `.bzl` file and each `BUILD` file get their own execution context. Each rule is also analyzed in its own context.

For example, here is the content of the file `foo.bzl`:
Let's go through an example with the file `foo.bzl`:

```python
var = []
# `foo.bzl`
var = [] # declare a list

def fct():
var.append(5)
def fct(): # declare a function
var.append(5) # append a value to the list

fct()
fct() # execute the fct function
```

The variable `var` is created when `foo.bzl` is loaded. `fct()` is called during
the same context, so it is safe. At the end of the evaluation, the environment
contains an entry mapping the identifier `var` to a list `[5]`; this list is
then frozen.
Bazel creates `var` when `foo.bzl` loads. `var` is thus part of `foo.bzl`'s context. When `fct()` runs, it does so within the context of `foo.bzl`. After evaluation for `foo.bzl` completes, the environment contains an immutable entry, `var`, with the value `[5]`.

It is possible for multiple other files to load symbols from `foo.bzl` at the
same time. For this reason, the following code is not legal:
When another, `bar.bzl` loads symbols from `foo.bzl`, loaded values remain immutable. For this reason, the following code in `bar.bzl` is illegal:

```python
load(":foo.bzl", "var", "fct")
# `bar.bzl`
load(":foo.bzl", "var", "fct") # loads `var`, and `fct` from `./foo.bzl`

var.append(6) # runtime error, the list stored in var is frozen

fct() # runtime error, fct() attempts to modify a frozen list
```

Evaluation contexts are also created for the analysis of each custom rule. This
means that any values that are returned from the rule's analysis are frozen.
Note that by the time a custom rule's analysis begins, the .bzl file in which
it is defined has already been loaded, and so the global variables are already
frozen.

Just like the above example using `bzl` files, values returned by rules are immutable too.

## Differences between BUILD and .bzl files

The goal of a BUILD file is to register targets, by making calls to rules. On
the other hand, loading a .bzl file doesn't have any side effect: .bzl files are
only providing definitions (constants or functions).
`BUILD` files register targets via making calls to rules. `.bzl` files provide definitions for constants, rules, macros, and functions.

[Some functions](../be/functions.html) and [native rules](
../be/overview.html#language-specific-native-rules) are exposed to BUILD files
as global symbols. In bzl files, they are available under the [`native` module](
[Native functions](../be/functions.html) and [native rules](
../be/overview.html#language-specific-native-rules) are global symbols in `BUILD` files. `bzl` files need to load them using the [`native` module](
https://docs.bazel.build/versions/master/skylark/lib/native.html).

There are two syntactic restrictions in BUILD files:

* Function definitions (`def` statements) are not allowed in BUILD files.

* `*args` and `**kwargs` arguments are not allowed in BUILD files; instead list
all the arguments explicitly.

There are two syntactic restrictions in `BUILD` files: 1) declaring functions is illegal, and 2) `*args` and `**kwargs` arguments are not allowed.

## Differences with Python

In addition to the mutability restrictions, there are also differences with
Python:

* Global variables cannot be reassigned.
* Global variables are immutable.

* `for` statements are not allowed at the top-level; factor them into functions
instead. In BUILD files, you may use list comprehensions.
* `for` statements are not allowed at the top-level. Use them within functions instead. In BUILD files, you may use list comprehensions.

* `if` statements are not allowed at the top-level. However, `if` expressions
can be used: `first = data[0] if len(data) > 0 else None`.
* `if` statements are not allowed at the top-level. However, `if` expressions can be used: `first = data[0] if len(data) > 0 else None`.

* Dictionaries have a deterministic order of iteration.
* Deterministic order for iterating through Dictionaries.

* Recursion is not allowed.

* Int type is limited to 32-bit signed integers (an overflow will throw an
error).
* Int type is limited to 32-bit signed integers. Overflows will throw an error.

* Modifying a collection during iteration is an error. You can avoid the error
by iterating over a copy of the collection, e.g.
`for x in list(my_list): ...`. You can still modify its deep contents
regardless.
* Modifying a collection during iteration is an error.

* The comparison operators (`<`, `<=`, `>=`, `>`) are not defined across
different types of values, e.g., you can't compare `5 < 'foo'` (however you
still can compare them using `==` or `!=`). This is a difference with Python
2, but consistent with Python 3. Note that this means you are unable to sort
lists that contain mixed types of values.
* Except for equality tests, comparison operators `<`, `<=`, `>=`, `>`, etc. are not defined across value types. In short: `5 < 'foo'` will throw an error and `5 == "5"`
will return false.

* Tuple syntax is more restrictive. You may use a trailing comma only when the
tuple is between parentheses, e.g. write `(1,)` instead of `1,`.
* In tuples, a trailing comma is valid only when the tuple is between parentheses, e.g. write `(1,)` instead of `1,`.

* Dictionary literals cannot have duplicated keys. For example, this is an
error: `{"a": 4, "b": 7, "a": 1}`.
* Dictionary literals cannot have duplicated keys. For example, this is an error: `{"a": 4, "b": 7, "a": 1}`.

* Variable of a comprehension may not be used after the comprehension. This is
stricter than Python 2 and Python 3, which have different behavior (shadowing
vs reassignment).
* The variable used in a comprehension may not be used after the comprehension. This is stricter than Python 2 and Python 3, which have different behavior (shadowing vs reassignment).

* Strings are represented with double-quotes (e.g. when you
call [repr](lib/globals.html#repr)).
* Strings are represented with double-quotes (e.g. when you call [repr](lib/globals.html#repr)).

The following Python features are not supported:

* implicit string concatenation (use explicit `+` operator)
* Chained comparisons (e.g. `1 < x < 5`)
* `class` (see [`struct`](lib/struct.html#struct) function)
* `import` (see [`load`](concepts.md#loading-an-extension) statement)
* `while`, `yield`
* float and set types
* generators and generator expressions
* `lambda` and nested functions
* `is` (use `==` instead)
* `try`, `raise`, `except`, `finally` (see [`fail`](lib/globals.html#fail) for
fatal errors)
* `global`, `nonlocal`
* most builtin functions, most methods
* implicit string concatenation (use explicit `+` operator).
* Chained comparisons (e.g. `1 < x < 5`).
* `class` (see [`struct`](lib/struct.html#struct) function).
* `import` (see [`load`](concepts.md#loading-an-extension) statement).
* `while`, `yield`.
* float and set types.
* generators and generator expressions.
* `lambda` and nested functions.
* `is` (use `==` instead).
* `try`, `raise`, `except`, `finally` (see [`fail`](lib/globals.html#fail) for fatal errors).
* `global`, `nonlocal`.
* most builtin functions, most methods.

0 comments on commit 4df6a07

Please sign in to comment.