Skip to Content

Subscription Handles and Event Values

Every subscription produces a subscription handle, and an event name in value position produces an event value. The handle covers unsubscribing and stats; the event value covers passing a channel around as data.

event Ticked(n: int); event MovedX(dx: int); var total = 0; func Add(n: int) { print($"added {n}"); } func Track(n: int) { total += n; }

The Subscription Handle’s Three Members

The value of a ~> / <~ / once ~> expression is a subscription: Cancel() unsubscribes, IsCancelled reports status, TriggerCount counts firings:

val h: subscription = Ticked ~> Add; emit Ticked(1); print("triggered", h.TriggerCount, "cancelled?", h.IsCancelled); h.Cancel(); emit Ticked(2); print("cancelled?", h.IsCancelled); h.Cancel();
added 1 triggered 1 cancelled? False cancelled? True

After cancellation, later emits are not delivered, and calling Cancel() again is harmless.

once: Auto-Cancel After One Firing

A once ~> subscription automatically becomes cancelled after its first firing:

val single = Ticked once ~> Add; emit Ticked(10); emit Ticked(20); print("once cancelled?", single.IsCancelled, "count", single.TriggerCount);
added 10 once cancelled? True count 1

Unsubscribing Anonymous Lambdas

An anonymous lambda subscription gets a handle too; keep the handle and unsubscribe with it:

val lh = Ticked ~> (n: int) => { total += n; }; emit Ticked(100); lh.Cancel(); emit Ticked(1000); print("total =", total);
total = 100

Events as First-Class Values

An event name in value position is an event value, typed by the shape type event<P...>. Equality is by declaration identity (only the same declaration compares equal), and an emit through a value still runs the pipelines declared on it:

val v: event<int> = MovedX; print(v == MovedX); emit v(7);
True

A “group of channels” can be expressed as a list of event values, subscribed and unsubscribed in a loop:

val watch: list<event<int>> = [MovedX, Ticked]; for e in watch { e ~> Track; } emit MovedX(3); emit Ticked(4); print("total =", total);
total = 107

Notes

  • Event values must share the same payload shape: more, fewer, or mismatched parameter types cannot be assigned to each other.
  • eventof(obj.Prop) takes an event prop channel as an event<T, T> value (payload: old value, new value), usable like any other event value afterwards.
  • A generic event’s type arguments are inferred from the handler’s parameters or the emit arguments.
Last updated on October 11, 2026