<style> .reveal .code-wrapper {width: fit-content; max-width: 200%; font-size: xxx-large;} .reveal img {max-width: 200%;} .reveal pre {max-height: 600px;} .reveal pre code {max-height: 600px; padding: 10px !important;} .reveal .hljs-ln-numbers {display: none;} code {color: #c7254e;} .fold {position: relative; left: -130px;} .tweaglogo {height: 60px; margin: 0 !important;} .haskellatwork {position: relative; left: -450px;} .list {position: relative; left: -90px; top: -70px;} .traverse {position: relative; left: -90px;} .traversePetFor {position: realtive; left: -40px;} .traverseParseEmail {position: relative; left: -60px;} .parseEmail {position: relative; left: -35px;} .foldlM {position: relative; left: -90px;} .httpCountDogs {position: relative; left: -115px;} .reveal table th, .reveal table td {border-right: 1px solid; border-color: white;} .reveal table thead tr th:last-child, .reveal table tbody tr td:last-child {border-right: none;} .reveal table tr td:first-child {font-weight: bold;} .highlight {color: #d9480f} .backgrounds {background-image: url(https://github.com/marcosh/functional-loops/blob/main/images/Tweag_Preso_BGND.svg?raw=true);} .reveal .slide-background { background-image: url(https://github.com/marcosh/functional-loops/blob/main/images/T_Mark_Orange.svg?raw=true); height: 50px; width: 50px; position: absolute; top: 20px; right: 35px; } .mermaid {text-align: center !important; background-color:#2d2d2d !important;} </style> # State machines with [`crem`](https://github.com/tweag/crem) <img src="https://raw.githubusercontent.com/tweag/crem/main/logo/crem-transparent.png" width="200"> --- ![the picture that explains everything](http://marcosh.github.io/img/the-picture-that-explains-everything.png) --- ### <span style="color: orange">Domain events</span> ![event](https://github.com/marcosh/ddd-machines-dddeurope/raw/main/images/the-picture-that-explains-everything-event.png) Events relevant for domain experts --- ### <span style="color: dodgerblue">Commands</span> ![command](https://github.com/marcosh/ddd-machines-dddeurope/raw/main/images/the-picture-that-explains-everything-command.png) User intentions/actions/decisions --- ### <span style="color: lightyellow">Read models</span> ![read model](https://github.com/marcosh/ddd-machines-dddeurope/raw/main/images/the-picture-that-explains-everything-read-model.png) Data needed in order to make decisions --- ### <span style="color: yellow">Aggregates</span> ![aggregate](https://github.com/marcosh/ddd-machines-dddeurope/raw/main/images/the-picture-that-explains-everything-aggregate.png) Decide what happens on commands --- ### <span style="color: orchid">Policies</span> ![policy](https://github.com/marcosh/ddd-machines-dddeurope/raw/main/images/the-picture-that-explains-everything-policy.png) Reactive logic that takes place after an event --- ### <span style="color: lightgreen">Projections</span> ![projection](https://github.com/marcosh/ddd-machines-dddeurope/raw/main/images/the-picture-that-explains-everything-projection.png) Aggregate data from events --- ## <span style="color: yellow">Aggregates</span>, <span style="color: lightgreen">projections</span> and <span style="color: orchid">policies</span> could be implemented as state machines --- ### <span style="color: yellow">Aggregates</span> ![aggregate-command-event](https://github.com/marcosh/ddd-machines-dddeurope/raw/main/images/the-picture-that-explains-everything-aggregate-command-event.png) From <span style="color: dodgerblue">commands</span> to <span style="color: orange">events</span> --- ### <span style="color: lightgreen">Projections</span> ![projection-event-read-model](https://github.com/marcosh/ddd-machines-dddeurope/raw/main/images/the-picture-that-explains-everything-projection-event-read-model.png) From <span style="color: orange">events</span> to <span style="color: lightyellow">read models</span> --- ### <span style="color: orchid">Policies</span> ![policy-event-command](https://github.com/marcosh/ddd-machines-dddeurope/raw/main/images/the-picture-that-explains-everything-policy-event-command.png) From <span style="color: orange">events</span> to <span style="color: dodgerblue">commands</span> --- ## How should we encode a state machine? --- ### Mealy machines ```haskell data Mealy state input output = Mealy { initialState :: state , action :: state -> input -> (state, output) } ``` --- ### [Mealy machines](https://hackage.haskell.org/package/machines-0.7.3/docs/Data-Machine-Mealy.html#t:Mealy) ```haskell newtype Mealy input output = Mealy { runMealy :: input -> (output, Mealy input output) } ``` --- ## Machines are composable --- ### Sequential ```haskell Sequential :: Mealy a b -> Mealy b c -> Mealy a c ``` ```mermaid %%{init: {'theme':'dark'}}%% stateDiagram-v2 direction LR a --> b b --> c ``` --- ### Parallel ```haskell Parallel :: Mealy a b -> Mealy c d -> Mealy (a, c) (b, d) ``` ```mermaid %%{init: {'theme':'dark'}}%% stateDiagram-v2 direction LR state fork <<fork>> (a,c) --> fork fork --> a fork --> c a --> b c --> d state join <<join>> b --> join d --> join join --> (b,d) ``` --- ### Alternative ```haskell Alternative :: Mealy a b -> Mealy c d -> Mealy (Either a c) (Either c d) ``` ```mermaid %%{init: {'theme':'dark'}}%% stateDiagram-v2 direction LR eitherac: Either a c state fork <<choice>> eitherac --> fork fork --> a fork --> c a --> b c --> d state join <<choice>> b --> join d --> join join --> eitherbd eitherbd: Either b d ``` --- ### Feedback ```haskell Feedback :: Mealy a [b] -> Mealy b [a] -> Mealy a [b] ``` ```mermaid %%{init: {'theme':'dark'}}%% stateDiagram-v2 direction LR a --> b: [] b --> a: [] ``` --- ## This encoding can only be run note: Once we defined a machine, the only way to extract information about it, is to run it, giving it inputs and observing its outputs --- ### What about documentation? --- ### What about invariant enforcement? --- ## Strengthening the type ```haskell data Machine (topology :: Topology vertex) input output ``` --- ### Topology ```mermaid %%{init: {'theme':'dark'}}%% stateDiagram-v2 NoData --> CollectedUserData CollectedUserData --> CollectedLoanDetailsFirst CollectedUserData --> ReceivedCreditBureauDataFirst CollectedLoanDetailsFirst --> CollectedAllData ReceivedCreditBureauDataFirst --> CollectedAllData ``` --- ### Adding the `Topology` in the type --- Allows us to enforce execution of allowed transitions --- Allows us to retrieve information about our state machine without running it --- ### But... --- ### Composition becomes harder ```haskell Sequential :: Machine t1 a b -> Machine t2 b c -> Machine ??? a c ``` --- Requires computation at the type level --- Breaks usage of standard typeclasses like `Arrow` or `Category` --- ## Can we get the best of both worlds? --- ```haskell data StateMachine input output where ``` --- ```haskell data StateMachine input output where Basic :: Machine topology input output -> StateMachine input output ``` --- ```haskell data StateMachine input output where ... Sequential :: StateMachine a b -> StateMachine b c -> StateMachine a c ``` --- ```haskell data StateMachine input output where ... Parallel :: StateMachine a b -> StateMachine c d -> StateMachine (a, c) (b, d) ``` --- ```haskell data StateMachine input output where ... Alternative :: StateMachine a b -> StateMachine c d -> StateMachine (Either a c) (Either b d) ``` --- ```haskell data StateMachine input output where ... Feedback :: StateMachine a [b] -> StateMachine b [a] -> StateMachine a [b] ``` --- ```mermaid %%{init: {'theme':'dark'}}%% graph TD A[Sequential] --> B[Parallel] A --> C[Alternative] B --> D[Machine 1] B --> E[Machine 2] C --> F[Machine 3] C --> G[Machine 4] ``` --- ## Composable --- ## Executable --- ## Representable --- For the `Basic` constructor, we demote information from the type level to the value level --- For the other contructor, we compose the information according to the constructor semantic --- ### We can draw our machines --- <img alt="risk manager" src="https://mermaid.ink/svg/pako:eNqlVF1PwjAU_SvkPmkyCGCBbQ8mCvqkxkj0wSwhzXad1a4lXUdAsv9u9-HcCMgMfepOz73n3PbubsGXAYILsaYaZ4yGikbd1dATHbNysEPDUGGY7bYFnK0KXDzIGdX0BZXG9b7zqeQcfY3Bc4yqJfVOUjFDTRmPb5mK9eGQJ_SRrTCYKgyYvk4U0iQTORJWKV1x_renenmdbvfyxMqa1EMJ29ffLvGpt7TfzyH3R--0hZ1_5E7rzbqUnPmbeqcWyOLsvMH-bepMqOAUJ2WGhn5DQckPY4VJ0VCp0MWciZDjYwXs8bqjXlHrOpEvhU91XaSEdmupOcrSlSywIEIVURaY3ztP4oF-xwg9cM02oOrTA0-khpcsA6N4Y95CKnDfKI_RAppoOd8IH1ytEvwhlSOiYi2peJWy8Q3uFtbgkv6oN7ZtZ0xscjEZWbABdziY9IjjODaZkMFw0HdIasFXHt_vGQrmFu6LkZRPpvQbS36R_g" height="600"> --- ## Demo time ![the hobbit](https://ready64.org/giochi/full/h/hobbit_01.png) [The Hobbit code](https://github.com/tweag/crem/blob/main/examples/Crem/Example/TheHobbit.hs) [The Hobbit map](https://mermaid.live/edit#pako:eNp9ksFuwjAMhl-l8rlFUNrS5rDLNmkHkKZtpykXi3gQkTqoTdEY4t0Xyqhox3aKrf-L_Sf2AZZWEQioHTp60LiqsIx2sWTJbw0zmbne0BMaI3lufYqsai9V1pj63hBWmleSX_SOWNGJWuja7Re2YYeaL-QzunV3C3c0LB5E0V1wVb8LW-FPI2d14KWft8iVvRvqtcWOvFm6rw5e2ktvtz31-MfzUD5_VBf_MgshlFSVqJUf4EFyEEhwaypJgvChwmojQfLRc81W-fk-Ku1sBeIDTU0hYOPs656XIFzV0AX6WYKO2iK_W9vLQRzgE0RUTIpsNB4XsyzN4jSEPYikiEdpPomLPI_9kR5D-GpvF6NZkuTTdDpJsmk-G3ucWjuL8wK2e3j8Bgbo5M8) note: `cabal run hobbit-game` `cabal run hobbit-map` --- ## That's all!
{"metaMigratedAt":"2023-06-17T22:13:16.174Z","metaMigratedFrom":"YAML","title":"State machines with [`crem`](https://github.com/tweag/crem)","breaks":true,"slideOptions":"{\"progress\":true,\"controls\":false,\"slideNumber\":false}","contributors":"[{\"id\":\"0893b955-bc9c-4ecc-b38c-6072b5bc1ecd\",\"add\":10403,\"del\":711}]"}
    365 views