# `nupp.events`
Addressed, synchronous events whose payloads are ordinary declarations.
An event is a `record` or a fixed-layout `struct` marked `@derive(events.Event)`.
Emitting one by type constructs it into storage the source already owns, and
only when something at that address is observing; delivering an instance the
caller built allocates nothing. Observers receive the event as a call-scoped
borrow they may write to but not keep.
```nupp
local events = nupp.events
@derive(events.Event)
local record Damage
amount: number
source: integer
kind: string = "physical"
end
local bus: events.MessageBus = events.newMessageBus()
bus:observe(7, Damage, |event| -> print(event.amount, event.kind), "log")
bus:emit(7, Damage, amount = 10, source = 3)
```
Here `bus` is the event source: it owns the observer registry and reusable
event storage. `7` is the address within that source, so observers registered
at another entity id do not receive this delivery. The `MessageBus` methods
forward to the module functions below; the equivalent direct call is
`events.observe(bus, 7, Damage, callback, "log")`.
Observer state belongs to a source: `MessageBus` is one, and any record with
an `observers` field implements [`nupp.events.Source`](#nupp.events.Source) and dispatches through
the same functions. Record events draw from a `nupp.mem.pool` and struct
events from a `nupp.mem.arena`, each created the first time an event type is
emitted through a source; `setAllocator` installs one the caller owns instead.
[NEP 29](../../neps/0029-typed-events.md) records the design.
## Constructors
### `events.newMessageBus` _constructor_
```nupp
function events.newMessageBus(): events.MessageBus
```
A bus with no observers.
The address type is whatever the binding asks for:
`local bus: events.MessageBus = events.newMessageBus()`.
#### Type parameters
| Name | Description |
| --- | --- |
| `A` | |
#### Returns
| Type | Description |
| --- | --- |
| `events.MessageBus\` | the bus |
### `events.newObservers` _constructor_
```nupp
function events.newObservers(): events.Observers
```
Empty observer state for a new source.
#### Type parameters
| Name | Description |
| --- | --- |
| `A` | |
#### Returns
| Type | Description |
| --- | --- |
| `events.Observers\` | the state, with nothing registered |
## Types
### `Allocator` _interface_
```nupp
interface events.Allocator
acquire: function(exclusive self: Allocator): E
release: function(exclusive self: Allocator, value: E): nil
end
```
Storage an event type is constructed into and returned to.
`acquire` hands out reusable storage of the event's own representation, which
the emitting source initializes; `release` ends that lease when delivery is
over. `nupp.mem.pool` and `nupp.mem.arena` implement it.
#### Type parameters
| Name | Description |
| --- | --- |
| `E` | |
#### Methods
##### `acquire`
```nupp
acquire: function(exclusive self: Allocator): E
```
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `Allocator\` | |
###### Returns
| Type | Description |
| --- | --- |
| `E` | |
##### `release`
```nupp
release: function(exclusive self: Allocator, value: E): nil
```
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `Allocator\` | |
| `value` | `E` | |
###### Returns
| Type | Description |
| --- | --- |
| `nil` | |
### `Emittable` _interface_
```nupp
interface events.Emittable
end
```
What a declaration becomes by deriving `events.Event`.
The bound an event type parameter carries, so a source refuses a record
that never derived the event contract before anything runs.
### `MessageBus` _record_
```nupp
record events.MessageBus is events.Source
observers: events.Observers
observe: function(
exclusive self: MessageBus,
address: A,
event: Type,
callback: events.Observer,
id: string?
): nil
observeOnce: function(
exclusive self: MessageBus,
address: A,
event: Type,
callback: events.Observer,
id: string?
): nil
stopObserving: function(
exclusive self: MessageBus,
address: A,
event: Type,
callbackOrId: events.Observer | string
): boolean
hasObservers: function(self: MessageBus, address: A, event: Type): boolean
emit: function(
exclusive self: MessageBus,
address: A,
event: Type,
...: unpackof Construction(E)
): nil
deliver: function(
exclusive self: MessageBus,
address: A,
event: Type,
instance: E
): nil
clearAddress: function(exclusive self: MessageBus, address: A): nil
reset: function(exclusive self: MessageBus): nil
setAllocator: function(
exclusive self: MessageBus,
event: Type,
allocator: events.Allocator
): nil
end
```
A source that is nothing but its observers.
Every method forwards to the function of the same name, passing the bus as
the source that owns observer state.
#### Type parameters
| Name | Description |
| --- | --- |
| `A` | |
#### Methods
##### `observe`
```nupp
observe: function(
exclusive self: MessageBus,
address: A,
event: Type,
callback: events.Observer,
id: string?
): nil
```
Registers an observer; see [`nupp.events.observe`](#nupp.events.observe).
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `MessageBus\` | |
| `address` | `A` | |
| `event` | `Type\` | |
| `callback` | `events.Observer\` | |
| `id` | `string?` | |
###### Returns
| Type | Description |
| --- | --- |
| `nil` | |
##### `observeOnce`
```nupp
observeOnce: function(
exclusive self: MessageBus,
address: A,
event: Type,
callback: events.Observer,
id: string?
): nil
```
Registers an observer consumed on first delivery; see
[`nupp.events.observeOnce`](#nupp.events.observeOnce).
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `MessageBus\` | |
| `address` | `A` | |
| `event` | `Type\` | |
| `callback` | `events.Observer\` | |
| `id` | `string?` | |
###### Returns
| Type | Description |
| --- | --- |
| `nil` | |
##### `stopObserving`
```nupp
stopObserving: function(
exclusive self: MessageBus,
address: A,
event: Type,
callbackOrId: events.Observer | string
): boolean
```
Removes registrations; see [`nupp.events.stopObserving`](#nupp.events.stopObserving).
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `MessageBus\` | |
| `address` | `A` | |
| `event` | `Type\` | |
| `callbackOrId` | `events.Observer\ | string` | |
###### Returns
| Type | Description |
| --- | --- |
| `boolean` | |
##### `hasObservers`
```nupp
hasObservers: function(self: MessageBus, address: A, event: Type): boolean
```
Whether anything observes an event here; see [`nupp.events.hasObservers`](#nupp.events.hasObservers).
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `self` | `MessageBus\` | |
| `address` | `A` | |
| `event` | `Type\` | |
###### Returns
| Type | Description |
| --- | --- |
| `boolean` | |
##### `emit`
```nupp
emit: function(
exclusive self: MessageBus,
address: A,
event: Type,
...: unpackof Construction(E)
): nil
```
Constructs and delivers an event; see [`nupp.events.emit`](#nupp.events.emit).
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `MessageBus\` | |
| `address` | `A` | |
| `event` | `Type\` | |
| `...` | `unpackof Construction(E)` | |
###### Returns
| Type | Description |
| --- | --- |
| `nil` | |
##### `deliver`
```nupp
deliver: function(
exclusive self: MessageBus,
address: A,
event: Type,
instance: E
): nil
```
Delivers an instance; see [`nupp.events.deliver`](#nupp.events.deliver).
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `MessageBus\` | |
| `address` | `A` | |
| `event` | `Type\` | |
| `instance` | `E` | |
###### Returns
| Type | Description |
| --- | --- |
| `nil` | |
##### `clearAddress`
```nupp
clearAddress: function(exclusive self: MessageBus, address: A): nil
```
Removes every registration at an address; see [`nupp.events.clearAddress`](#nupp.events.clearAddress).
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `MessageBus\` | |
| `address` | `A` | |
###### Returns
| Type | Description |
| --- | --- |
| `nil` | |
##### `reset`
```nupp
reset: function(exclusive self: MessageBus): nil
```
Removes every registration; see [`nupp.events.reset`](#nupp.events.reset).
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `MessageBus\` | |
###### Returns
| Type | Description |
| --- | --- |
| `nil` | |
##### `setAllocator`
```nupp
setAllocator: function(
exclusive self: MessageBus,
event: Type,
allocator: events.Allocator
): nil
```
Installs storage for an event type; see [`nupp.events.setAllocator`](#nupp.events.setAllocator).
###### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive self` | `MessageBus\` | |
| `event` | `Type\` | |
| `allocator` | `events.Allocator\` | |
###### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### Fields
##### `observers`
```nupp
observers: events.Observers
```
### `Observer` _type_
```nupp
type events.Observer = function(borrows event: E): nil
```
An observer of one event type.
The event is borrowed for the call: it may be read and written, and cannot be
stored or returned. State beyond the event is explicit: capture it in the
observer or carry it as an event field.
#### Type parameters
| Name | Description |
| --- | --- |
| `E` | |
### `Observers` _record_
```nupp
record events.Observers
readonly count: integer
end
```
Observer registrations for one source, keyed by address and event identity.
A source holds one of these in its `observers` field. Nothing exists for an
address until something observes there, so a world of millions of entities
pays for the observers it has and not for the entities it could have.
#### Type parameters
| Name | Description |
| --- | --- |
| `A` | |
#### Fields
##### `count`
```nupp
count: integer
```
How many registrations are live across every address.
### `Source` _interface_
```nupp
interface events.Source
observers: events.Observers
end
```
Something that owns observer state.
A bus or a world implements this by holding an `observers` field; the
dispatch functions take the source exclusively and reach its state through
that field. The source is not implicitly passed to observers.
#### Type parameters
| Name | Description |
| --- | --- |
| `A` | |
#### Fields
##### `observers`
```nupp
observers: events.Observers
```
## Functions
### `events.clearAddress` _function_
```nupp
function events.clearAddress>(exclusive source: S, borrows address: A): nil
```
Removes every registration at one address.
A delivery in flight at that address finishes the list it already holds; the
next emission there finds nothing. Registrations at other addresses are
untouched, which is how a world clears an entity without losing what
observes the world itself.
#### Type parameters
| Name | Description |
| --- | --- |
| `A` | |
| `S` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive source` | `S` | the bus or world |
| `borrows address` | `A` | the address to clear |
#### Returns
| Type | Description |
| --- | --- |
| `nil` | |
### `events.deliver` _function_
```nupp
function events.deliver, E is events.Emittable>(exclusive source: S, borrows address: A, event: Type, instance: E): nil
```
Delivers an event instance the caller already holds.
The instance is neither acquired nor released: what the caller built stays
the caller's, and is handed to each observer as the borrow its contract
names. Everything else is as `emit`.
#### Type parameters
| Name | Description |
| --- | --- |
| `A` | |
| `S` | |
| `E` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive source` | `S` | the bus or world |
| `borrows address` | `A` | where to deliver |
| `event` | `Type\` | the event declaration |
| `instance` | `E` | the instance to deliver |
#### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### Raises
- what an observer raised
### `events.emit` _function_
```nupp
function events.emit, E is events.Emittable>(exclusive source: S, borrows address: A, event: Type, ...: unpackof Construction(E)): nil
```
Constructs an event into the source's storage and delivers it.
Nothing is acquired, initialized, or run when no observer is registered at
the address for the event: the argument expressions are evaluated as they
are for any call, and that is all. Otherwise storage is leased from the
event's allocator, the declaration's initializer fills it from the
arguments, applying a field default where an argument is nil, every
observer registered when the delivery began runs in order, and the lease is
released whether the delivery returned, raised, or was cancelled while an
observer was suspended.
#### Type parameters
| Name | Description |
| --- | --- |
| `A` | |
| `S` | |
| `E` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive source` | `S` | the bus or world |
| `borrows address` | `A` | where to deliver |
| `event` | `Type\` | the event declaration |
| `...` | `unpackof Construction(E)` | the event's constructor arguments, positional or named |
#### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### Raises
- what an observer or the initializer raised, after the lease is released
### `events.Event` _comptime function_
```nupp
comptime function events.Event(info: nupp.derive.Info): nupp.derive.Result
```
Marks a record or a fixed-layout struct as an event.
The derive records the event's name and representation, and asks the compiler
for the declaration's initializer: its constructor body, or its field list,
run against storage a source already holds. A declaration with several
constructors, one whose constructor keeps `self` or moves an owner into it,
an affine field, or a generic owner is refused, since none of those can run
against reused storage.
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `info` | `nupp.derive.Info` | the declaration being derived |
#### Returns
| Type | Description |
| --- | --- |
| `nupp.derive.Result\` | the recipe claiming `events.Emittable` |
### `events.hasObservers` _function_
```nupp
function events.hasObservers, E is events.Emittable>(borrows source: S, borrows address: A, event: Type): boolean
```
Whether anything observes one event type at one address.
#### Type parameters
| Name | Description |
| --- | --- |
| `A` | |
| `S` | |
| `E` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `borrows source` | `S` | the bus or world |
| `borrows address` | `A` | the address to ask about |
| `event` | `Type\` | the event declaration |
#### Returns
| Type | Description |
| --- | --- |
| `boolean` | whether a delivery there would reach an observer |
### `events.id` _function_
```nupp
function events.id(event: Type): integer
```
The identity an event type has in this runtime.
Assigned once, the first time a source, `id`, or `name` asks, from a counter
local to this Lua state. It is stable for the life of the state and is never
a persistent or wire identity.
#### Type parameters
| Name | Description |
| --- | --- |
| `E` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `event` | `Type\` | the event declaration |
#### Returns
| Type | Description |
| --- | --- |
| `integer` | its identity |
#### Raises
- when the type did not derive `events.Event`
### `events.name` _function_
```nupp
function events.name(event: Type): string
```
The name an event type registered under.
#### Type parameters
| Name | Description |
| --- | --- |
| `E` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `event` | `Type\` | the event declaration |
#### Returns
| Type | Description |
| --- | --- |
| `string` | the declared or `@event`-given name |
#### Raises
- when the type did not derive `events.Event`
### `events.observe` _function_
```nupp
function events.observe, E is events.Emittable>(exclusive source: S, borrows address: A, event: Type, callback: events.Observer, id: string?): nil
```
Registers an observer of one event type at one address.
Observers run in registration order. A name lets `stopObserving` remove this
registration by name; a name already registered at the same address and event
is an error, since the second could never be removed alone.
#### Type parameters
| Name | Description |
| --- | --- |
| `A` | |
| `S` | |
| `E` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive source` | `S` | the bus or world that owns the observer registry and event storage |
| `borrows address` | `A` | where the event is delivered |
| `event` | `Type\` | the event declaration |
| `callback` | `events.Observer\` | what runs when one is delivered |
| `id` | `string?` | an optional name for removal |
#### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### Raises
- when the type did not derive `events.Event`, or the name is taken
### `events.observeOnce` _function_
```nupp
function events.observeOnce, E is events.Emittable>(exclusive source: S, borrows address: A, event: Type, callback: events.Observer, id: string?): nil
```
Registers an observer that is consumed before its first delivery.
The registration is tombstoned before the callback is invoked, so a nested or
interleaved delivery of the same event never reaches it again, whatever the
callback goes on to do.
#### Type parameters
| Name | Description |
| --- | --- |
| `A` | |
| `S` | |
| `E` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive source` | `S` | the bus or world to observe through |
| `borrows address` | `A` | where the event is delivered |
| `event` | `Type\` | the event declaration |
| `callback` | `events.Observer\` | what runs, once |
| `id` | `string?` | an optional name for removal |
#### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### Raises
- when the type did not derive `events.Event`, or the name is taken
### `events.reset` _function_
```nupp
function events.reset>(exclusive source: S): nil
```
Removes every registration at every address.
Allocators are left alone: an arena the caller installed is still theirs, and
the source's own storage is reused by the next emission.
#### Type parameters
| Name | Description |
| --- | --- |
| `A` | |
| `S` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive source` | `S` | the bus or world |
#### Returns
| Type | Description |
| --- | --- |
| `nil` | |
### `events.setAllocator` _function_
```nupp
function events.setAllocator, E is events.Emittable>(exclusive source: S, event: Type, allocator: events.Allocator): nil
```
Installs the storage one event type is constructed into at this source.
The allocator is the caller's: it must outlive the source's use of it, and
the source never resets or closes it. Replacing one while a delivery holds
storage from it is refused, since the lease would have nowhere to return.
#### Type parameters
| Name | Description |
| --- | --- |
| `A` | |
| `S` | |
| `E` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive source` | `S` | the bus or world |
| `event` | `Type\` | the event declaration |
| `allocator` | `events.Allocator\` | storage of the event's representation |
#### Returns
| Type | Description |
| --- | --- |
| `nil` | |
#### Raises
- when a delivery of that event is active
### `events.stopObserving` _function_
```nupp
function events.stopObserving, E is events.Emittable>(exclusive source: S, borrows address: A, event: Type, callbackOrId: events.Observer | string): boolean
```
Removes registrations at one address and event.
Given a name, the first registration with that name goes; given a callback,
every registration of it goes. A removal during a delivery takes effect at
once for anything not yet reached and is compacted out after the outermost
delivery leaves. A caller removing by callback keeps the value it registered:
a short function is a fresh object each time its expression runs.
#### Type parameters
| Name | Description |
| --- | --- |
| `A` | |
| `S` | |
| `E` | |
#### Arguments
| Name | Type | Description |
| --- | --- | --- |
| `exclusive source` | `S` | the bus or world observed through |
| `borrows address` | `A` | where the registration was made |
| `event` | `Type\` | the event declaration |
| `callbackOrId` | `events.Observer\ | string` | the callback registered, or the name it was given |
#### Returns
| Type | Description |
| --- | --- |
| `boolean` | whether anything was removed |