Repository navigation
API Pluck
kimja edited this page Sep 28, 2026
·
4 revisions
𧬠API ⺠Pluck
π Gently plucks a single matter into rzeka.
rzeka.Pluck(this, new GameStarted());- Pluck has no automatic casuality tracking and it is usually OK for its usecases, which are mostly to just tick something once at the beginning of something greater.
- When you know what other matter caused it (usually while calling it from inside a Loom or Weave), stamp the circumstances yourself using
.WithCircumstances<T>():
rzeka.Pluck(this, new GamePaused().WithCircumstances(triggeringEvent));Eris will warn you that the
Pluckwas automatically scheduled onto the main thread when you pluck from a worker thread.
- This is not an error, but an off-thread Pluck will still likely mean its matter would be better taken care of through a Shuttle response or a Strand, which carries its causality with it instead of needing a manual stamp.
- The warning is there because your Pluck is no longer instantenous. It queues behind whatever is already on the main thread, rather than landing immediately. Eris considers that suspicious.
π Example of Strand replacing a recurring off-thread Plucking
Imagine a native/network/IO callback that fires repeatedly on its own thread β this is also the case when you will get much spam in Eris (one Hunch per emission).
- As a Strand it becomes a registered spell.
- Eris gets occurrences, mana lifecycle for downstream spell works, and disposing the Strand unsubscribes the event.
// β Off-thread Pluck, once per packet
public override void _Ready()
{
_socket.OnPacketReceived += packet =>
rzeka.Pluck(this, new PacketReceived(packet));
}
public override void _Ready()
{
// β Strand β the source is registered once
Q += rzeka.Strand(
this,
Observable
.FromEvent<Packet>(
h => _socket.OnPacketReceived += h,
h => _socket.OnPacketReceived -= h)
.Select(p => new PacketReceived(p))
);
}π Example of Shuttle replacing a recurring off-thread Plucking
An async operation kicked off imperatively, whose result gets yeeted into rzeka from the completion callback.
// β Off-thread Pluck from a completion callback
void OnSaveButtonPressed(int slot)
{
_saveSystem.SaveAsync(
slot,
wasSuccessful => rzeka.Pluck(this, new GameSaved(wasSuccessful)));
}
// β Shuttle + Ask combo
// Shuttle performs and reports
Q += rzeka.Shuttle<SaveGameRequest, SaveGameResponse>(
this,
spell =>
spell
.SelectMany(req => Observable
.FromAsync(ct => _saveSystem.SaveAsync(req.Slot, ct))
.Select(ok => new SaveGameResponse(req, ok)))
);
// The caller Asks, and the round trip carries its own causality
// Butten press gets its own matter emission somewhere upstream
Q += rzeka.Loom<SaveButtonPressed, GameSaved>(
this,
spell =>
spell
.SelectMany(evt =>
rzeka
.Ask<SaveGameRequest, SaveGameResponse>(
this,
new SaveGameRequest(evt.Slot).WithCircumstances(evt))
.Select(res => new GameSaved(res.WasSuccessful)
.WithCircumstances<GameSaved>(evt, res)))
);See also: Strand · circumstance rules · 𧬠API overview