A road to state machines
Me
- Margarita, @rita_krutikova
- 🇺🇦 Ukraine → Sweden, Gothenburg 🇸🇪 🌧
- React + Typescript, Reason,
- GraphQL + Apollo
- F#, C#
- Functional programming, F#, ReasonML
- dev.to/margaretkrutikova

👩💻


🏠
Before we start
- Assumes some basic knowledge of functional concepts
	- variants (DU)
- pattern matching
- pure functions/immutability
 
- Examples in ReasonML
- Beginner intro to state machines
Agenda
- A few words about state machine
- Pack your bag, we are driving out
- State machine recap
- Benefits
- Recognising state machines
Machine?
- mathematical concept
- design pattern in software development
- 
	domain modelling - 
		represent data and behaviour in code 
 
- 
		

A road to state machine #1
type user = {
  isLoggedIn: bool,
};type userData = {
  userName: string,
  email: string,
};type user = {
  isLoggedIn: bool,
  userData: option(userData),
};type user = {
  isLoggedIn: bool,
  userData: option(userData),
  tempUserName: option(string),
};A road to state machine #2
let setTempUserName = (tempUserName, user) => 
  {...user, tempUserName: Some(tempUserName)};let logIn = (userData, user) => {
  ...user, 
  isLoggedIn: true, 
  userData: Some(userData),
};let logOut = user => {...user, isLoggedIn: false};let postQuestion = (question, user) => {
  let authorName =
    switch (user.tempUserName, user.userData) {
    | (Some(tempName), _) => tempName
    | (_, Some(userData)) => userData.userName
    | _ => failwith("You can't post questions!")
    };
  post(question, authorName)
};A road to state machine #3
A road to state machine #3
let setTempUserName = (tempUserName, user) => 
  {...user, tempUserName: Some(tempUserName)};Is not allowed when logged-in! 🐞
#1 What, I just set temporary name while being logged-in! Now I am stuck with it! 😠
A road to state machine #3
let logIn = (userData, user) => {
  ...user, isLoggedIn: true, userData: Some(userData),
};Temporary name is still there! 🐞
#2 Wait, I logged in, why does it show my temporary name? 🤨
A road to state machine #3
How about resetting userData? 🐞
let logOut = user => {...user, isLoggedIn: false};#3 Haha, I logged out and could still post with my user name! 😆
What went wrong?
A road to state machine #4
type user = {
  isLoggedIn: bool,
  userData: option(userData),
  tempUserName: option(string),
};- Many invalid combinations
- User can only be anonymous OR temporary OR logged in
One of the states at a time!
What went wrong?
A road to state machine #4
- States are implicit
- Update properties that belong to one state?
let logOut = user => {...user, isLoggedIn: false};- User inputs are implicit too
- No connection between the states and inputs!
A road to state machine #5
type user =
  | Anonymous
  | TemporaryUser(string)
  | LoggedInUser(userData)type userInput =
  | SetTemporaryName(string)
  | LogIn(userData)
  | LogOut;Explicit state and inputs

A road to state machine #5
nextState = transition(currentState, input)
combinations = states * inputs
Transition function
A road to state machine #6
Visualising
Anonymous
Temporary user
Logged in user
SetTempName
Log in
Log in
Log out
A road to state machine #6
Visualising
A road to state machine #7
let transition = (user, input) => {
  switch (user) {
  | Anonymous =>
    switch (input) {
    | SetTemporaryName(name) => TemporaryUser(name)
    | LogIn(data) => LoggedInUser(data)
    | LogOut => user
    }
  | TemporaryUser(_) =>
    switch (input) {
    | LogIn(data) => LoggedInUser(data)
    | SetTemporaryName(_) | LogOut => user
    }
  | LoggedInUser(_) => ...
  };
};Implementing transition

A road to state machine #7
let transition = (user, input) => {
  switch (user, input) {
  | (Anonymous, SetTemporaryName(tempName)) => TemporaryUser(tempName)
  | (Anonymous, LogIn(userData)) => LoggedInUser(userData)
  | (TemporaryUser(_), LogIn(userData)) => LoggedInUser(userData)
  | (LoggedInUser(_), LogOut) => Anonymous
  | _ => user
  };
};Avoid _ pattern match

A road to state machine #8
Use as reducer
[@react.component]
let make = () => {
  let (state, dispatch) = 
    React.useReducer(transition, Anonymous);
  
  <button onClick={_ => dispatch(LogOut)}>
    {React.string("Log out")}
  </button>;
};
A road to state machine #9
Use in your UI
<div>
  {(switch (state) {
    | Anonymous => "You can't post questions."
    | TemporaryUser(userName) =>
      "Post with your temporary name: " ++ userName
    | LoggedInUser({userName}) => 
      "Post with your user name:" ++ userName
  }) |> React.string}
</div>;
State machine Recap #1
- States - variant (discriminated union)
- Only one state at a time
- Start in initial state
- Input (event) - variant (DU)
- Transition - new state from current state + input
- It is just a design pattern (no lib required)
- Can be introduced just in a small part of your app
- Can be used in any language/environment
- Better support in functional languages 
	- Variants/pattern matching
- Compiler warnings - exhaustive match
 
State machine Recap #2
Benefits
- Protect against invalid states
- Protect against invalid state transitions
- Account for all edge cases (trivial...)
- Better understand the domain
- Discover potential problems in spec of business rules
- IT IS EASY - follow the pattern, focus on the behaviour
- Fun to visualize


Use cases
- Boolean flags
	- isValid, isLoading, isError,
 
- Options
	- error, validResponse etc.
 
- A flood of If-checks
type apiRequest('t) = {
  isLoading: bool,
  data: option('t),
  error: option(string),
};
Thank you! ❤️

Questions?



A road to state machines
By margaretkru
A road to state machines
- 586
 
   
   
  