Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve hierarchical state machines #548

Open
sockeqwe opened this issue Jul 27, 2023 · 0 comments
Open

Improve hierarchical state machines #548

sockeqwe opened this issue Jul 27, 2023 · 0 comments
Milestone

Comments

@sockeqwe
Copy link
Collaborator

sockeqwe commented Jul 27, 2023

Something to consider for FlowRedux 2.0 as working with onEnterStartStateMachine or onActionStartStateMachine is not so convenient, i.e. you dont need to deal with the initial state problem of the child state machines.

Maybe we could do something like we do with untilIdentityChanged( { ... } ).

I am not sure how feasible it is to implement and how much sense it makes, I am just sketching out an idea here:

sealed interface State
object Loading : State
data class ShowContent(items : List<Item>, val otherState : OtherState) : State

selead interface OtherState
object A : OtherState
object B : OtherState

then we could have our regular FlowReduxStateMachine but instead of having another FlowReduxStateMachine that manages OtherState we could let that be handled by the "parent" FlowReduxStateMachine (i will add a thought about reusability at the end) :

class MyStateMachine : FlowReduxStateMachine<State, Action>( Loading ){

  init {
    spec {
      inState<Loading> {
         onEnter { state -> 
           delay(1000)
           state.override { ShowContent( emptyList(), A) }
        }
     }

   inState<ShowContent> { 

     // new DSL block
     asChildState( { state : ShowContent -> state.otherState }) {  // specify which property of ShowContent state should be treated as a sub state
          // inside asSubState you can use all the regular DSL blocks as if it was the top level spec block of a regular FlowReduxStateMachine
         inState<A> {
            on<Action1> { ... }
            onEnter { ... }
            collectWhileInState { ... }
            condition { ... }
            untilIdentityChanged { ... }

            asSubState(...) // In theory we could support nesting too
         }
   
         inState<B> {     
            ...
         }

      }
    }
  }

}

The new DSL block is asChildState (better name needed). Basically you can "teach" a FlowReduxStateMachine how to act on a certain property of a State by treating it as a substate. That should come close to something like onEnterStartStateMachine or onActionStartStateMachine do today but I am not 100% sure if it is a 1-to-1 replacement or not 😄 need to think about it a bit more in detail ...

One of the nice things of onEnterStartStateMachine or onActionStartStateMachine is that you can make "reusable" smaller state machines. This is done by having small FlowReduxStateMachines on its own that can be "composed" into bigger state machines and reused when needed. We should aim to have the same advantage with the propose asChildState block.
How we could achieve that is by using extension functions (or maybe context receivers). I am not 100% sure if that really would compile but let's assume we have a SuperBuilderBlock (I am lacking a better name for now 😄 ) then we could maybe extract the logic descirbed above into an extension function. Something like this:

// Pseudo-code
fun SuperBuilderBlock.otherStateDefinition() {
    inState<A> {
            on<Action1> { ... }
            onEnter { ... }
            collectWhileInState { ... }
            condition { ... }
            untilIdentityChanged { ... }

            asSubState(...) // In theory we could support nesting too
         }
   
         inState<B> {     
            ...
         }
   }
}

once this logic is extracted in it's own extension function, we could simply invoke this extension function wherever we want to reuse this "state machine definition" for OtherState. For example the example above could look as follows:

class MyStateMachine : FlowReduxStateMachine<State, Action>( Loading ){

  init {
    spec {
      inState<Loading> {
         onEnter { state -> 
           delay(1000)
           state.override { ShowContent( emptyList(), A) }
        }
     }

   inState<ShowContent> { 

     // new DSL block
     asChildState( { state : ShowContent -> state.otherState }) {  // specify which property of ShowContent state should be treated 
           otherStateDefinition()
      }
    }
  }
}

and if we want to have a dedicated FlowReduxStateMachine for OtherState we could resue

class OtherStateMachine : FlowReduxStateMachine<OtherState, Action> {
   init {
     spec {  
        otherStateDefinition()
     }
  }
}

Again, not sure if this all will work or if we can achieve that with kotlin nor if it is a good ideas to add something like that to the DSL. I am just doing a braindump 😄

@gabrielittner gabrielittner added this to the 2.0 milestone Jul 28, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

2 participants