Skip to content

Instantly share code, notes, and snippets.

@mattiamanzati
Created January 26, 2020 11:29
Show Gist options
  • Save mattiamanzati/4d3cfde5cd0820644a70c50da6156689 to your computer and use it in GitHub Desktop.
Save mattiamanzati/4d3cfde5cd0820644a70c50da6156689 to your computer and use it in GitHub Desktop.
import {cmd} from 'elm-ts'
// TODO: See what's Option from functional programming
type Option<A> = {
type: 'none'
} | { type: 'some', value: A}
/**
* This is the users updater, it stores a list of user objects and exposes selectors to get them
*/
export type User = { id: number, name: string, defaultTodoDone: boolean}
export type UserModel = User[]
export type UserMsg = { type: 'userFetched', payload: User}
export function msgUserFetched(user: User): UserMsg{
return { type: 'userFetched', payload: user }
}
export function userUpdater(msg: UserMsg, model: UserModel): UserModel{
switch(msg.type){
case "userFetched":
return model.concat([msg.payload])
default:
return model
}
}
export function getUserById(model: UserModel): (id: number) => Option<User> {
return id => {
const foundUsers = model.filter(user => user.id === id)
if(foundUsers.length === 0) return { type: 'none'}
return { type: 'some', value: foundUsers[0]}
}
}
/**
* Inside another module, we "lift" the definition of the reducer/updater, in order to take first the definition of capabilities.
* Capabilities are the functionalities required by a reducer to run.
* This is some sort of Dependency Injection, and also works great in order to decouple reducers implementation to test them.
*/
export type Todo = { id: number, name: string, user_id: number, done: boolean}
export type TodoModel = Todo[]
export type TodoMsg = { type: 'todoAdded', payload: { user_id: number, name: string }}
export function msgTodoAdded(user_id: number, name: string): TodoMsg {
return { type: 'todoAdded', payload: {user_id, name} }
}
type TodoCapabilities = {
getUserById: (id: number) => Option<User>
}
function createTodoUpdater(capabilities: TodoCapabilities) {
return function updater(msg: TodoMsg, model: TodoModel): TodoModel {
switch(msg.type){
case "todoAdded":
const todoUser = capabilities.getUserById(msg.payload.user_id)
// if user is invalid, reject the update
if(todoUser.type === "none") return model
// else, create the todo using user defaults
const todo = {
id: model.reduce((id, todo) => Math.max(id, todo.id), 1), // Just use max id + 1,
name: msg.payload.name,
user_id: todoUser.value.id,
done: todoUser.value.defaultTodoDone
}
return model.concat([todo])
default:
return model
}
}
}
/**
* Ok, now we have two different models and we need to wire the updaters!
*/
type AppModel = {
users: UserModel,
todos: TodoModel
}
type AppMsg = UserMsg | TodoModel
export function updater(msg: AppMsg, model: AppModel): AppModel {
// users section
const users = userUpdater(msg as any, model.users) // TODO: better typing
// todos section
const todoCapabilities: TodoCapabilities = {
getUserById: id => getUserById(model.users)(id)
}
const todos = createTodoUpdater(todoCapabilities)(msg as any, model.todos)
// merge result
return { users, todos }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment