omaskery / fsm-rs

A simple Finite State Machine library in Rust, define a couple of enums, construct a machine, define some transition behaviours and voila!
MIT License
14 stars 2 forks source link

Predicate implementation guide #1

Open Kerollmops opened 8 years ago

Kerollmops commented 8 years ago

Hello,

I need this crates in my project, I think I can contribute to yours, do you want me to implement the predicates ?

Can I have a guide or something ?

omaskery commented 8 years ago

Hi!

I haven't worked on this in a while, I recall last time I had trouble deciding how to implement predicates in a way that satisfied my previous design decisions.

I'm happy to discuss your thoughts on how you might implement it, and if it sounds compatible it would be great to get a pull request that moves this package closer to what I originally hoped for it :)

To give you something to chew on, here are my thoughts as a vague recollection of where I was when I last worked on this:

I believe the obstacle I encountered was that so far in my implementation my states & events were C-like enums, which are trivially copyable, comparable for equality and able to be cast as integers (for efficient indexing into arrays!). In order for predicates to make sense in this system you need state (and/or events) more complex than C-like enums and more like the tagged unions usually seen in Rust code.

The first problem this causes is that tagged unions cease to be trivially comparable for equality or castable to/from integers.

The second problem is that currently users inform the state machine of valid transitions by giving it the instances of source & target state values. This works well for simple enums, but falls down with complex states as you now have to instantiate a valid enum value to pass to the add_transition method. I'm not sure, but I suspect this is ugly and potentially undesirable in some use cases.

I'm interested in hearing your thoughts on how this could be resolved as I appreciate the help/interest :)

omaskery commented 8 years ago

Hi,

I see that you've done some nice tidying up in your fork of this repo - I appreciate your help!

Any thoughts on what I wrote above? :)

Kerollmops commented 8 years ago

Hi,

Can you explain more in details the role of a predicate in an fsm context ? And have you thought using Cargo doc ? Do you want some help implementing the travis.yml file ?

How do you think users can add_transitions in another manner ?

omaskery commented 8 years ago

Hi,

My intention for predicates was that you could register functions that would, given a current state and the occurring event, return true or false to indicate whether a given transition was valid.

For example, with the "TurnStyle" example used in the README.md. It is easy to imagine wanting to make the transition from Locked -> Unlocked conditional on how much money was inserted. You could do this by adding information to the InsertCoin event indicating what value coin was inserted (ignoring for a moment the issues with C-like-enums described in my initial reply). I imagined that when defining the transition:

machine.add_transition(
    TurnStyleState::Locked, TurnStyleEvent::InsertCoin,
    TurnStyleState::Unlocked, |_,_| println!("unlocked")
);

In psuedo-code, you might do the following:

machine.add_transition_conditional(
    TurnStyleState::Locked, TurnStyleEvent::InsertCoin,
    TurnStyleState::Unlocked, |_,_| println!("unlocked"),
    |state, event| => bool {
        match event {
            TurnStyleEvent::InsertCoin(particular_value_here) => true,
            _ => false,
        }
    }
);

I haven't yet decided how to make this a tenable interface, though and would appreciate any input. As I say, the main issues are how to have complex state/event enum values whilst still making those code examples read nicely.

The reason the second example is psuedo-code is because TurnStyleEvent::InsertCoin is no longer a valid expression once InsertCoin becomes a rust-enum with associated data. One would have to write something similar to TurnStyleEvent::InsertCoin(something_here), where something_here would have to be either some arbitrary valid value which is ignored or some specially defined invalid value?

Also I haven't thought about cargo doc/travis.yml. I'm not familiar with the latter if I'm honest, though discussion on that would probably be best in a separate issue, perhaps? :)

omaskery commented 8 years ago

Just a brief note, apologies that my answer is not more forthcoming with solutions - I'm just not that good at Rust yet. Hence I'm very grateful for any input!