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.

local events = nupp.events

@derive(events.Event)
local record Damage
    amount: number
    source: integer
    kind: string = "physical"
end

local bus: events.MessageBus<integer> = 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<A> is one, and any record with an observers field implements nupp.events.Source and dispatches through the same functions. Record events draw from a nupp.util.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.

Module contents

Types

TypeKindDescription
AllocatorinterfaceStorage an event type is constructed into and returned to.
EmittableinterfaceWhat a declaration becomes by deriving events.Event.
MessageBusrecordA source that is nothing but its observers.
ObservertypeAn observer of an event type.
ObserversrecordObserver registrations for a source, keyed by address and event identity.
SourceinterfaceSomething that owns observer state.

Functions

FunctionKindDescription
clearAddressfunctionRemoves every registration at one address.
deliverfunctionDelivers an event instance the caller already holds.
emitfunctionConstructs an event into the source's storage and delivers it.
Eventcomptime functionMarks a record or a fixed-layout struct as an event.
hasObserversfunctionWhether anything observes an event type at an address.
idfunctionThe identity an event type has in this runtime.
namefunctionThe name an event type registered under.
newMessageBusfunctionA bus with no observers.
newObserversfunctionEmpty observer state for a new source.
observefunctionRegisters an observer of an event type at an address.
observeOncefunctionRegisters an observer that is consumed before its first delivery.
resetfunctionRemoves every registration at every address.
setAllocatorfunctionInstalls storage for constructing an event type at this source.
stopObservingfunctionRemoves registrations at an address and event.

Types#

Allocatorinterface#

interface events.Allocator<E> ...

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.util.Pool and nupp.mem.arena implement it.

Type parameters

NameDescription
E

Members

NameKindDescription
acquiremethod
releasemethod

acquiremethod#

acquire: function(exclusive self: Allocator<E>): E
Arguments
NameTypeDescription
exclusive selfAllocator<E>
Returns
TypeDescription
E

releasemethod#

release: function(exclusive self: Allocator<E>, value: E): nil
Arguments
NameTypeDescription
exclusive selfAllocator<E>
valueE
Returns
TypeDescription
nil

Emittableinterface#

interface events.Emittable

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.

MessageBusrecord#

record events.MessageBus<A> is events.Source<A> ...

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

NameDescription
A

Members

NameKindDescription
observersfield
observemethodRegisters an observer; see nupp.events.observe.
observeOncemethodRegisters an observer consumed on first delivery; see nupp.events.observeOnce.
stopObservingmethodRemoves registrations; see nupp.events.stopObserving.
hasObserversmethodWhether anything observes an event here; see nupp.events.hasObservers.
emitmethodConstructs and delivers an event; see nupp.events.emit.
delivermethodDelivers an instance; see nupp.events.deliver.
clearAddressmethodRemoves every registration at an address; see nupp.events.clearAddress.
resetmethodRemoves every registration; see nupp.events.reset.
setAllocatormethodInstalls storage for an event type; see nupp.events.setAllocator.

observersfield#

observers: events.Observers<A>

observemethod#

observe: function<E is events.Emittable>(
    exclusive self: MessageBus<A>,
    address: A,
    event: Type<E>,
    callback: events.Observer<E>,
    id: string?
): nil

Registers an observer; see nupp.events.observe.

Arguments
NameTypeDescription
exclusive selfMessageBus<A>
addressA
eventType<E>
callbackevents.Observer<E>
idstring?
Returns
TypeDescription
nil

observeOncemethod#

observeOnce: function<E is events.Emittable>(
    exclusive self: MessageBus<A>,
    address: A,
    event: Type<E>,
    callback: events.Observer<E>,
    id: string?
): nil

Registers an observer consumed on first delivery; see nupp.events.observeOnce.

Arguments
NameTypeDescription
exclusive selfMessageBus<A>
addressA
eventType<E>
callbackevents.Observer<E>
idstring?
Returns
TypeDescription
nil

stopObservingmethod#

stopObserving: function<E is events.Emittable>(
    exclusive self: MessageBus<A>,
    address: A,
    event: Type<E>,
    callbackOrId: events.Observer<E> | string
): boolean

Removes registrations; see nupp.events.stopObserving.

Arguments
NameTypeDescription
exclusive selfMessageBus<A>
addressA
eventType<E>
callbackOrIdevents.Observer<E> | string
Returns
TypeDescription
boolean

hasObserversmethod#

