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
310
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
220
CodeMash 2020: Solving the Boolean Identity Crisis
jfairbank
1
160
CodeMash 2020: Practical Functional Programming
jfairbank
1
330
Connect.Tech 2019: Practical Functional Programming
jfairbank
0
370
Connect.Tech 2019: Solving the Boolean Identity Crisis
jfairbank
0
190
Lambda Squared 2019: Solving the Boolean Identity Crisis
jfairbank
0
140
All Things Open 2018: Practical Functional Programming
jfairbank
2
270
Connect.Tech 2018: Effective React Testing
jfairbank
1
180
Fluent Conf 2018: Building web apps with Elm Tutorial
jfairbank
2
870
Other Decks in Programming
See All in Programming
組織もソフトウェアも難しく考えない、もっとシンプルな考え方で設計する #phpconfuk
o0h
PRO
10
4.5k
目的で駆動する、AI時代のアーキテクチャ設計 / purpose-driven-architecture
minodriven
7
2.5k
イベントストーミングのはじめかた / Getting Started with Event Storming
nrslib
1
610
AIと協働し、イベントソーシングとアクターモデルで作る後悔しないアーキテクチャ Regret-Free Architecture with AI, Event Sourcing, and Actors
tomohisa
2
2.6k
歴史から学ぶ「Why PHP?」 PHPを書く理由を改めて理解する / Learning from History: “Why PHP?” Rediscovering the Reasons for Writing PHP
seike460
PRO
0
160
Chart.jsで長い項目を表示するときのハマりどころ
yumechi
0
120
JEP 496 と JEP 497 から学ぶ耐量子計算機暗号入門 / Learning Post-Quantum Crypto Basics from JEP 496 & 497
mackey0225
2
390
オフライン対応!Flutterアプリに全文検索エンジンを実装する @FlutterKaigi2025
itsmedreamwalker
2
220
仕様がそのままテストになる!Javaで始める振る舞い駆動開発
ohmori_yusuke
8
4.5k
JJUG CCC 2025 Fall: Virtual Thread Deep Dive
ternbusty
3
450
Core MIDI を勉強して作曲用の電子ピアノ作ってみた!
hypebeans
0
110
Flutterアプリ運用の現場で役立った監視Tips 5選
ostk0069
1
480
Featured
See All Featured
Learning to Love Humans: Emotional Interface Design
aarron
274
41k
Side Projects
sachag
455
43k
Building an army of robots
kneath
306
46k
Art, The Web, and Tiny UX
lynnandtonic
303
21k
Let's Do A Bunch of Simple Stuff to Make Websites Faster
chriscoyier
508
140k
CoffeeScript is Beautiful & I Never Want to Write Plain JavaScript Again
sstephenson
162
15k
YesSQL, Process and Tooling at Scale
rocio
174
15k
Large-scale JavaScript Application Architecture
addyosmani
514
110k
Why You Should Never Use an ORM
jnunemaker
PRO
60
9.6k
Fashionably flexible responsive web design (full day workshop)
malarkey
407
66k
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.2k
Six Lessons from altMBA
skipperchong
29
4.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