-
-
Save galanggg/f45fe61485bbe33c648176ece6667d3d to your computer and use it in GitHub Desktop.
Revisions
-
rupertlssmith revised this gist
Jan 12, 2018 . 1 changed file with 6 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -14,3 +14,9 @@ certain states are valid: InPlay (State { definition = { boardSize = 100 }, play = { score = 0, position = [] } }) GameOver (State { definition = { boardSize = 100 }, finalScore = 123 }) Ready (State { definition = { boardSize = 100 } }) The state machine exported by this module can only be manipulated in legal ways by code consuming the module. A constructor function is provided to create the initial state 'loading' but from there the 'toX' functions must be used to move through states without bypassing the rules of the state machine. Note: The 'State' type is fully exposed in the the-sett/elm-state-machines package here: [ https://github.com/the-sett/elm-state-machines/blob/1.0.0/src/StateMachine.elm#L27 ]. This means that it is possible to create illegal states and use the exposed Game constructors to build them. The alternative would be to not expose the 'State' type, but that would require that each state machine define its own private version of it with 'map' and 'untag' functions. In order to cheat in this way, the consumer of the state machine would need to import the StateMachine module, and use it to build illegal states. -
rupertlssmith revised this gist
Jan 12, 2018 . 2 changed files with 127 additions and 232 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,232 +0,0 @@ This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,127 @@ module GameState exposing ( Game(..) , GameDefinition , PlayState , loading , updateGameDefinition , updatePlayState , updateScore , toReady , toReadyWithGameDefinition , toInPlayWithPlayState , toGameOver ) import StateMachine exposing (..) -- An Example model for a game of some kind. type alias GameDefinition = { boardSize : Int } type alias PlayState = { score : Int , position : List Int } -- The state definitions have enough typing information to enforce matching -- states against legal state transitions, and against the available data model -- in the state. type Game = Loading (State { ready : Allowed } {}) | Ready (State { inPlay : Allowed } { definition : GameDefinition }) | InPlay (State { gameOver : Allowed } { definition : GameDefinition, play : PlayState }) | GameOver (State { ready : Allowed } { definition : GameDefinition, finalScore : Int }) -- State constructors. loading : Game loading = State {} |> Loading ready : GameDefinition -> Game ready definition = State { definition = definition } |> Ready inPlay : GameDefinition -> PlayState -> Game inPlay definition play = State { definition = definition, play = play } |> InPlay gameOver : GameDefinition -> Int -> Game gameOver definition score = State { definition = definition, finalScore = score } |> GameOver -- Update functions that can be applied when parts of the model are present. mapDefinition : (a -> b) -> ({ m | definition : a } -> { m | definition : b }) mapDefinition func = \model -> { model | definition = func model.definition } mapPlay : (a -> b) -> ({ m | play : a } -> { m | play : b }) mapPlay func = \model -> { model | play = func model.play } updateGameDefinition : (GameDefinition -> GameDefinition) -> State p { m | definition : GameDefinition } -> State p { m | definition : GameDefinition } updateGameDefinition func state = map (mapDefinition func) state updatePlayState : (PlayState -> PlayState) -> State p { m | play : PlayState } -> State p { m | play : PlayState } updatePlayState func state = map (mapPlay func) state updateScore : Int -> PlayState -> PlayState updateScore score play = { play | score = score } -- State transition functions that can be applied only to states that are permitted -- to make a transition. toReady : State { a | ready : Allowed } { m | definition : GameDefinition } -> Game toReady (State model) = ready model.definition toReadyWithGameDefinition : GameDefinition -> State { a | ready : Allowed } m -> Game toReadyWithGameDefinition definition game = ready definition toInPlayWithPlayState : PlayState -> State { a | inPlay : Allowed } { m | definition : GameDefinition } -> Game toInPlayWithPlayState play (State model) = inPlay model.definition play toGameOver : State { a | gameOver : Allowed } { m | definition : GameDefinition, play : PlayState } -> Game toGameOver (State model) = gameOver model.definition model.play.score -
rupertlssmith revised this gist
Jan 11, 2018 . 1 changed file with 7 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -7,4 +7,10 @@ and functions need to be mapped over that data or updates to it made, rather tha As the example runs it prints out the states, showing how the shape of the model varies as the state machine runs. This is the point of using the state machine; it only makes available fields in the model that need to exist in any given state. This removes the need for lots of fields in the model to by 'Maybe's, or to have lots of 'Bool' flags in the model to indicate when certain states are valid: Loading (State {}) Ready (State { definition = { boardSize = 100 } }) InPlay (State { definition = { boardSize = 100 }, play = { score = 0, position = [] } }) GameOver (State { definition = { boardSize = 100 }, finalScore = 123 }) Ready (State { definition = { boardSize = 100 } }) -
rupertlssmith revised this gist
Jan 11, 2018 . 1 changed file with 6 additions and 2 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -2,5 +2,9 @@ This Gist explores the idea of using phantom types to encode the possible states some other state in a state machine. This also demonstrates how this can be used in a more real world setting where states in the machine may have addition data, and functions need to be mapped over that data or updates to it made, rather than just a pure state machine. As the example runs it prints out the states, showing how the shape of the model varies as the state machine runs. This is the point of using the state machine; it only makes available fields in the model that need to exist in any given state. This removes the need for lots of fields in the model to by 'Maybe's, or to have lots of 'Bool' flags in the model to indicate when certain states are valid. -
rupertlssmith revised this gist
Jan 11, 2018 . 1 changed file with 18 additions and 22 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -36,7 +36,7 @@ main = init : ( Model, Cmd Msg ) init = ( { game = loading , previous = [] , count = 5 } @@ -63,13 +63,9 @@ update msg model = ) ( InPlay inPlay, Die finalScore ) -> ( { model | game = toGameOver <| (updatePlayState <| updateScore finalScore) inPlay } , msgToCmd AnotherGo ) ( GameOver gameOver, AnotherGo ) -> ( { model | game = toReady gameOver } @@ -137,8 +133,8 @@ type Game -- The state definitions with enough typing information to enforce matching -- states against legal state transitions, and against the available data model -- in the state. type alias Loading = @@ -161,24 +157,24 @@ type alias GameOver = -- State constructors. loading : Game loading = State {} |> Loading ready : GameDefinition -> Game ready definition = State { definition = definition } |> Ready inPlay : GameDefinition -> PlayState -> Game inPlay definition play = State { definition = definition, play = play } |> InPlay gameOver : GameDefinition -> Int -> Game gameOver definition score = State { definition = definition, finalScore = score } |> GameOver @@ -218,19 +214,19 @@ updatePlayState func (State model) = toReady : State { a | ready : Allowed } { m | definition : GameDefinition } -> Game toReady (State model) = ready model.definition toReadyWithGameDefinition : GameDefinition -> State { a | ready : Allowed } m -> Game toReadyWithGameDefinition definition game = ready definition toInPlay : PlayState -> State { a | inPlay : Allowed } { m | definition : GameDefinition } -> Game toInPlay play (State model) = inPlay model.definition play toGameOver : State { a | gameOver : Allowed } { m | definition : GameDefinition, play : PlayState } -> Game toGameOver (State model) = gameOver model.definition model.play.score -
rupertlssmith revised this gist
Jan 11, 2018 . 1 changed file with 1 addition and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -104,7 +104,7 @@ type Allowed = Allowed type State trans model = State model -
rupertlssmith revised this gist
Jan 11, 2018 . 1 changed file with 155 additions and 44 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,9 +1,117 @@ module Game exposing (..) import Html exposing (Html) import Task -- An example of it in action. type Msg = Loaded GameDefinition | StartGame | Die Int | AnotherGo type alias Model = { game : Game , previous : List Game , count : Int } msgToCmd msg = Task.perform (\() -> msg) (Task.succeed ()) main = Html.program { init = init , subscriptions = \_ -> Sub.none , update = update , view = view } init : ( Model, Cmd Msg ) init = ( { game = Loading loading , previous = [] , count = 5 } , msgToCmd <| Loaded { boardSize = 100 } ) update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = let noop = ( model, Cmd.none ) ( nextGame, cmd ) = case ( model.game, (Debug.log "update" msg) ) of ( Loading loading, Loaded gameDefinition ) -> ( { model | game = toReadyWithGameDefinition gameDefinition loading } , msgToCmd StartGame ) ( Ready ready, StartGame ) -> ( { model | game = toInPlay { score = 0, position = [] } ready } , msgToCmd <| Die 123 ) ( InPlay inPlay, Die finalScore ) -> let newPlayState = (updatePlayState <| updateScore finalScore) inPlay in ( { model | game = toGameOver newPlayState } , msgToCmd AnotherGo ) ( GameOver gameOver, AnotherGo ) -> ( { model | game = toReady gameOver } , msgToCmd StartGame ) ( _, _ ) -> noop in if model.count > 0 then ( { nextGame | previous = model.game :: model.previous , count = model.count - 1 } , cmd ) else noop view : Model -> Html Msg view model = Html.div [] <| List.map (\game -> Html.p [] [ Html.text (toString game) ]) (List.reverse model.previous) -- Reusable state machine concepts. type Allowed = Allowed type State pre model = State model -- An Example model for a game of some kind. type alias GameDefinition = { boardSize : Int } @@ -15,31 +123,38 @@ type alias PlayState = } updateScore : Int -> PlayState -> PlayState updateScore score play = { play | score = score } type Game = Loading Loading | Ready Ready | InPlay InPlay | GameOver GameOver -- The state definitions with enough typing information to enforce matching -- states against states that can make transitions into the state, and against -- the available data model in the state. type alias Loading = State { ready : Allowed } {} type alias Ready = State { inPlay : Allowed } { definition : GameDefinition } type alias InPlay = State { gameOver : Allowed } { definition : GameDefinition, play : PlayState } type alias GameOver = State { ready : Allowed } { definition : GameDefinition, finalScore : Int } @@ -48,41 +163,30 @@ type alias GameOver = loading : Loading loading = State {} ready : GameDefinition -> Ready ready definition = State { definition = definition } inPlay : GameDefinition -> PlayState -> InPlay inPlay definition play = State { definition = definition, play = play } gameOver : GameDefinition -> Int -> GameOver gameOver definition score = State { definition = definition, finalScore = score } -- Map functions that can be applied when parts of the model are present. mapGameDefinition : (GameDefinition -> a) -> State p { m | definition : GameDefinition } -> a mapGameDefinition func (State model) = func model.definition @@ -93,33 +197,40 @@ mapGameDefinition func (Game model) = updateGameDefinition : (GameDefinition -> GameDefinition) -> State p { m | definition : GameDefinition } -> State p { m | definition : GameDefinition } updateGameDefinition func (State model) = State { model | definition = func model.definition } updatePlayState : (PlayState -> PlayState) -> State p { m | play : PlayState } -> State p { m | play : PlayState } updatePlayState func (State model) = State { model | play = func model.play } -- State transition functions that can be applied only to states that are permitted -- to make a transition. toReady : State { a | ready : Allowed } { m | definition : GameDefinition } -> Game toReady (State model) = ready model.definition |> Ready toReadyWithGameDefinition : GameDefinition -> State { a | ready : Allowed } m -> Game toReadyWithGameDefinition definition game = ready definition |> Ready toInPlay : PlayState -> State { a | inPlay : Allowed } { m | definition : GameDefinition } -> Game toInPlay play (State model) = inPlay model.definition play |> InPlay toGameOver : State { a | gameOver : Allowed } { m | definition : GameDefinition, play : PlayState } -> Game toGameOver (State model) = gameOver model.definition model.play.score |> GameOver -
rupertlssmith revised this gist
Jan 11, 2018 . 1 changed file with 1 addition and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +1,6 @@ This Gist explores the idea of using phantom types to encode the possible states that are allowed to make transitions into some other state in a state machine. This also demonstrates how this can be used in a more real world setting where states in the machine may have addition data, and functions need to be mapped over that data or updates to it made whilst remaining in the current state, rather than just a pure state machine. -
rupertlssmith revised this gist
Jan 11, 2018 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
Empty file. -
rupertlssmith revised this gist
Jan 11, 2018 . 2 changed files with 5 additions and 5 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,5 +0,0 @@ This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,5 @@ This Gist explores the idea of using phantom types to encode the possible states that are allowed to make transitions into some other state in a state machine. This also demonstrates how this can be used in a more real world setting where states in the machine may have addition data, and functions need to be mapped over that data or updates to it made whilst remaining in the current state, rather than just a pure state machine. -
rupertlssmith revised this gist
Jan 11, 2018 . 1 changed file with 125 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,125 @@ module Game exposing (..) type Allowed = Allowed type alias GameDefinition = { boardSize : Int } type alias PlayState = { score : Int , position : List Int } type State = Loading | Ready | InPlay | GameOver type Game pre model = Game model type alias Loading = Game {} { state : State } type alias Ready = Game { loading : Allowed, gameOver : Allowed } { state : State, definition : GameDefinition } type alias InPlay = Game { ready : Allowed } { state : State, definition : GameDefinition, play : PlayState } type alias GameOver = Game { inPlay : Allowed } { state : State, definition : GameDefinition, finalScore : Int } -- State constructors. loading : Loading loading = Game { state = Loading } ready : GameDefinition -> Ready ready definition = Game { state = Ready , definition = definition } inPlay : GameDefinition -> PlayState -> InPlay inPlay definition play = Game { state = InPlay , definition = definition , play = play } gameOver : GameDefinition -> Int -> GameOver gameOver definition score = Game { state = GameOver , definition = definition , finalScore = score } -- Map functions that can be applied when parts of the model are present. mapGameDefinition : (GameDefinition -> a) -> Game p { m | definition : GameDefinition } -> a mapGameDefinition func (Game model) = func model.definition -- ... more mapping functions -- Update functions that can be applied when parts of the model are present. updateGameDefinition : (GameDefinition -> GameDefinition) -> Game p { m | definition : GameDefinition } -> Game p { m | definition : GameDefinition } updateGameDefinition func (Game model) = Game { model | definition = func model.definition } -- ... more update functions -- State transition functions that can be applied only to states that are permitted -- to make a transition. toReady : Game { a | ready : Allowed } { m | definition : GameDefinition } -> Ready toReady (Game model) = ready model.definition toReadyWithGameDefinition : GameDefinition -> Game { a | ready : Allowed } m -> Ready toReadyWithGameDefinition definition game = ready definition toInPlay : PlayState -> Game { a | inPlay : Allowed } { m | definition : GameDefinition } -> InPlay toInPlay play (Game model) = inPlay model.definition play toGameOver : Game { a | gameOver : Allowed } { m | definition : GameDefinition, play : PlayState } -> GameOver toGameOver (Game model) = gameOver model.definition model.play.score -
rupertlssmith revised this gist
Jan 11, 2018 . 1 changed file with 4 additions and 1 deletion.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -1,2 +1,5 @@ This Gist explores the idea of using phantom types to encode the possible states that are allowed to make transitions into some other state in a state machine. This also demonstrates how this can be used in a more real world setting where states in the machine may have addition data, and functions need to be mapped over that data or updates to it made whilst remaining in the current state, rather than just a pure state machine. -
rupertlssmith created this gist
Jan 11, 2018 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,2 @@ This Gist explores the idea of using phantom types to encode the possible states that are allowed to make transitions into some other state in a state machine.