---
title: Evaluation and Purity
description: How bindings are evaluated, dependency tracking, and pure contexts.
---

## Dependency Tracking

A [binding](../bindings/) is evaluated lazily: its expression runs when the property's value is queried, not when the binding is set.

While a binding's expression is evaluated, every property it reads is registered as a dependency of that binding.
When any of those properties changes, the binding is marked dirty.
The next query of the property re-evaluates the expression and rebuilds its dependency set from the properties read during that evaluation.
A binding whose expression reads no properties is never marked dirty and is evaluated at most once.

The dependency set is rebuilt on each evaluation, so it always reflects the properties read on the most recent run.
A property read on a path that a later evaluation does not take is not a dependency of that evaluation.

## Overwritten Bindings

A property holds at most one binding.
Setting a new binding on a property replaces any previous one; the last binding wins.

A binding is active only as long as the property is assigned with a binding expression (`x: other.value`) or a [two-way binding](../two-way-bindings/) (`<=>`).
An imperative [assignment](../statements/#assignment) (`foo.bar = 42;`) removes the binding:
from that point on the property no longer reacts to the values it was bound to.
The property can still be updated through further assignments, but its automatic reactivity is lost.

An assignment intercepted by a two-way binding is the exception: it writes through the link and leaves the binding in place.

This also applies to most built-in `in-out` properties, such as the `text` property of a `TextInput`:
user interaction writes the property like an imperative assignment and removes an existing binding to it.

To keep reactivity in such cases, use a two-way binding,
or track updates with a [change callback](../callbacks/#change-callbacks).

## Purity

Evaluating a property binding must not change any observable state other than the property itself.
An expression with this quality is *pure*; otherwise it has side effects.
Because bindings are evaluated lazily, the order of side effects and whether they happen at all are not defined, so bindings must be pure.

The compiler enforces that code in a *pure context* is free of side effects.
The pure contexts are:

- Property binding expressions.
- Model expressions of a [`for` element](../repetition-and-conditional-elements/).
- The body of a function or callback declared `pure`.

In a pure context, the following are compile errors:

- An assignment or self-assignment (`=`, `+=`, `-=`, `*=`, `/=`) to a property.
- A call to a callback that is not `pure`.
- A call to a function that is not pure.
- A call to a built-in function that has side effects.

The `pure` keyword annotates [callbacks](../callbacks/) and [functions](../functions/)
so they may be called from a pure context.
A `pure` callback's handler and a `pure` function's body are themselves pure contexts, and are checked accordingly.

```slint
export component Example {
    pure callback foo() -> int;
    public pure function bar(x: int) -> int {
        return x + foo();
    }
}
```

The purity of a private function is inferred: the compiler treats it as pure exactly when its body contains no side effects.
A private function may still be declared `pure` to have the compiler reject a body that has side effects.

A function that implements a `pure` function from an interface must itself be pure; declaring it impure is a compile error.
The reverse is allowed: a pure implementation may satisfy an impure declaration.

Purity must match across a [callback alias](../callbacks/#aliases) or a callback [two-way binding](../two-way-bindings/):
both sides must be `pure` or both impure, otherwise it is a compile error.
