automat
automat copied to clipboard
Conditional State Transitions?
Fully recognize this question may reflect bad practice or might just run counter to the opinion of the framework (which is great!), but do you have any plan to implement conditional state transitions?
Borrowing from your example, if the coffee maker needs, say, '5 beans' in order to brew coffee, is there any way (within the framework) to check that condition is satisfied, or would something like the following be the 'automatic' way to implement that? (pythonic -> automat-ic? get it?)
def put_in_some_beans(self, beans):
self._bean_counter += beans.count
if self._bean_counter >= 5:
self._put_in_enough_beans(beans)
else:
self._put_in_not_enough_beans(beans)
@_machine.input()
def _put_in_enough_beans(self, beans):
"There are enough beans in the hopper"
@_machine.input()
def _put_in_not_enough_beans(beans):
"There aren't enough beans"
@_machine.output()
def bean_error(self, beans):
return f'Missing {5-self._bean_counter} beans'
dont_have_beans.upon(_put_in_enough_beans, enter=have_beans, outputs=[])
dont_have_beans.upon(_put_in_not_enough_beans, enter=dont_have_beans, outputs=[bean_error])
I don't have any ideas or thoughts about how an alternative method would be implemented, or whether it even belongs. The reason I'm asking is that I see what could end up being a lot of state-checking and perhaps a lot of branching logic outside of the automat framework -> and maybe thats ok? Just making sure I'm not missing something.
As an aside: I'm working through this document: and trying to learn automat by implementing the examples; which is helpful in terms of thinking about setting up more complicated machines from primitives (cascading, parallel, etc), but their implementation philosophy is so different... if I can grok it, it might make for some helpful documentation additions.
Thanks @colemand77, that is a great reference document for us to look at!
do you have any plan to implement conditional state transitions?
In a word: no. Conditional transitions would compromise the purity of the machine, and make it more difficult to reason about what it takes to reach a particular state, which would obviate much of the usefulness of being able to diagram the machine automatically et cetera. But, obviously we need some way to handle the case that you've brought up. This is, in my mind, yet another use-case for #41 .
The way to implement this within the scope of Automat's design would be for the put_in_bean
input to generate a count_beans
output, which would +=
the counter, and then, conditionally, potentially itself produce an _enough_beans
input, using the feedback mechanism described in #41 . I am using the "private method" notation here because while _enough_beans
is an input to the state machine, it is an input only generated by this output, not something that external users of the state machine should be supplying.
Does this make sense to you as a solution?
Yep, totally. The "no inputs
in the outputs=[]
" field is a logical constraint I keep bumping my head on. Looks like this is something that (may) get some work in the future, so I'll work around it and keep my eyes peeled.
If I can get something worked out from your use-case in twisted, I'll submit it, but pretty sure out of my depth.
Great job on the show by the way.
Another use case for this kind of logic is a retry loop, where you want to retry something up to N times until you get a success. It is possible to represent both of these use cases as a state machine, but you need N copies of each of the states involved in processing a submission. This is similar to the (foo){1,50}
syntax in regular expressions, where the state machine needed to recognize "between one and fifty copies of 'foo'" is much larger than the state machine for just foo
. I don't know a good syntax for duplicating states and transitions, but that would be another way of addressing this fairly common use case that still gives the ability to diagram the output.