Home Documentation Templates Examples Showcase GitHub
Theme

Core language · State & bindings

Declarations & assignments.

Nift v4.1 introduced explicit lexical declarations and assignment. Use := to create a binding and = to update an existing mutable binding; the type inferred at declaration remains stable.

:= declares; = assigns.

They are deliberately different operations. Assignment never creates a missing name, which catches typos and makes the lifetime of state visible in the source.

Declare and assign

$[count := 0]
$[count = count + 1]
$[price := 19.95]
$[title := "Nift"]
$[published := true]

A top-level declaration or assignment inside $[...] changes state and renders no text. Reading the binding later renders its value normally:

$[visits := 1]
$[visits = visits + 1]
<p>Visits: $[visits]</p>

Stable inferred types

The declaration establishes the binding's type. Reassignment must preserve it.

$[count := 0]
$[count = 12]          
$[count = "twelve"]    

Numeric literal typing is lexical. An integer spelling (0, 8) infers int; a fractional or exponent spelling (0.0, 8.0, 8.5, 1e3) infers double, even when the value is integral. The inferred type then stays stable.

$[n := 0]
$[n = 8]
$[f := 0.0]
$[f = 8.5]
$[f = 3]

The .0 spelling makes 0.0 a floating-point literal, so f is double: reassigning 8.5 is valid while reassigning the integer 3 is an error.

An arithmetic expression is double when any operand is double, so a double accumulator remains double even when it lands on an integral value:

$[total := 0.0]
$[total = total + 8.5]
$[total = total + 9.0]

Arrays, objects and struct instances likewise retain their declared kind. Struct fields have the same stable inferred-type rule.

Lexical scope

Nested constructs run in child scopes. They can read and update an existing outer mutable binding, while a declaration made inside the child disappears when that child exits.

$[visible := 0]

@if(show) {
    $[visible = visible + 1]
    $[temporary := "inside only"]
}

$[visible]      

const and immut

$[const site_name := "Nift"]
$[immut config := {"features":["docs","builds"]}]

const prevents rebinding. immut establishes a recursively read-only binding/view contract. An immut view does not make separately reachable mutable storage globally immutable.

Arrays (lists)

Nift calls list-like values arrays, following JSON terminology. JSON arrays existed as data values before v4.1; v4.1 made array and object literals directly declarable in the expression language.

$[names := ["Ada", "Grace", "Linus"]]
$[matrix := [[1, 2], [3, 4]]]
$[users := [{"name":"Ada"}, {"name":"Grace"}]]

Use zero-based indexing to read an item and @for to iterate:

<p>First: $[names[0]]</p>

<ul>
@for(name : names) {
    <li>$[name]</li>
}
</ul>

Nift does not currently expose in-place list methods such as push, pop, append or remove. Collection operations are value-producing transforms: filter, map, sort, slice, reverse and distinct return new arrays rather than mutating their source. If you need a different literal array in mutable state, assign another array of the same type; for data transformation, use the collection operations page.

$[names := ["Ada", "Grace"]]
$[names = ["Ada", "Grace", "Linus"]]

This distinction is intentional: lexical bindings can be mutable while Nift's collection transforms remain predictable and non-mutating.

Objects

$[site := {"title":"Example", "draft":false}]
$[sites := [{"title":"One"}, {"title":"Two"}]]

$[site.title]
$[sites[1].title]

Objects and arrays can be nested freely. Ordinary JSON object/array values are not structs: use structs when you need fixed-shape state with methods, privacy and reference semantics.

Multiline declarations

@:=(config){
    {
        "title": "Example",
        "navigation": ["Home", "Docs", "About"]
    }
}

@:=(name){...} is the multiline spelling of $[name := expression]. The outer braces frame one expression; they are not part of the value.

Chained declaration and assignment

:= and = are right-associative, so related state can be initialized or updated in one expression when that improves clarity.

$[a := b := 0]
$[a = b = 5]

Prefer separate lines when the chain would make scope or intent harder to read.