Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
Codemash 2017: Toward a Better Front-end Archit...
Search
Jeremy Fairbank
January 12, 2017
Programming
1
320
Codemash 2017: Toward a Better Front-end Architecture: Elm
Jeremy Fairbank
January 12, 2017
Tweet
Share
More Decks by Jeremy Fairbank
See All by Jeremy Fairbank
Connect.Tech 2020: Advanced Cypress Testing
jfairbank
1
230
CodeMash 2020: Solving the Boolean Identity Crisis
jfairbank
1
170
CodeMash 2020: Practical Functional Programming
jfairbank
1
340
Connect.Tech 2019: Practical Functional Programming
jfairbank
0
390
Connect.Tech 2019: Solving the Boolean Identity Crisis
jfairbank
0
200
Lambda Squared 2019: Solving the Boolean Identity Crisis
jfairbank
0
150
All Things Open 2018: Practical Functional Programming
jfairbank
2
270
Connect.Tech 2018: Effective React Testing
jfairbank
1
190
Fluent Conf 2018: Building web apps with Elm Tutorial
jfairbank
2
890
Other Decks in Programming
See All in Programming
CSC307 Lecture 01
javiergs
PRO
0
670
疑似コードによるプロンプト記述、どのくらい正確に実行される?
kokuyouwind
0
250
[AI Engineering Summit Tokyo 2025] LLMは計画業務のゲームチェンジャーか? 最適化業務における活⽤の可能性と限界
terryu16
2
340
副作用をどこに置くか問題:オブジェクト指向で整理する設計判断ツリー
koxya
1
450
インターン生でもAuth0で認証基盤刷新が出来るのか
taku271
0
170
AI Agent Tool のためのバックエンドアーキテクチャを考える #encraft
izumin5210
6
1.7k
Cap'n Webについて
yusukebe
0
170
[AtCoder Conference 2025] LLMを使った業務AHCの上⼿な解き⽅
terryu16
6
1k
大規模Cloud Native環境におけるFalcoの運用
owlinux1000
0
250
re:Invent 2025 トレンドからみる製品開発への AI Agent 活用
yoskoh
0
660
Canon EOS R50 V と R5 Mark II 購入でみえてきた最近のデジイチ VR180 事情、そして VR180 静止画に活路を見出すまで
karad
0
140
Patterns of Patterns
denyspoltorak
0
630
Featured
See All Featured
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
1.7k
Gemini Prompt Engineering: Practical Techniques for Tangible AI Outcomes
mfonobong
2
260
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Building Experiences: Design Systems, User Experience, and Full Site Editing
marktimemedia
0
380
Producing Creativity
orderedlist
PRO
348
40k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
Side Projects
sachag
455
43k
The Success of Rails: Ensuring Growth for the Next 100 Years
eileencodes
47
7.9k
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
2
81
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.3k
Reality Check: Gamification 10 Years Later
codingconduct
0
2k
The Power of CSS Pseudo Elements
geoffreycrofte
80
6.1k
Transcript
TOWARD A BETTER |> FRONT END |> ARCHITECTURE elm Jeremy
Fairbank blog.jeremyfairbank.com tw: @elpapapollo / gh: jfairbank
sigient.com Your website, SimplyBuilt. simplybuilt.com
None
None
None
× two-way data binding mvc
× ✓ two-way data binding unidirectional mvc components
None
null undefined is not a function
let name = 'Jeremy'; export function getName() { return name;
} export function setName(newName) { name = newName; } Impure and Mutable
Unsafe APIs axios.get(`/user/${id}`) .then((user) => { console.log(user.name); }) .catch((error) =>
{ console.log(error); });
Unsafe APIs axios.get(`/user/${id}`) .then((user) => { console.log(user.name); }) .catch((error) =>
{ console.log(error); });
Error Handling function mayThrow() { throw new Error('Whoops...') } function
unsafe() { try { mayThrow(); } catch (e) { handleError(e); } } Call Stack ×
axios.get(`/user/${id}`) .then((user) => { console.log(user.name); }); Error Handling
axios.get(`/user/${id}`) .then((user) => { console.log(user.name); }); Error Handling catch?
elm
No runtime errors in practice. No null. No undefined is
not a function. - guide.elm-lang.org
confidence
confidence Static typing Expressive Terse Pure Unidirectional Architecture Immutable
functional
greet name = "Hello, " ++ name add x y
= x + y greet "Codemash" -- Hello, Codemash add 2 3 -- 5 functional
greet name = "Hello, " ++ name add x y
= x + y greet "Codemash" -- Hello, Codemash add 2 3 -- 5 functional
greet name = "Hello, " ++ name add x y
= x + y greet "Codemash" -- Hello, Codemash add 2 3 -- 5 functional
greet name = "Hello, " ++ name add x y
= x + y greet "Codemash" -- Hello, Codemash add 2 3 -- 5 functional
greet name = "Hello, " ++ name add x y
= x + y greet "Codemash" -- Hello, Codemash add 2 3 -- 5 functional
greet name = "Hello, " ++ name add x y
= x + y greet "Codemash" -- Hello, Codemash add 2 3 -- 5 functional
pure
pure add x y = x + y add 2
3 -- 5 add 2 3 -- 5 add 2 3 -- 5
pure add x y = x + y add 2
3 -- 5 add 2 3 -- 5 add 2 3 -- 5 Referentially Transparent
function fetchUser(id) { const url = `/user/${id}`; return axios.get(url); }
pure ✓ × fetchUser id = let url = "/user/" ++ (toString id) request = Http.get url userDecoder in Http.send LoadUser request
declarative Declare what the desired result is
function doubleNumbers(numbers) { const doubled = []; const l =
numbers.length; for (let i = 0; i < l; i++) { doubled.push(numbers[i] * 2); } return doubled; } doubleNumbers([1, 2, 3, 4, 5]); // [2, 4, 6, 8, 10] imperative
function doubleNumbers(numbers) { const doubled = []; const l =
numbers.length; for (let i = 0; i < l; i++) { doubled.push(numbers[i] * 2); } return doubled; } doubleNumbers([1, 2, 3, 4, 5]); // [2, 4, 6, 8, 10] imperative ×
myList = [1, 2, 3, 4, 5] double n =
n * 2 doubleNumbers list = List.map double list doubleNumbers myList -- [2, 4, 6, 8, 10] declarative
myList = [1, 2, 3, 4, 5] double n =
n * 2 doubleNumbers list = List.map double list doubleNumbers myList -- [2, 4, 6, 8, 10] declarative
myList = [1, 2, 3, 4, 5] double n =
n * 2 doubleNumbers list = List.map double list doubleNumbers myList -- [2, 4, 6, 8, 10] declarative
myList = [1, 2, 3, 4, 5] double n =
n * 2 doubleNumbers list = List.map double list doubleNumbers myList -- [2, 4, 6, 8, 10] declarative
currying Creating building blocks
currying add x y z = x + y +
z add 1 2 3 -- 6 ((add 1) 2) 3 -- 6 add1 = add 1 add3 = add1 2 add1 2 3 -- 6 add3 3 -- 6
currying add x y z = x + y +
z add 1 2 3 -- 6 ((add 1) 2) 3 -- 6 add1 = add 1 add3 = add1 2 add1 2 3 -- 6 add3 3 -- 6
currying add x y z = x + y +
z add 1 2 3 -- 6 ((add 1) 2) 3 -- 6 add1 = add 1 add3 = add1 2 add1 2 3 -- 6 add3 3 -- 6
currying add x y z = x + y +
z add 1 2 3 -- 6 ((add 1) 2) 3 -- 6 add1 = add 1 add3 = add1 2 add1 2 3 -- 6 add3 3 -- 6
currying double n = n * 2 doubleNumbers list =
List.map double list
currying double n = n * 2 doubleNumbers = List.map
double
currying double = ((*) 2) doubleNumbers = List.map double
currying double = ((*) 2) doubleNumbers = List.map double
currying doubleNumbers = List.map ((*) 2)
list = List.range 1 10 square n = n *
n List.map square (List.filter ((<) 6) (List.map ((*) 2) list)) piping
list = List.range 1 10 square n = n *
n List.map square (List.filter ((<) 6) (List.map ((*) 2) list)) piping
piping list = List.range 1 10 square n = n
* n list |> List.map ((*) 2) |> List.filter ((<) 6) |> List.map square
piping list = List.range 1 10 square n = n
* n list |> List.map ((*) 2) |> List.filter ((<) 6) |> List.map square
|> List.map ((*) 2) |> List.filter ((<) 6) |> List.map
square piping list
|> List.map ((*) 2) |> List.filter ((<) 6) |> List.map
square piping list doubled = List.map ((*) 2) list
|> List.filter ((<) 6) |> List.map square piping doubled =
List.map ((*) 2) list doubled
|> List.filter ((<) 6) |> List.map square piping doubled =
List.map ((*) 2) list doubled filtered = List.filter ((<) 6) mapped
piping doubled = List.map ((*) 2) list filtered = List.filter
((<) 6) mapped |> List.map square filtered
piping doubled = List.map ((*) 2) list filtered = List.filter
((<) 6) mapped |> List.map square filtered squared = List.map square filtered
strong static typing Creating and adhering to contracts
life : Int life = 42 greeting : String greeting
= "Hello World" isTrue : Bool isTrue = True numbers : List Int numbers = [1, 2, 3] strong static typing
strong static typing greet : String -> String greet name
= "Hello, " ++ name add : Int -> Int -> Int add x y = x + y
strong static typing greet : String -> String greet name
= "Hello, " ++ name add : Int -> Int -> Int add x y = x + y
strong static typing greet : String -> String greet name
= "Hello, " ++ name add : Int -> Int -> Int add x y = x + y
strong static typing greet : String -> String greet name
= "Hello, " ++ name add : Int -> (Int -> Int) add x y = x + y
greet True add 2 "5"
dog : ( String, Int ) dog = ( "Tucker",
11 ) name = Tuple.first dog -- "Tucker" age = Tuple.second dog -- 11 tuples
dog : ( String, Int ) dog = ( "Tucker",
11 ) name = Tuple.first dog -- "Tucker" age = Tuple.second dog -- 11 tuples
dog : ( String, Int ) dog = ( "Tucker",
11 ) name = Tuple.first dog -- "Tucker" age = Tuple.second dog -- 11 tuples
dog : ( String, Int ) dog = ( "Tucker",
11 ) name = Tuple.first dog -- "Tucker" age = Tuple.second dog -- 11 tuples
records dog : { name : String, age : Int,
breed : String } dog = { name = "Tucker" , age = 11 , breed = "Sheltie" } dog.name -- "Tucker" dog.age -- 11 dog.breed -- "Sheltie" .name dog -- "Tucker"
records dog : { name : String, age : Int,
breed : String } dog = { name = "Tucker" , age = 11 , breed = "Sheltie" } dog.name -- "Tucker" dog.age -- 11 dog.breed -- "Sheltie" .name dog -- "Tucker"
records dog : { name : String, age : Int,
breed : String } dog = { name = "Tucker" , age = 11 , breed = "Sheltie" } dog.name -- "Tucker" dog.age -- 11 dog.breed -- "Sheltie" .name dog -- "Tucker"
records dog : { name : String, age : Int,
breed : String } dog = { name = "Tucker" , age = 11 , breed = "Sheltie" } dog.name -- "Tucker" dog.age -- 11 dog.breed -- "Sheltie" .name dog -- "Tucker"
records dog : { name : String, age : Int,
breed : String } dog = { name = "Tucker" , age = 11 , breed = "Sheltie" } dog.name -- "Tucker" dog.age -- 11 dog.breed -- "Sheltie" .name dog -- "Tucker"
type alias Dog = { name : String , age
: Int , breed : String } dog : Dog dog = { name = "Tucker" , age = 11 , breed = "Sheltie" } aliases
type alias Dog = { name : String , age
: Int , breed : String } dog : Dog dog = { name = "Tucker" , age = 11 , breed = "Sheltie" } aliases
type alias Dog = { name : String , age
: Int , breed : String } dog : Dog dog = { name = "Tucker" , age = 11 , breed = "Sheltie" } aliases
aliases type alias Dog = { name : String ,
age : Int , breed : String } dog : Dog dog = Dog "Tucker" 11 "Sheltie"
immutable Create state, don’t mutate it
immutable dog = Dog "Tucker" 11 "Sheltie" olderDog = {
dog | age = dog.age + 1 } dog.age -- 11 olderDog.age -- 12
immutable dog = Dog "Tucker" 11 "Sheltie" olderDog = {
dog | age = dog.age + 1 } dog.age -- 11 olderDog.age -- 12
immutable dog = Dog "Tucker" 11 "Sheltie" olderDog = {
dog | age = dog.age + 1 } dog.age -- 11 olderDog.age -- 12
immutable dog = Dog "Tucker" 11 "Sheltie" olderDog = {
dog | age = dog.age + 1 } dog.age -- 11 olderDog.age -- 12
immutable dog = Dog "Tucker" 11 "Sheltie" olderDog = {
dog | age = dog.age + 1 } dog.age -- 11 olderDog.age -- 12
union types type alias Dog = { name : String
, age : Int , breed : String }
union types type alias Dog = { name : String
, age : Int , breed : String }
type Breed = Sheltie | Corgi | GoldenRetriever | Mix
Breed Breed union types type alias Dog = { name : String , age : Int , breed : String }
type Breed = Sheltie | Corgi | GoldenRetriever | Mix
Breed Breed union types type alias Dog = { name : String , age : Int , breed : Breed }
type Breed = Sheltie | Corgi | GoldenRetriever | Mix
Breed Breed union types
type Breed = Sheltie | Corgi | GoldenRetriever | Mix
Breed Breed union types
type Breed = Sheltie | Corgi | GoldenRetriever | Mix
Breed Breed union types
type Breed = Sheltie | Corgi | GoldenRetriever | Mix
Breed Breed dog1 = Dog "Tucker" 11 Sheltie dog2 = Dog "Sally" 5 Corgi dog3 = Dog "Rover" 1 Mix
type Breed = Sheltie | Corgi | GoldenRetriever | Mix
Breed Breed dog1 = Dog "Tucker" 11 Sheltie dog2 = Dog "Sally" 5 Corgi dog3 = Dog "Rover" 1 Mix
type Breed = Sheltie | Corgi | GoldenRetriever | Mix
Breed Breed dog1 = Dog "Tucker" 11 Sheltie dog2 = Dog "Sally" 5 Corgi dog3 = Dog "Rover" 1 Mix
type Breed = Sheltie | Corgi | GoldenRetriever | Mix
Breed Breed dog1 = Dog "Tucker" 11 Sheltie dog2 = Dog "Sally" 5 Corgi dog3 = Dog "Rover" 1 Mix ?
dog1 = Dog "Tucker" 11 Sheltie dog2 = Dog "Sally"
5 Corgi dog3 = Dog "Rover" 1 (Mix Sheltie Corgi) type Breed = Sheltie | Corgi | GoldenRetriever | Mix Breed Breed
null
type Maybe a = Just a | Nothing safety
type Maybe a = Just a | Nothing safety
divide : number -> number -> Maybe Float divide x
y = if y == 0 then Nothing else Just (x / y) divide 4 2 -- Just 2 divide 4 0 -- Nothing
divide : number -> number -> Maybe Float divide x
y = if y == 0 then Nothing else Just (x / y) divide 4 2 -- Just 2 divide 4 0 -- Nothing
divide : number -> number -> Maybe Float divide x
y = if y == 0 then Nothing else Just (x / y) divide 4 2 -- Just 2 divide 4 0 -- Nothing
divide : number -> number -> Maybe Float divide x
y = if y == 0 then Nothing else Just (x / y) divide 4 2 -- Just 2 divide 4 0 -- Nothing
divide : number -> number -> Maybe Float divide x
y = if y == 0 then Nothing else Just (x / y) divide 4 2 -- Just 2 divide 4 0 -- Nothing
case divide 4 2 of Just n -> "Result is
" ++ (toString n) Nothing -> "No Result"
case divide 4 2 of Just n -> "Result is
" ++ (toString n) Nothing -> "No Result"
case divide 4 2 of Just n -> "Result is
" ++ (toString n) Nothing -> "No Result"
case divide 4 2 of Just n -> "Result is
" ++ (toString n) Nothing -> "No Result"
case half 4 of Just n -> "Result is "
++ (toString n)
Compiler REPL Dev Server built-in tooling Packages
built-in framework elm + vs.
architecture model-view-update unidirectional organized communication
elm app model
elm app model
elm app model
elm app model Commands HTTP Dates Random #’s
elm app model Commands HTTP Dates Random #’s Subscriptions WebSockets
Browser Window Mouse Position
elm app model Commands HTTP Dates Random #’s Subscriptions WebSockets
Browser Window Mouse Position Events Text Input Mouse Click
elm app model Commands HTTP Dates Random #’s Subscriptions WebSockets
Browser Window Mouse Position Events Text Input Mouse Click Tasks
elm app model Commands HTTP Dates Random #’s Subscriptions WebSockets
Browser Window Mouse Position Events Text Input Mouse Click Tasks
elm app
elm app model
elm app model Update
elm app model Update View
model Update View elm
model Update View elm
model Update View elm
model Update View elm !
model Update View elm !
model Update View elm
model Update View elm
model Update View elm
model Update View elm
github.com/jfairbank/arch-elm Demo
THANKS! Jeremy Fairbank blog.jeremyfairbank.com tw: @elpapapollo / gh: jfairbank |>
elm-lang.org |> guide.elm-lang.org |> github.com/jfairbank/arch-elm