hasObservers: function<E is events.Emittable>(self: MessageBus<A>, address: A, event: Type<E>): boolean

Whether anything observes an event here; see nupp.events.hasObservers.

Arguments
NameTypeDescription
selfMessageBus<A>
addressA
eventType<E>
Returns
TypeDescription
boolean

emitmethod#

emit: function<E is events.Emittable>(
    exclusive self: MessageBus<A>,
    address: A,
    event: Type<E>,
    ...: unpackof Construction(E)
): nil

Constructs and delivers an event; see nupp.events.emit.

Arguments
NameTypeDescription
exclusive selfMessageBus<A>
addressA
eventType<E>
...unpackof Construction(E)
Returns
TypeDescription
nil

delivermethod#

deliver: function<E is events.Emittable>(
    exclusive self: MessageBus<A>,
    address: A,
    event: Type<E>,
    instance: E
): nil

Delivers an instance; see nupp.events.deliver.

Arguments
NameTypeDescription
exclusive selfMessageBus<A>
addressA
eventType<E>
instanceE
Returns
TypeDescription
nil

clearAddressmethod#

clearAddress: function(exclusive self: MessageBus<A>, address: A): nil

Removes every registration at an address; see nupp.events.clearAddress.

Arguments
NameTypeDescription
exclusive selfMessageBus<A>
addressA
Returns
TypeDescription
nil

resetmethod#

reset: function(exclusive self: MessageBus<A>): nil

Removes every registration; see nupp.events.reset.

Arguments
NameTypeDescription
exclusive selfMessageBus<A>
Returns
TypeDescription
nil

setAllocatormethod#

setAllocator: function<E is events.Emittable>(
    exclusive self: MessageBus<A>,
    event: Type<E>,
    allocator: events.Allocator<E>
): nil

Installs storage for an event type; see nupp.events.setAllocator.

Arguments
NameTypeDescription
exclusive selfMessageBus<A>
eventType<E>
allocatorevents.Allocator<E>
Returns
TypeDescription
nil

Observertype#

type events.Observer<E> = function(borrows event: E): nil

An observer of an 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

NameDescription
E

Observersrecord#

record events.Observers<A> ...

Observer registrations for a 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

NameDescription
A

Members

NameKindDescription
byAddressfield
countfieldHow many registrations are live across every address.
depthfield
pendingfield
dirtyfield
detachedfield
freeListsfield
freeMapsfield
allocatorsfield
ownedArenasfield

byAddressfield#

byAddress: {[A]: {[integer]: any}}
@private

countfield#

count: integer
@readonly

How many registrations are live across every address.

depthfield#

depth: integer
@private

pendingfield#

pending: boolean
@private

dirtyfield#

dirty: {[A]: boolean}
@private

detachedfield#

detached: {any}
@private

freeListsfield#

freeLists: {any}
@private

freeMapsfield#

freeMaps: {any}
@private

allocatorsfield#

allocators: {[integer]: any}
@private

ownedArenasfield#

ownedArenas: {any}
@private

Sourceinterface#

interface events.Source<A> ...

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

NameDescription
A

Members

NameKindDescription
observersfield

observersfield#

observers: events.Observers<A>

Functions#

events.clearAddressfunction#

function events.clearAddress<A, S is events.Source<A>>(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

NameDescription
A
S

Arguments

NameTypeDescription
exclusive sourceS

the bus or world

borrows addressA

the address to clear

Returns

TypeDescription
nil

events.deliverfunction#

function events.deliver<A, S is events.Source<A>, E is events.Emittable>(exclusive source: S, borrows address: A, event: Type<E>, 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

NameDescription
A
S
E

Arguments

NameTypeDescription
exclusive sourceS

the bus or world

borrows addressA

where to deliver

eventType<E>

the event declaration

instanceE

the instance to deliver

Returns

TypeDescription
nil

Raises

TypeCondition
any

what an observer raised

events.emitfunction#

function events.emit<A, S is events.Source<A>, E is events.Emittable>(exclusive source: S, borrows address: A, event: Type<E>, ...: 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

NameDescription
A
S
E

Arguments

NameTypeDescription
exclusive sourceS

the bus or world

borrows addressA

where to deliver

eventType<E>

the event declaration

...unpackof Construction(E)

the event's constructor arguments, positional or named

Returns

TypeDescription
nil

Raises

TypeCondition
any

what an observer, initializer, or allocator raised after bookkeeping is restored

events.Eventcomptime function#

@comptime function events.Event(info: nupp.derive.Info): nupp.derive.Result<events.Emittable>
@comptime

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

NameTypeDescription
infonupp.derive.Info

the declaration being derived

Returns

TypeDescription
nupp.derive.Result<events.Emittable>

the recipe claiming events.Emittable

events.hasObserversfunction#

function events.hasObservers<A, S is events.Source<A>, E is events.Emittable>(borrows source: S, borrows address: A, event: Type<E>): boolean

Whether anything observes an event type at an address.

Type parameters

NameDescription
A
S
E

Arguments

NameTypeDescription
borrows sourceS

the bus or world

borrows addressA

the address to ask about

eventType<E>

the event declaration

Returns

TypeDescription
boolean

whether a delivery there would reach an observer

events.idfunction#

function events.id<E is events.Emittable>(event: Type<E>): 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

NameDescription
E

Arguments

NameTypeDescription
eventType<E>

the event declaration

Returns

TypeDescription
integer

its identity

Raises

TypeCondition
string

when the type did not derive events.Event

events.namefunction#

function events.name<E is events.Emittable>(event: Type<E>): string

The name an event type registered under.

Type parameters

NameDescription
E

Arguments

NameTypeDescription
eventType<E>

the event declaration

Returns

TypeDescription
string

the declared or @event-given name

Raises

TypeCondition
string

when the type did not derive events.Event

events.newMessageBusfunction#

function events.newMessageBus<A>(): events.MessageBus<A>

A bus with no observers.

The address type is whatever the binding asks for: local bus: events.MessageBus<integer> = events.newMessageBus().

Type parameters

NameDescription
A

Returns

TypeDescription
events.MessageBus<A>

the bus

events.newObserversfunction#

function events.newObservers<A>(): events.Observers<A>

Empty observer state for a new source.

Type parameters

NameDescription
A

Returns

TypeDescription
events.Observers<A>

the state, with nothing registered

events.observefunction#

function events.observe<A, S is events.Source<A>, E is events.Emittable>(exclusive source: S, borrows address: A, event: Type<E>, callback: events.Observer<E>, id: string?): nil

Registers an observer of an event type at an 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

NameDescription
A
S
E

Arguments

NameTypeDescription
exclusive sourceS

the bus or world that owns the observer registry and event storage

borrows addressA

where the event is delivered

eventType<E>

the event declaration

callbackevents.Observer<E>

what runs when one is delivered

idstring?

an optional name for removal

Returns

TypeDescription
nil

Raises

TypeCondition
string

when the type did not derive events.Event, or the name is taken

events.observeOncefunction#

function events.observeOnce<A, S is events.Source<A>, E is events.Emittable>(exclusive source: S, borrows address: A, event: Type<E>, callback: events.Observer<E>, 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

NameDescription
A
S
E

Arguments

NameTypeDescription
exclusive sourceS

the bus or world to observe through

borrows addressA

where the event is delivered

eventType<E>

the event declaration

callbackevents.Observer<E>

what runs, once

idstring?

an optional name for removal

Returns

TypeDescription
nil

Raises

TypeCondition
string

when the type did not derive events.Event, or the name is taken

events.resetfunction#

function events.reset<A, S is events.Source<A>>(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

NameDescription
A
S

Arguments

NameTypeDescription
exclusive sourceS

the bus or world

Returns

TypeDescription
nil

events.setAllocatorfunction#

function events.setAllocator<A, S is events.Source<A>, E is events.Emittable>(exclusive source: S, event: Type<E>, allocator: events.Allocator<E>): nil

Installs storage for constructing an event type 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

NameDescription
A
S
E

Arguments

NameTypeDescription
exclusive sourceS

the bus or world

eventType<E>

the event declaration

allocatorevents.Allocator<E>

storage of the event's representation

Returns

TypeDescription
nil

Raises

TypeCondition
string

when a delivery of that event is active

events.stopObservingfunction#

function events.stopObserving<A, S is events.Source<A>, E is events.Emittable>(exclusive source: S, borrows address: A, event: Type<E>, callbackOrId: events.Observer<E> | string): boolean

Removes registrations at an 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

NameDescription
A
S
E

Arguments

NameTypeDescription
exclusive sourceS

the bus or world observed through

borrows addressA

where the registration was made

eventType<E>

the event declaration

callbackOrIdevents.Observer<E> | string

the callback registered, or the name it was given

Returns

TypeDescription
boolean

whether anything was removed