Codemotion Roma 2015 - Reactive Extensions (Rx) has brought reactive programming to the mainstream in recent years with successful adoption in languages such as C#, Java and JavaScript. But have you ever wondered what Rx will look like as a language? Elm is a new programming language based on the idea of Functional Reactive Programming (FRP). Elm lets you create highly interactive web applications without all the messy callbacks tangling around shared states. In this talk Yan Cui will give a gentle introduction to Elm and FRP and finish off with a live demo building a web-based game from scratch.
13. ROME 27-28 march 2015 β Yan Cui @theburningmonk
Reactive is Dead,
long live composing side effects.
bit.ly/1sb5hCu
14. ROME 27-28 march 2015 β Yan Cui @theburningmonk
βOne thing Iβm discovering is
that transforming data is
easier to think about than
maintaining state.β
!
- Dave Thomas
15. ROME 27-28 march 2015 β Yan Cui @theburningmonk
let y = f(x)
Imperative Functional
x.f()
16. ROME 27-28 march 2015 β Yan Cui @theburningmonk
mutation
let y = f(x)
Imperative Functional
x.f()
18. ROME 27-28 march 2015 β Yan Cui @theburningmonk
Move Up
Move Down
19. ROME 27-28 march 2015 β Yan Cui @theburningmonk
private var arrowKeyUp:Bool;
private var arrowKeyDown:Bool;
!
private var platform1:Platform;
private var platform2:Platform;
private var ball:Ball;
20. ROME 27-28 march 2015 β Yan Cui @theburningmonk
function keyDown(event:KeyboardEvent):Void {
if (currentGameState == Paused &&
event.keyCode == 32) {
setGameState(Playing);
} else if (event.keyCode == 38) {
arrowKeyUp = true;
} else if (event.keyCode == 40) {
arrowKeyDown = true;
}
}
21. ROME 27-28 march 2015 β Yan Cui @theburningmonk
function keyUp(event:KeyboardEvent):Void {
if (event.keyCode == 38) {
arrowKeyUp = false;
} else if (event.keyCode == 40) {
arrowKeyDown = false;
}
}
22. ROME 27-28 march 2015 β Yan Cui @theburningmonk
function everyFrame(event:Event):Void {
if(currentGameState == Playing){
if (arrowKeyUp) {
platform1.y -= platformSpeed;
}
if (arrowKeyDown) {
platform1.y += platformSpeed;
}
if (platform1.y < 5) platform1.y = 5;
if (platform1.y > 395) platform1.y = 395;
}
}
23. ROME 27-28 march 2015 β Yan Cui @theburningmonk
function everyFrame(event:Event):Void {
if(currentGameState == Playing){
if (arrowKeyUp) {
platform1.y -= platformSpeed;
}
if (arrowKeyDown) {
platform1.y += platformSpeed;
}
if (platform1.y < 5) platform1.y = 5;
if (platform1.y > 395) platform1.y = 395;
}
}
24. ROME 27-28 march 2015 β Yan Cui @theburningmonk
source ο¬les
state changes
25. ROME 27-28 march 2015 β Yan Cui @theburningmonk
source ο¬les execution
26. ROME 27-28 march 2015 β Yan Cui @theburningmonk
source ο¬les execution
27. ROME 27-28 march 2015 β Yan Cui @theburningmonk
mental model
input state new state behaviour
{ x; y } { x; y-speed }
{ x; y } { x; y+speed }
timer { x; y } { x; y } draw platform
β¦ β¦ β¦ β¦
28. ROME 27-28 march 2015 β Yan Cui @theburningmonk
transformation
let y = f(x)
Imperative Functional
x.f()
29. ROME 27-28 march 2015 β Yan Cui @theburningmonk
Transformations
simplify problem
decomposition
30. ROME 27-28 march 2015 β Yan Cui @theburningmonk
Move Up
Move Down
31. ROME 27-28 march 2015 β Yan Cui @theburningmonk
type alias Platform = {x:Int, y:Int}
defaultPlatform = {x=5, y=0}
!
delta = Time.fps 20
input = Signal.sampleOn delta Keyboard.arrows
!
cap x = max 5 <| min x 395
!
p1 : Signal Platform
p1 = foldp ({x, y} s -> {s | y <- cap <| s.y + 5*y})
defaultPlatform
input
32. ROME 27-28 march 2015 β Yan Cui @theburningmonk
type alias Platform = {x:Int, y:Int}
defaultPlatform = {x=5, y=0}
!
delta = Time.fps 20
input = Signal.sampleOn delta Keyboard.arrows
!
cap x = max 5 <| min x 395
!
p1 : Signal Platform
p1 = foldp ({x, y} s -> {s | y <- cap <| s.y + 5*y})
defaultPlatform
input
33. ROME 27-28 march 2015 β Yan Cui @theburningmonk
type alias Platform = {x:Int, y:Int}
defaultPlatform = {x=5, y=0}
!
delta = Time.fps 20
input = Signal.sampleOn delta Keyboard.arrows
!
cap x = max 5 <| min x 395
!
p1 : Signal Platform
p1 = foldp ({x, y} s -> {s | y <- cap <| s.y + 5*y})
defaultPlatform
input
34. ROME 27-28 march 2015 β Yan Cui @theburningmonk
UP
{ x=0, y=1 }
DOWN
{ x=0, y=-1 }
LEFT
{ x=-1, y=0 }
RIGHT
{ x=1, y=0 }
35. ROME 27-28 march 2015 β Yan Cui @theburningmonk
type alias Platform = {x:Int, y:Int}
defaultPlatform = {x=5, y=0}
!
delta = Time.fps 20
input = Signal.sampleOn delta Keyboard.arrows
!
cap x = max 5 <| min x 395
!
p1 : Signal Platform
p1 = foldp ({x, y} s -> {s | y <- cap <| s.y + 5*y})
defaultPlatform
input
36. ROME 27-28 march 2015 β Yan Cui @theburningmonk
type alias Platform = {x:Int, y:Int}
defaultPlatform = {x=5, y=0}
!
delta = Time.fps 20
input = Signal.sampleOn delta Keyboard.arrows
!
cap x = max 5 <| min x 395
!
p1 : Signal Platform
p1 = foldp ({x, y} s -> {s | y <- cap <| s.y + 5*y})
defaultPlatform
input
37. ROME 27-28 march 2015 β Yan Cui @theburningmonk
type alias Platform = {x:Int, y:Int}
defaultPlatform = {x=5, y=0}
!
delta = Time.fps 20
input = Signal.sampleOn delta Keyboard.arrows
!
cap x = max 5 <| min x 395
!
p1 : Signal Platform
p1 = foldp ({x, y} s -> {s | y <- cap <| s.y + 5*y})
defaultPlatform
input
38. ROME 27-28 march 2015 β Yan Cui @theburningmonk
type alias Platform = {x:Int, y:Int}
defaultPlatform = {x=5, y=0}
!
delta = Time.fps 20
input = Signal.sampleOn delta Keyboard.arrows
!
cap x = max 5 <| min x 395
!
p1 : Signal Platform
p1 = foldp ({x, y} s -> {s | y <- cap <| s.y + 5*y})
defaultPlatform
input
39. ROME 27-28 march 2015 β Yan Cui @theburningmonk
type alias Platform = {x:Int, y:Int}
defaultPlatform = {x=5, y=0}
!
delta = Time.fps 20
input = Signal.sampleOn delta Keyboard.arrows
!
cap x = max 5 <| min x 395
!
p1 : Signal Platform
p1 = foldp ({x, y} s -> {s | y <- cap <| s.y + 5*y})
defaultPlatform
input
40. ROME 27-28 march 2015 β Yan Cui @theburningmonk
Rx Dart Elm
Observable Stream Signal
= =
50. ROME 27-28 march 2015 β Yan Cui @theburningmonk
add x y = x + y
51. ROME 27-28 march 2015 β Yan Cui @theburningmonk
add : Int -> Int -> Int
add x y = x + y
52. ROME 27-28 march 2015 β Yan Cui @theburningmonk
calcAngle start end =
let
distH
= end.x - start.x
distV
= end.y - start.y
in atan2 distV distH
53. ROME 27-28 march 2015 β Yan Cui @theburningmonk
calcAngle start end =
let
distH
= end.x - start.x
distV
= end.y - start.y
in atan2 distV distH
54. ROME 27-28 march 2015 β Yan Cui @theburningmonk
calcAngle start end =
let
distH
= end.x - start.x
distV
= end.y - start.y
in atan2 distV distH
55. ROME 27-28 march 2015 β Yan Cui @theburningmonk
multiply x y
= x * y
triple = multiply 3
56. ROME 27-28 march 2015 β Yan Cui @theburningmonk
multiply x y
= x * y
triple = multiply 3
57. ROME 27-28 march 2015 β Yan Cui @theburningmonk
f a b c d = β¦
f :
Int ->
(Int ->
(Int ->
(Int -> Int)))
58. ROME 27-28 march 2015 β Yan Cui @theburningmonk
double list = List.map (x -> x * 2) list
59. ROME 27-28 march 2015 β Yan Cui @theburningmonk
double list = List.map ((*) 2) list
60. ROME 27-28 march 2015 β Yan Cui @theburningmonk
tuple1 = (2,βthreeβ)
tuple2 = (2,βthreeβ, [4, 5])
61. ROME 27-28 march 2015 β Yan Cui @theburningmonk
tuple4 = (,) 2 βthreeβ
tuple5 = (,,) 2 βthreeβ [4, 5]
62. ROME 27-28 march 2015 β Yan Cui @theburningmonk
x = { age=42, name=βfooβ }
63. ROME 27-28 march 2015 β Yan Cui @theburningmonk
lightweight, labelled
data structure
64. ROME 27-28 march 2015 β Yan Cui @theburningmonk
x.age
x.name
-- 42
-- βfooβ
65. ROME 27-28 march 2015 β Yan Cui @theburningmonk
x.age
x.name
-- 42
-- βfooβ
.age x
.name x
-- 42
-- βfooβ
66. ROME 27-28 march 2015 β Yan Cui @theburningmonk
-- clone and update
y = { x | name <- "bar" }
67. ROME 27-28 march 2015 β Yan Cui @theburningmonk
type alias Character =
{ age : Int, name : String }
68. ROME 27-28 march 2015 β Yan Cui @theburningmonk
type alias Named a = { a | name : String }
type alias Aged a = { a | age : Int }
69. ROME 27-28 march 2015 β Yan Cui @theburningmonk
lady : Named ( Aged { } )
lady = { name=βfooβ, age=42 }
70. ROME 27-28 march 2015 β Yan Cui @theburningmonk
getName : Named x -> String
getName { name } = name
71. ROME 27-28 march 2015 β Yan Cui @theburningmonk
getName : Named x -> String
getName { name } = name
!
getName lady
-- βfooβ
72. ROME 27-28 march 2015 β Yan Cui @theburningmonk
type Status = Flying Pos Speed
| Exploding Radius
| Exploded
73. ROME 27-28 march 2015 β Yan Cui @theburningmonk
aka.
βsums-and-productsβ
data structures
74. ROME 27-28 march 2015 β Yan Cui @theburningmonk
type Status = Flying Pos Speed
| Exploding Radius
| Exploded
sums :
choice between variants of a type
75. ROME 27-28 march 2015 β Yan Cui @theburningmonk
products :
tuple of types
type Status = Flying Pos Speed
| Exploding Radius
| Exploded
76. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y radius =
circle radius
|> ο¬lled (rgb 150 170 150)
|> alpha 0.5
|> move (x, y)
77. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y radius =
circle radius
|> ο¬lled (rgb 150 170 150)
|> alpha 0.5
|> move (x, y)
ο¬lled : Color -> Shape -> Form
78. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y radius =
circle radius
|> ο¬lled (rgb 150 170 150)
|> alpha 0.5
|> move (x, y)
79. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y radius =
circle radius
|> ο¬lled (rgb 150 170 150)
|> alpha 0.5
|> move (x, y)
80. ROME 27-28 march 2015 β Yan Cui @theburningmonk
ββ¦a clean design is one that supports
visual thinking so people can meet their
informational needs with a minimum of
conscious effort.β
!
- Daniel Higginbotham
(www.visualmess.com)
82. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y radius =
radius |> circle
|> ο¬lled (rgb 150 170 150)
|> alpha 0.5
|> move (x, y)
2.top-to-bottom
1. left-to-right
83. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle : Int -> Int -> Float -> Form
84. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
circle
>> ο¬lled (rgb 150 170 150)
>> alpha 0.5
>> move (x, y)
85. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
circle
>> ο¬lled (rgb 150 170 150)
>> alpha 0.5
>> move (x, y)
circle : Float -> Shape
86. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
(Float -> Shape)
>> ο¬lled (rgb 150 170 150)
>> alpha 0.5
>> move (x, y)
87. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
(Float -> Shape)
>> ο¬lled (rgb 150 170 150)
>> alpha 0.5
>> move (x, y)
ο¬lled : Color -> Shape -> Form
88. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
(Float -> Shape)
>> ο¬lled (rgb 150 170 150)
>> alpha 0.5
>> move (x, y)
Curried!
ο¬lled : Color -> Shape -> Form
89. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
(Float -> Shape)
>> ο¬lled (rgb 150 170 150)
>> alpha 0.5
>> move (x, y)
Shape -> Form
90. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
(Float -> Shape)
>> (Shape -> Form)
>> alpha 0.5
>> move (x, y)
91. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
(Float -> Shape)
>> (Shape -> Form)
>> alpha 0.5
>> move (x, y)
92. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
(Float -> Shape)
>> (Shape -> Form)
>> alpha 0.5
>> move (x, y)
93. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
(Float -> Shape)
>> (Shape -> Form)
>> alpha 0.5
>> move (x, y)
94. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
(Float -> Form)
>> alpha 0.5
>> move (x, y)
95. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
(Float -> Form)
>> (Form -> Form)
>> move (x, y)
96. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
(Float -> Form)
>> (Form -> Form)
>> move (x, y)
97. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
(Float -> Form)
>> move (x, y)
98. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
(Float -> Form)
>> (Form -> Form)
99. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
(Float -> Form)
>> (Form -> Form)
100. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle x y =
(Float -> Form)
101. ROME 27-28 march 2015 β Yan Cui @theburningmonk
drawCircle : Int -> Int -> (Float -> Form)
102. ROME 27-28 march 2015 β Yan Cui @theburningmonk
greet name =
case name of
"Yan"
-> βhi, theburningmonk"
_
-> βhi,β ++ name
103. ROME 27-28 march 2015 β Yan Cui @theburningmonk
greet name =
case name of
"Yan"
-> βhi, theburningmonk"
_
-> βhi,β ++ name
104. ROME 27-28 march 2015 β Yan Cui @theburningmonk
ο¬zzbuzz n =
if | n % 15 == 0
-> "ο¬zz buzz"
| n % 3
== 0
-> "ο¬zz"
| n % 5
== 0
-> "buzz"
| otherwise
-> show n
105. ROME 27-28 march 2015 β Yan Cui @theburningmonk
Mouse.position
Mouse.clicks
Mouse.isDown
β¦
106. ROME 27-28 march 2015 β Yan Cui @theburningmonk
Window.dimension
Window.width
Window.height
107. ROME 27-28 march 2015 β Yan Cui @theburningmonk
Time.every
Time.fps
Time.timestamp
Time.delay
β¦
108. ROME 27-28 march 2015 β Yan Cui @theburningmonk
Mouse.position : Signal (Int, Int)
109. ROME 27-28 march 2015 β Yan Cui @theburningmonk
Mouse.position : Signal (Int, Int)
110. ROME 27-28 march 2015 β Yan Cui @theburningmonk
Mouse.position : Signal (Int, Int)
(10, 23) (3, 16) (8, 10) (12, 5) (18, 3)
111. ROME 27-28 march 2015 β Yan Cui @theburningmonk
Keyboard.lastPressed : Signal Int
112. ROME 27-28 march 2015 β Yan Cui @theburningmonk
Keyboard.lastPressed : Signal Int
H E L L O space
113. ROME 27-28 march 2015 β Yan Cui @theburningmonk
Keyboard.lastPressed : Signal Int
H E L L O space
72 69 76 76 79 32
114. ROME 27-28 march 2015 β Yan Cui @theburningmonk
map : (a -> b) -> Signal a -> Signal b
175. ROME 27-28 march 2015 β Yan Cui @theburningmonk
github.com/theburningmonk/elm-snake
github.com/theburningmonk/elm-missile-
command
Missile Command
Snake
176. ROME 27-28 march 2015 β Yan Cui @theburningmonk
elm-lang.org/try
debug.elm-lang.org/try