👩💻
🏠
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),
};