I’m probably doing it wrong, but when there’s a state with multiple transitions out, I can either model it as distinct methods per transition in which case the caller needs to know how to transition between states or I can have the caller pass an enum in which moves the branch into the state machine at the expense of an enum and a match statement. It’s also like 10x the code. Again, I’m very open to the possibility that I’m doing something wrong. Curious how you would model a state machine for (1) reserving the right to do the inventory (2) querying the next page of results (based on a cursor) and (3) recording the page information and the next cursor.
How do you model cases where the reservation fails, where the inventory operation is already complete, etc? Basically conditional transitions based on some data received? Also, where does the actual I/O happen? Presumably there is some shell that drives the state machine that does the I/O before or after executing the transition?
I think this makes sense. I’m eager to try it. Some residual questions: what is “untyped” in this case and what is “PostConditional” (an enum of possible states?)? How do subsequent transitions work?
I also just realized that delegating to “UntypedStateMachine” for I/O probably doesn’t allow for Sans-I/O, right? Presumably the UntypedStateMachine must commit to either being sync or async and that must propagate to the typestate state machine itself, no?
Comments
I’m probably doing it wrong, but when there’s a state with multiple transitions out, I can either model it as distinct methods per transition in which case the caller needs to know how to transition between states or I can have the caller pass an enum in which moves the branch into the state machine at the expense of an enum and a match statement. It’s also like 10x the code. Again, I’m very open to the possibility that I’m doing something wrong. Curious how you would model a state machine for (1) reserving the right to do the inventory (2) querying the next page of results (based on a cursor) and (3) recording the page information and the next cursor.
In the type state pattern you would have something like this
pub trait ValidState {}
struct StateMachine<'a, T>
where
{untyped: &'a mut UntypedStateMachine,
_marker: PhantomData<T>
}
fn reserve_right<'a>(state: StateMachine<'a, Begin>) -> StateMachine<'a, Reserved>
fn query<'a>(state: StateMachine<'a, Reserved>) -> StateMachine<'a, Queried>
fn record<'a>(state: StateMachine<'a, Queried>) -> StateMachine<'a, Recorded>
How do you model cases where the reservation fails, where the inventory operation is already complete, etc? Basically conditional transitions based on some data received? Also, where does the actual I/O happen? Presumably there is some shell that drives the state machine that does the I/O before or after executing the transition?
The I/O is done using the mutable reference to the UntypedStateMachine in the functions.
For example UntypedStateMachine could just be a vec that you append to or read from.
If you want to model cases with failure your return type will be
Result<StateMachine<Reserved>, Error>
A conditional transition should return something like
(StateMachine<PostConditional>, ConditionalData)
I think this makes sense. I’m eager to try it. Some residual questions: what is “untyped” in this case and what is “PostConditional” (an enum of possible states?)? How do subsequent transitions work?
I also just realized that delegating to “UntypedStateMachine” for I/O probably doesn’t allow for Sans-I/O, right? Presumably the UntypedStateMachine must commit to either being sync or async and that must propagate to the typestate state machine itself, no?