Make Automaton instances immutable
See original GitHub issue@eliotwrobson I want to run something by you that I’ve been mulling over for the past week.
Part 1: Precomputing lambda closures was a good idea
When you integrated networkx
into the library back in ed1cdffcccb90a03f1892147f42ea1a9f62414f6, you added logic to precompute+cache the lambda closures for an NFA (via self._lambda_closure_dict
). Previously, these lambda closures were (arguably excessively) computed on the fly when reading input via my NFA._get_lambda_closure
method.
Given the performance/efficiency consequences of my previous approach, I very much appreciate your contribution here (enough so that I am planning to expose this dictionary as part of the public API, as you may have noticed in my recent commits to develop
).
Part 2: Internal Automaton state
However, your changes do introduce an interesting dilemma. You see, before Automata v6 was released with your precomputed lambda closure logic, NFA instances could be mutated without consequence just like every other Automaton
instance in the library.
But now that lambda_closures
(a derived attribute, in database terms) is in the picture, the NFA instance could end up in a situation where the user updates states
, transitions
, and/or input_symbols
, leaving lambda_closures
stale.
As I see it, there are three solutions to this stale lambda_closures
situation if we want NFAs to remain fully mutable:
-
We watch for changes to
states
,input_symbols
, andtransitions
(which is a nested structure) and automatically recomputelambda_closures
when any of the aforementioned attributes change; this is the ideal solution, but not sure how feasible it is to implement -
We require the user to explicitly recompute the lambda closures (via some public method) when they choose to mutate the NFA. This is not my favorite approach, as the precomputing of lambda closures is really an implementation detail the user should not be concerned with. But it would balance implementation simplicity with performance.
-
We revert to caching nothing and computing the lambda closures on the fly when reading input; naturally, this would re-introduce a significant performance consequence, especially if you want to read many input strings with the same NFA instance
Of course, if we make NFAs fully immutable, then this problem goes away, and we don’t need to worry about recomputing lambda_closures
post-instantiation.
Part 3: Mutable or immutable?
So far, my blurb above is focused primarily on NFAs, but I think it opens up a larger question about whether any automaton should be a mutable structure, or whether it should be immutable. My ultimate aim for this library is to have a consistent and friendly API that makes working with these data structures pleasant and practical. And right now, NFAs are inconsistent with the rest of the Automaton sub-types because NFAs have some additional internal state (i.e. lambda_closures
) which must be managed.
Questions for you
Therefore, I will sum up my musings with a few questions regarding your own use of this library:
- How frequently are you reading input with the same automaton?
- How frequently (if ever) do you mutate an automaton after its instantiation?
- What are your thoughts about making all automata instances immutable?
- Anything else you want to add?
@abhinavsinha-adrino @Tagl If you guys happen to have a moment, I would love any feedback you have as well. This library isn’t any good if it doesn’t serve real-world use cases for working with these machines.
Much thanks in advance, Caleb
Issue Analytics
- State:
- Created a year ago
- Comments:49 (49 by maintainers)
@eliotwrobson @Tagl Since the Immutability work itself is already complete, I decided to merge the
immutability
branch intodevelop
. In accordance with this, I’ve switched the target branches of both your open PRs fromimmutability
todevelop
.I’ve also deleted the
immutability
branch, sodevelop
is back to being the correct target branch for which to submit PRs going forward.I have not been working with the library for quite some time. However, I never had to alter the automata I used after creation. I worked with them as if they were immutable. If I needed to mutate I would perform all mutations myself. Otherwise I used the union/intersect/etc. methods to mutate and construct more complicated DFAs. However I must note that I was working with permutation patterns so my bottleneck in performance (after improving the time complexity in the library) was more in regards to combinatorial explosion of the set of permutations rather than anything to do with the state machines. I think immutability is a good choice.