scrml.dev v0.7.1
Reference › Keywords

lift

Emits a value from inside an anonymous logic block to the surrounding context. The scrml-flavored yield — renamed to avoid collision with JavaScript generators.

What it means

lift emits a value from within a ${ } logic context up to the parent context. The lifted value is coerced according to the parent's expected type — markup, CSS, or a scalar (in value-lift mode). Execution continues after lift; it is not a return statement.

Two modes per §10.1:

  1. Accumulation mode. Inside a bare ${ } whose parent is a markup or style context, each lift appends one item to the block's accumulator array. On block exit, the array is coerced per the parent (per §10.2 coercion table).
  2. Value-lift mode. Inside an if-as-expression arm (§17.6), one lift per arm designates the expression's result. Multiple lifts on a single execution path through one arm fires E-LIFT-002.

Syntax

Statement form:

lift <li value=name>;

Chain form (per §10.3) — equivalent:

users.select(userName).get().lift(<li value=userName>)

Worked example: building markup from an array

The classic accumulation case — one lift per loop iteration, accumulator coerced to a markup sequence by the parent <ul>:

<contacts> = [
  { name: "Alice",   email: "alice@example.com"   },
  { name: "Bob",     email: "bob@example.com"     },
}

<ul>
  ${ for (let c of @contacts) { lift <li>${c.name} — ${c.email}</li> } }
</ul>

The ${ } block sits as a child of the markup <ul>. Each lift appends one <li> element to the accumulator. On block exit, the array of <li> elements coerces to a markup sequence — rendered inside the <ul>.

Worked example: if-as-expression value-lift

An if-as-expression (§17.6) produces a single value per evaluation. Each arm body contains exactly one lift designating that arm's result:

const <message> = ${
  if (@count == 0)        { lift "no items"    }
  else if (@count == 1)   { lift "one item"    }
  else                    { lift "${@count} items" }
}

Notice: the lifted value is NOT accumulated into an array here. The if-as-expression's surrounding ${ } is a value-lift context (per §10.2 row 4), not an accumulation context. The lifted scalar becomes the const binding's value.

Coercion by parent context

Parent context Expected type Coercion
Markup markup[] Rendered as a markup sequence; non-coercible items are E-TYPE-010.
Style #{ } cssClass[] Coerced to CSS class list; non-CSS items are E-TYPE-011.
Logic any No coercion; passes through as array.
If-as-expression arm scalar Lifted value IS the expression's result; not accumulated.

Where lift is allowed

lift is valid in anonymous ${ } logic blocks and in fn bodies (where it accumulates into the fn's ~, returned via return ~). It is NOT valid in a bare function body. Per §10.4 + §48.5:

  • lift inside function name() { ... } is E-SYNTAX-002. Named functions return markup as a return value; callers lift the return if needed.
  • A fn name { ... } body, by contrast, DOES permit lift — it accumulates into the fn's ~ and is returned via return ~ (§48.5). E-SYNTAX-002 fires only on a bare function.
  • lift outside any ${ } context is E-SYNTAX-001.
  • Multiple lifts on the same execution path through one if-as-expression arm is E-LIFT-002.
  • Heterogeneous lifted values that aren't mutually coercible is E-TYPE-012.

lift under markup-as-value

Per §10.1.1, scrml's L1 pillar puts markup as a first-class value type — markup may sit on the RHS of any binding ( let, const, <x> =, function returns, function arguments, slot fills). lift is one path by which markup becomes a value — specifically, when the surrounding context is an anonymous accumulating ${ }. It is not the only path. Markup-typed derived cells (§6.6.17), markup-typed function returns, and inline markup-as-expression all produce markup values without lift.

Related

  • derived — markup-typed const <x> = <markup> is a markup value via reactive binding, no lift involved.
  • <program> — the root container; multi-page composition is via filesystem-routed <page>, NOT lift.

Specification

Normative text: SPEC §10 in full (semantics §10.1, L1 reframe §10.1.1, coercion §10.2, syntax §10.3, use-site restrictions §10.4) + §17.6 (if-as-expression value-lift). Spec lives at compiler/SPEC.md .