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
340
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
210
CodeMash 2020: Practical Functional Programming
jfairbank
1
360
Connect.Tech 2019: Practical Functional Programming
jfairbank
0
430
Connect.Tech 2019: Solving the Boolean Identity Crisis
jfairbank
0
240
Lambda Squared 2019: Solving the Boolean Identity Crisis
jfairbank
0
190
All Things Open 2018: Practical Functional Programming
jfairbank
2
280
Connect.Tech 2018: Effective React Testing
jfairbank
1
220
Fluent Conf 2018: Building web apps with Elm Tutorial
jfairbank
2
930
Other Decks in Programming
See All in Programming
FDEが実現するAI駆動経営の現在地
gonta
2
190
【やさしく解説 設計編・中級 #1】一つの車に、運転手は一人 ~ある倉庫システムの事例から~
panda728
PRO
0
190
型も通る、synthも通る、それでも危ない 〜AIのCDKの権限とコストを機械で検証する〜 / It Passes Type Checks, It Passes Synth Checks, but It’s Still Risky — Automatically Verifying Permissions and Costs in AI’s CDK —
seike460
PRO
1
390
なぜ関数型プログラミングで「型」と「証明」が語られるのか #fp_matsuri
kajitack
3
990
言語を使う側から、作る側へ。 自作 Lisp で得た新たな気づき。
andpad
0
130
Laravel Boostに学ぶ、AIにPHPを書かせる技術 〜OSSの実装から蒸留するエージェント制御の王道〜
kentaroutakeda
3
510
ビデオ通話が繋がる0.2秒で何が起きているのか
supurazako
2
150
【SRE NEXT 2026 Lunch Session】一人目専任SREの立ち上げを加速する ― AIと進めたオンボーディングで2分を0.04秒にした話
pkshadeck
PRO
0
2.9k
これからAgentCoreを触る方へトレンドはGatewayです
har1101
6
500
才能?センス?知らん、 続けたもん勝ちだ。-- 結婚・出産・癌を越えてなお、私がプロダクトを創り続ける理由
16bitidol
2
890
Hatena Engineer Seminar #37「言語モデルの活用に関する研究」
slashnephy
0
530
鹿野さんに聞く!『TypeScriptコードレシピ集』で磨く実践力
tonkotsuboy_com
4
1.1k
Featured
See All Featured
Prompt Engineering for Job Search
mfonobong
0
380
Building Adaptive Systems
keathley
44
3.1k
Paper Plane
katiecoart
PRO
2
52k
A brief & incomplete history of UX Design for the World Wide Web: 1989–2019
jct
2
420
Crafting Experiences
bethany
1
230
Un-Boring Meetings
codingconduct
0
350
Exploring the Power of Turbo Streams & Action Cable | RailsConf2023
kevinliebholz
37
6.5k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.6k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
254
22k
Faster Mobile Websites
deanohume
310
32k
Navigating Team Friction
lara
192
16k
Making Projects Easy
brettharned
120
6.7k
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