-
-
Notifications
You must be signed in to change notification settings - Fork 68
Description
I find myself always needing to include my Selector in my actor’s state for any non-trivial actor, or even for trivial actors that are under a supervisor. This feels like I’m carrying around an implementation detail.
Aside from it being cumbersome, I am frequently making mistakes that the compiler can’t catch. It is very easy to accidentally orphan a Subject for your actor if you forget to pass your selector forward on a branch.
Possible Solution
Add a way to merge new selectors into the actor’s current selector. Users won’t need to worry about managing their selectors manually.
Design 1: Add a new Next variant
pub type Next(message, state) {
Continue(state: state, selector: Option(Selector(message)))
AddSelector(state: state: selector: Selector(message))
Stop(ExitReason)
} then merge it with the existing one in actor.loop. Avoids any breaking changes, but feels janky.
Design 2: Make a breaking change to Next
This is effectively the same idea, but I prefer this because it models things better.
pub opaque type NextSelector(message) {
SameSelector
AddSelector(Selector(message))
ReplaceSelector(Selector(message))
}
pub type Next(message, state) {
Continue(state: state, selector: NextSelector(message))
Stop(ExitReason)
} Example usage
No need to store selector in state, and hard to accidentally orphan subjects.
fn my_actor_loop(message: Message, state: State) {
case message {
CallAnotherActor -> {
let selector = call_another_actor()
actor.continue(state) |> actor.add_selector(selector)
}
OtherActorResponse(_) -> actor.continue(state)
StartChild -> {
let assert Ok(child) = actor.start(child_spec())
let selector = monitor_child(child)
actor.continue(state) |> actor.add_selector(selector)
}
ChildDown(_) -> actor.continue(state)
}
}