Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
Codemash 2017: Toward a Better Front-end Archit...
Search
Jeremy Fairbank
January 12, 2017
Programming
350
1
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
Codemash 2017: Toward a Better Front-end Architecture: Elm
Jeremy Fairbank
January 12, 2017
More Decks by Jeremy Fairbank
See All by Jeremy Fairbank
Connect.Tech 2020: Advanced Cypress Testing
jfairbank
1
260
CodeMash 2020: Solving the Boolean Identity Crisis
jfairbank
1
230
CodeMash 2020: Practical Functional Programming
jfairbank
1
370
Connect.Tech 2019: Practical Functional Programming
jfairbank
0
450
Connect.Tech 2019: Solving the Boolean Identity Crisis
jfairbank
0
270
Lambda Squared 2019: Solving the Boolean Identity Crisis
jfairbank
0
200
All Things Open 2018: Practical Functional Programming
jfairbank
2
300
Connect.Tech 2018: Effective React Testing
jfairbank
1
230
Fluent Conf 2018: Building web apps with Elm Tutorial
jfairbank
2
950
Other Decks in Programming
See All in Programming
re:Inventに行く前に知っておきたい現地参加のノウハウ
nokomoro3
0
410
R8 の設定見直しでアプリサイズ削減
andpad
0
210
Event is what happens
moro
1
320
パスワード再設定メールのURLについて知っておこう
uyuki234
0
140
Kubernetes祭り #2 OP/ED
bells17
0
110
コードで差別化できない時代の ソフトウェアエンジニアリング / Software Engineering in an Era Where Code Doesn’t Differentiate
kishida
2
170
GemmaをJevのように使ってみる / Use Gemma like Jev
kishida
5
780
AWS CDKのカスタムリソースでContinuum(旧Security Agent)を実装した話 | Implementing Continuum (formerly Security Agent) via AWS CDK Custom Resource
akihisaikeda
2
150
IBM Bob Dojo #1 仕様駆動開発入門
oniak3ibm
PRO
0
380
コーディング自動化の次へ。人の介入をゼロにするファクトリーとサポートAIによる実践
timakin
0
1.5k
Workers Cache を知る
syumai
0
360
Starting & Sustaining Code-Based E2E Testing for Non-Coding QA Teams( #jasstniigata )
teyamagu
PRO
1
980
Featured
See All Featured
Crafting Experiences
bethany
1
380
Refactoring Trust on Your Teams (GOTO; Chicago 2020)
rmw
35
3.8k
Making the Leap to Tech Lead
cromwellryan
135
10k
Building Applications with DynamoDB
mza
96
7.2k
Breaking role norms: Why Content Design is so much more than writing copy - Taylor Woolridge
uxyall
1
450
A Tale of Four Properties
chriscoyier
163
24k
Getting science done with accelerated Python computing platforms
jacobtomlinson
2
530
Creating an realtime collaboration tool: Agile Flush - .NET Oxford
marcduiker
35
2.6k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
56k
Exploring anti-patterns in Rails
aemeredith
4
530
Leadership Guide Workshop - DevTernity 2021
reverentgeek
1
400
How to build an LLM SEO readiness audit: a practical framework
nmsamuel
2
940
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