👩💻
🏠
domain modelling
represent data and behaviour in code
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),
};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)
};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! 😠
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? 🤨
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?
type user = {
  isLoggedIn: bool,
  userData: option(userData),
  tempUserName: option(string),
};One of the states at a time!
What went wrong?
let logOut = user => {...user, isLoggedIn: false};type user =
  | Anonymous
  | TemporaryUser(string)
  | LoggedInUser(userData)type userInput =
  | SetTemporaryName(string)
  | LogIn(userData)
  | LogOut;Explicit state and inputs
nextState = transition(currentState, input)
combinations = states * inputs
Transition function
Visualising
Anonymous
Temporary user
Logged in user
SetTempName
Log in
Log in
Log out
Visualising
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
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
Use as reducer
[@react.component]
let make = () => {
  let (state, dispatch) = 
    React.useReducer(transition, Anonymous);
  
  <button onClick={_ => dispatch(LogOut)}>
    {React.string("Log out")}
  </button>;
};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>;type apiRequest('t) = {
  isLoading: bool,
  data: option('t),
  error: option(string),
};