Idƴl Documentation

7 — Emit & catch

← Back to index · Previous: Control flow


The emit system lets temporal functions output named values alongside their main return value. Catch blocks let process blocks react to those emitted events.


Emit

Use emit name = expression inside a lambda block to publish a named side-channel value. Emitted values persist across ticks and are readable from outside.

step_counter(dt=100ms) = count |> {
    init: { count = 0  emit step = 0 }
    emit step = 3
    count = count + step
}

Here, step_counter has: - A main output: count (what the caller sees when using the instance as a value) - An emitted value: step (accessible via the :: operator)

Emit in init

Emitted variables can be initialized in the init block to set a default value before the first tick:

init: { emit finished = _ }   // starts as rest (no event)

Emit as event signal

A common pattern is emitting a trigger when a condition is met:

countdown(dt=100ms) = remaining |> {
    init: { remaining = 10  emit finished = _ }
    remaining = remaining - 1
    emit finished = (remaining == 0) ? _; !
}

finished is _ (rest) on most ticks, and ! (trigger) on the tick where remaining hits zero.


The :: accessor

The :: operator reads an emitted value from a temporal instance.

Syntax: instance::emitted_name

process: {
    c = step_counter()
    print("count:", c, "step:", c::step)
    doubled = c::step * 2
    print("doubled step:", doubled)
}

c is the main output of the instance. c::step reads the emitted step value.

The :: operator is also used for module namespace access (osc::send), but the evaluator distinguishes these by context.


Catch blocks

A catch block reacts to an emitted value becoming truthy. It is a standalone statement inside a process block that names the instance and signal using :: — consistent with how :: reads emitted values elsewhere.

Syntax:

catch instance::signal_name: {
    // handler statements
}

Example: countdown with completion handler

countdown(dt=100ms) = remaining |> {
    init: { remaining = 10  emit finished = _ }
    remaining = remaining - 1
    emit finished = (remaining == 0) ? _; !
}

process catch_demo, dur=3s: {
    timer = countdown()
    print("remaining:", timer)

    catch timer::finished: {
        print("Countdown complete!")
    }
}

On each tick, remaining is printed. When finished emits a trigger (!), the catch handler fires once and prints "Countdown complete!".

Catch semantics

Multiple catches

tracker(dt=50ms) = pos |> {
    init: { pos = 0  emit halfway = _  emit done = _ }
    pos = pos + 1
    emit halfway = (pos == 50)  ? _; !
    emit done    = (pos == 100) ? _; !
}

process: {
    t = tracker()

    catch t::halfway: {
        print("halfway there")
    }

    catch t::done: {
        print("finished")
    }
}

Anonymous instance catch (experimental)

The instance expression can be an inline call rather than a named binding. The instance is created when the process starts and lives for the lifetime of the process (or until it is removed by hot reload).

process: {
    // No binding needed — the counter instance is anonymous
    catch counter_n(4)::done: {
        print("caught after 4 ticks")
        stop
    }
}

The signal name resolves against the anonymous instance’s emitted_ map. If no matching emitted name is found, the instance’s main return value is used (so catch metro(dt=500ms)::tick: {} triggers whenever the metro’s trigger output is live).

Lifetime rule: the anonymous instance is alive as long as the process block that contains the catch is alive — it is cancelled automatically when the process stops or is hot-reloaded away.


Emit vs. output

Main output Emitted values
Access Use instance as value: c Use :: accessor: c::step
Per function Exactly one Any number
Declared with Output variable before \|> emit name = expr
Re-evaluated Every tick Every tick
External visibility Always Always (via ::)

The main output is what makes the instance usable in expressions (440 * (1 + osc * 0.1)). Emitted values are for metadata, events, and side-channel signals.


Next: Clock & tempo →