Core language · Reusable behaviour & rendering
Functions & fragments.
Functions run Nift's compact statement grammar and optionally return a value. Fragments remain template grammar for reusable rendered output. Both are lexically scoped callables.
Functions
Define a function with @fn(name(args)){...}. Inside its body, declarations, assignments, calls and control flow are statements: they do not need $[...] or template-directive @ prefixes.
@fn(score_class(score)) {
if(score >= 90) { return "excellent" }
if(score >= 75) { return "good" }
if(score >= 50) { return "pass" }
return "fail"
}
$[score_class(result.score)] Semicolons are optional, so compact forms such as if(x) { y = 5; return y } are valid.
State, loops and early control flow
@fn(count_visible(items)) {
count := 0
for(item : items) {
if(!item.visible) { continue }
count = count + 1
}
return count
} @fn(find_featured(items)) {
for(item : items) {
if(!item.visible) { continue }
if(item.featured) { return item }
}
return null
} break exits the innermost loop, continue advances it, and return exits the current function. Loop control cannot escape a callable and affect a caller's loop.
While loops inside functions
@fn(first_power_above(base, limit)) {
value := 1
while(value <= limit) {
value = value * base
}
return value
} Returning a value is optional
A function may return expression, use bare return, or fall through the end. A value-less result is null, which can be tested with expressions such as x != null.
Recursion
@fn(factorial(n)) {
if(n <= 1) { return 1 }
return n * factorial(n - 1)
} Fragments
Fragments are for reusable rendered content, so their bodies keep normal template grammar: @if, @for, @while and $[...].
@fragment(card(item)) {
@if(!item.visible) { return }
<article class="card">
<h2>$[item.title]</h2>
@if(item.stock <= 0) {
<p>Out of stock</p>
return
}
<p>$[item.stock] available</p>
</article>
} Bare return stops the current fragment. It does not roll back content already rendered, and fragments cannot return an expression value.
Scope
Functions and fragments receive their own child scope. Existing visible mutable bindings can be changed, while declarations created inside the callable disappear when it exits.
First-class callables and lambdas
Nift 4.3 makes named functions and lambdas first-class callable values. Lambdas support expression bodies, statement blocks, lexical closures, variadic parameters and higher-order collection operations.
$[double := x => x * 2]
$[operation := double]
$[operation(6)] For live-binding capture, escaping closures, factories, rendered lambdas, identity and collection examples, see Lambdas & closures.
Variadic parameters and spread (v4.4)
Functions and lambdas may end their parameter list with ...args. The variadic binding is an ordinary heterogeneous array. Call-site spread expands an array back into positional arguments.
@fn(join_words(prefix, ...words)) {
return prefix + words.join("-")
}
$[parts := ["one", "two"]]
$[join_words("nift:", ...parts)]
$[count := (...items) => items.size()]
$[count(1, "two", true)] Only one variadic parameter is allowed and it must be last. Spread requires an array.
Async functions (v4.5)
@fn[async](load_record(id)) {
return fetch_record(id)
}
record := await load_record(42) In standalone script syntax, omit the template prefix: fn[async](load_record(id)) { ... }. Calling an async function returns a future; consume it with prefix await. Async lambdas use async (args) => { ... }. See Threads, mutexes, async functions & futures for scheduling, failure and lifetime semantics.
Mapping values to rendered markup (v4.4)
$[html := posts
.filter(post => post.published)
.map(post => {
<article><h2>$[post.title]</h2></article>
})
.join("")]
$[html] A markup-first block lambda produces its rendered text as the callback value; ordinary statement-block lambdas keep their existing return semantics.