178
My adventure with ELM SCORE 42 by Yan Cui

My adventure with Elm

  • Upload
    yan-cui

  • View
    4.462

  • Download
    0

Embed Size (px)

DESCRIPTION

In this talk I introduced Functional Reactive Programming and ELM and finished off with a live demo building a complete game of snake in Elm. Code for the demo can be found at : https://github.com/theburningmonk/elm-snake

Citation preview

Page 1: My adventure with Elm

My adventure with ELM

SCORE 42

by Yan Cui

Page 2: My adventure with Elm

agenda

Page 3: My adventure with Elm

Hi, my name is Yan Cui.

Page 4: My adventure with Elm
Page 5: My adventure with Elm

I’m not an expert on Elm.

Page 6: My adventure with Elm
Page 7: My adventure with Elm

Function Reactive Programming?

Page 8: My adventure with Elm

Value over Time

Page 9: My adventure with Elm

Time

Value

Page 10: My adventure with Elm

Time

ValueSignal

Page 11: My adventure with Elm

Move Up

Move Down

Page 12: My adventure with Elm

private var arrowKeyUp:Bool;!private var arrowKeyDown:Bool;!!private var platform1:Platform;!private var platform2:Platform;!private var ball:Ball;

Page 13: My adventure with Elm

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;!! }!}

Page 14: My adventure with Elm

function keyUp(event:KeyboardEvent):Void {!! if (event.keyCode == 38) {!! ! arrowKeyUp = false;!! } else if (event.keyCode == 40) {!! ! arrowKeyDown = false;!! }!}

Page 15: My adventure with Elm

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;!! }!}

Page 16: My adventure with Elm

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;!! }!}

Page 17: My adventure with Elm

source files

state changes

Page 18: My adventure with Elm

source files execution

Page 19: My adventure with Elm

source files execution

Page 20: My adventure with Elm

mental model

input state new state behaviour

{ x; y } { x; y-speed }

{ x; y } { x; y+speed }

timer { x; y } { x; y } draw platform

… … … …

Page 21: My adventure with Elm

transformation

let y = f(x)

Imperative Functional

x.f()

mutation

Page 22: My adventure with Elm

“one thing I’m discovering is that transforming data is easier to think about than

maintaining state.” !

- Dave Thomas

Page 23: My adventure with Elm

transformations simplify problem decomposition

Page 24: My adventure with Elm

Move Up

Move Down

Page 25: My adventure with Elm

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

Page 26: My adventure with Elm

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

Page 27: My adventure with Elm

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

Page 28: My adventure with Elm

UP! ! ! { x=0, y=1 }!DOWN! ! { x=0, y=-1 }!LEFT!! ! { x=-1, y=0 }!RIGHT! ! { x=1, y=0 }

Keyboard.arrows

Page 29: My adventure with Elm

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

Page 30: My adventure with Elm

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

Page 31: My adventure with Elm

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

Page 32: My adventure with Elm

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

Page 33: My adventure with Elm

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

Page 34: My adventure with Elm

Rx Dart ElmObservable Stream Signal= =

Page 35: My adventure with Elm

see also http://bit.ly/1HPM3ul

Page 36: My adventure with Elm
Page 37: My adventure with Elm
Page 38: My adventure with Elm

Idea See in Action

Page 39: My adventure with Elm
Page 40: My adventure with Elm
Page 41: My adventure with Elm
Page 42: My adventure with Elm

see also http://bit.ly/1wV46XS

Page 43: My adventure with Elm

Elm Basics

Page 44: My adventure with Elm

add x y = x + y

Page 45: My adventure with Elm

add : Int -> Int -> Intadd x y = x + y

Page 46: My adventure with Elm

calcAngle start end = let distH = end.x - start.x distV = end.y - start.y in atan2 distV distH

Page 47: My adventure with Elm

calcAngle start end = let distH = end.x - start.x distV = end.y - start.y in atan2 distV distH

Page 48: My adventure with Elm

calcAngle start end = let distH = end.x - start.x distV = end.y - start.y in atan2 distV distH

Page 49: My adventure with Elm

multiply x y = x * y triple = multiply 3

Page 50: My adventure with Elm

multiply x y = x * y triple = multiply 3

Page 51: My adventure with Elm

double list = List.map (\x -> x * 2) list

Page 52: My adventure with Elm

double list = List.map ((*) 2) list

Page 53: My adventure with Elm

tuple1 = (2, “three”) tuple2 = (2, “three”, [4, 5])

Page 54: My adventure with Elm

tuple4 = (,) 2 “three” tuple5 = (,,) 2 “three” [4, 5]

Page 55: My adventure with Elm

x = { age=42, name=“foo” }

Page 56: My adventure with Elm

lightweight, labelled data structure

Page 57: My adventure with Elm

x.age x.name

-- 42 -- “foo”

Page 58: My adventure with Elm

x.age x.name

-- 42 -- “foo”

.age x

.name x-- 42 -- “foo”

Page 59: My adventure with Elm

-- clone and update y = { x | name <- "bar" }

Page 60: My adventure with Elm

type alias Character = { age : Int, name : String }

Page 61: My adventure with Elm

type alias Named a = { a | name : String } type alias Aged a = { a | age : Int }

Page 62: My adventure with Elm

lady : Named ( Aged { } ) lady = { name=“foo”, age=42 }

Page 63: My adventure with Elm

getName : Named x -> String getName { name } = name

Page 64: My adventure with Elm

getName : Named x -> String getName { name } = name !

getName lady -- “foo”

Page 65: My adventure with Elm

type Status = Flying Pos Speed | Exploding Radius | Exploded

Page 66: My adventure with Elm

aka. “sums-and-products”

data structures

Page 67: My adventure with Elm

type Status = Flying Pos Speed | Exploding Radius | Exploded

sums : choice between variants of a type

Page 68: My adventure with Elm

products : tuple of types

type Status = Flying Pos Speed | Exploding Radius | Exploded

Page 69: My adventure with Elm

drawCircle x y radius = circle radius |> filled (rgb 150 170 150) |> alpha 0.5 |> move (x, y)

Page 70: My adventure with Elm

drawCircle x y radius = circle radius |> filled (rgb 150 170 150) |> alpha 0.5 |> move (x, y)

filled : Color -> Shape -> Form

Page 71: My adventure with Elm

drawCircle x y radius = circle radius |> filled (rgb 150 170 150) |> alpha 0.5 |> move (x, y)

Page 72: My adventure with Elm

drawCircle x y radius = circle radius |> filled (rgb 150 170 150) |> alpha 0.5 |> move (x, y)

Page 73: My adventure with Elm

“…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)

Page 74: My adventure with Elm

Whilst talking with an ex-colleague, a question came up on how to implement the Stable Marriage problem using a message passing approach. Naturally, I wanted to answer that question with Erlang!!!Let’s first dissect the problem and decide what processes we need and how they need to interact with one another.!!The stable marriage problem is commonly stated as:!Given n men and n women, where each person has ranked all members of the opposite sex with a unique number between 1 and n in order of preference, marry the men and women together such that there are no two people of opposite sex who would both rather have each other than their current partners. If there are no such people, all the marriages are “stable”. (It is assumed that the participants are binary gendered and that marriages are not same-sex).!From the problem description, we can see that we need:!* a module for man!* a module for woman!* a module for orchestrating the experiment!In terms of interaction between the different modules, I imagined something along the lines of…

2. top-to-bottom1.left-to-right

how we read ENGLISH

Page 75: My adventure with Elm

public void DoSomething(int x, int y)!{! Foo(y,! Bar(x,! Zoo(Monkey())));!}

how we read CODE

Page 76: My adventure with Elm

public void DoSomething(int x, int y)!{! Foo(y,! Bar(x,! Zoo(Monkey())));!}

2. bottom-to-top

1.right-to-left

how we read CODE

Page 77: My adventure with Elm

Whilst talking with an ex-colleague, a question came up on how to implement the Stable Marriage problem using a message passing approach. Naturally, I wanted to answer that question with Erlang!!!Let’s first dissect the problem and decide what processes we need and how they need to interact with one another.!!The stable marriage problem is commonly stated as:!Given n men and n women, where each person has ranked all members of the opposite sex with a unique number between 1 and n in order of preference, marry the men and women together such that there are no two people of opposite sex who would both rather have each other than their current partners. If there are no such people, all the marriages are “stable”. (It is assumed that the participants are binary gendered and that marriages are not same-sex).!From the problem description, we can see that we need:!* a module for man!* a module for woman!* a module for orchestrating the experiment!In terms of interaction between the different modules, I imagined something along the lines of…

2. top-to-bottom

1.left-to-right

how we read ENGLISH

public void DoSomething(int x, int y)!{! Foo(y,! Bar(x,! Zoo(Monkey())));!}

2. top-to-bottom

1.right-to-left

how we read CODE

Page 78: My adventure with Elm

“…a clean design is one that supports visual thinking so

people can meet their informational needs with a

minimum of conscious effort.”

Page 79: My adventure with Elm

drawCircle x y radius = radius |> circle |> filled (rgb 150 170 150) |> alpha 0.5 |> move (x, y)

2. top-to-bottom

1. left-to-right

Page 80: My adventure with Elm

drawCircle : Int -> Int -> Float -> Form

Page 81: My adventure with Elm

drawCircle x y = circle >> filled (rgb 150 170 150) >> alpha 0.5 >> move (x, y)

Page 82: My adventure with Elm

drawCircle x y = circle >> filled (rgb 150 170 150) >> alpha 0.5 >> move (x, y)

circle : Float -> Shape

Page 83: My adventure with Elm

drawCircle x y = (Float -> Shape) >> filled (rgb 150 170 150) >> alpha 0.5 >> move (x, y)

Page 84: My adventure with Elm

drawCircle x y = (Float -> Shape) >> filled (rgb 150 170 150) >> alpha 0.5 >> move (x, y)

Shape -> Form

Page 85: My adventure with Elm

drawCircle x y = (Float -> Shape) >> (Shape -> Form) >> alpha 0.5 >> move (x, y)

Page 86: My adventure with Elm

drawCircle x y = (Float -> Shape) >> (Shape -> Form) >> alpha 0.5 >> move (x, y)

Page 87: My adventure with Elm

drawCircle x y = (Float -> Shape) >> (Shape -> Form) >> alpha 0.5 >> move (x, y)

Page 88: My adventure with Elm

drawCircle x y = (Float -> Shape) >> (Shape -> Form) >> alpha 0.5 >> move (x, y)

Page 89: My adventure with Elm

drawCircle x y = (Float -> Form) >> alpha 0.5 >> move (x, y)

Page 90: My adventure with Elm

drawCircle x y = (Float -> Form) >> (Form -> Form) >> move (x, y)

Page 91: My adventure with Elm

drawCircle x y = (Float -> Form) >> (Form -> Form) >> move (x, y)

Page 92: My adventure with Elm

drawCircle x y = (Float -> Form) >> move (x, y)

Page 93: My adventure with Elm

drawCircle x y = (Float -> Form) >> (Form -> Form)

Page 94: My adventure with Elm

drawCircle x y = (Float -> Form) >> (Form -> Form)

Page 95: My adventure with Elm

drawCircle x y = (Float -> Form)

Page 96: My adventure with Elm

drawCircle : Int -> Int -> (Float -> Form)

Page 97: My adventure with Elm

greet name = case name of "Yan" -> “hi, theburningmonk" _ -> “hi, “ ++ name

Page 98: My adventure with Elm

greet name = case name of "Yan" -> “hi, theburningmonk" _ -> “hi, “ ++ name

Page 99: My adventure with Elm

fizzbuzz n = if | n % 15 == 0 -> "fizz buzz" | n % 3 == 0 -> "fizz" | n % 5 == 0 -> "buzz" | otherwise -> show n

Page 100: My adventure with Elm

Mouse.position Mouse.clicks

Mouse.isDown …

Page 101: My adventure with Elm

Window.dimension Window.width Window.height

Page 102: My adventure with Elm

Time.every Time.fps

Time.timestamp Time.delay

Page 103: My adventure with Elm

Mouse.position : Signal (Int, Int)

Page 104: My adventure with Elm

Mouse.position : Signal (Int, Int)

Page 105: My adventure with Elm

Mouse.position : Signal (Int, Int)

(10, 23) (3, 16) (8, 10) (12, 5) (18, 3)

Page 106: My adventure with Elm

Keyboard.lastPressed : Signal Int

Page 107: My adventure with Elm

Keyboard.lastPressed : Signal Int

H E L L O space

Page 108: My adventure with Elm

Keyboard.lastPressed : Signal Int

H E L L O space

72 69 76 76 79 32

Page 109: My adventure with Elm

map : (a -> b) -> Signal a -> Signal b

Page 110: My adventure with Elm

<~

Page 111: My adventure with Elm

Signal of num of pixels in window

Page 112: My adventure with Elm

(\(w, h) -> w*h) <~ Window.dimensions

Page 113: My adventure with Elm

(\(w, h) -> w*h) <~ Window.dimensions

Signal (Int, Int)(Int, Int) -> Int

Page 114: My adventure with Elm

(\(w, h) -> w*h) <~ Window.dimensions

Signal (Int, Int)(Int, Int) -> Int

Signal Int

Page 115: My adventure with Elm

(10, 10) (15, 10) (18, 12)

100 150 216

(\(w, h) -> w*h) <~ Window.dimensions

Page 116: My adventure with Elm

map2 : (a -> b -> c) -> Signal a -> Signal b -> Signal c

Page 117: My adventure with Elm

~

Page 118: My adventure with Elm

(,) <~ Window.width ~ Window.height

Signal Int

a -> b -> (a, b)

Signal Int

Page 119: My adventure with Elm

(,) <~ Window.width ~ Window.height

Signal Int

Int -> Int -> (Int, Int)

Signal Int

Page 120: My adventure with Elm

map3 : (a -> b -> c -> d) -> Signal a -> Signal b -> Signal c -> Signal d

Page 121: My adventure with Elm

(,,) <~ signalA ~ signalB ~ signalC

Page 122: My adventure with Elm

map4 : … map5 : … map6 : … map7 : … map8 : …

Page 123: My adventure with Elm

foldp : (a -> b -> b) -> b -> Signal a -> Signal b

Page 124: My adventure with Elm

foldp : (a -> b -> b) -> b -> Signal a -> Signal b

Page 125: My adventure with Elm

foldp (\_ n -> n + 1) 0 Mouse.clicks

Page 126: My adventure with Elm

foldp (\_ n -> n + 1) 0 Mouse.clicks

Page 127: My adventure with Elm

foldp (\_ n -> n + 1) 0 Mouse.clicks

Page 128: My adventure with Elm

1 3 42 5

foldp (\_ n -> n + 1) 0 Mouse.clicks

Page 129: My adventure with Elm

UP { x=0, y=1 } DOWN { x=0, y=-1 } LEFT { x=-1, y=0 } RIGHT { x=1, y=0 }

Page 130: My adventure with Elm

merge : Signal a -> Signal a -> Signal a mergeMany : List (Signal a) -> Signal a …

Page 131: My adventure with Elm

Js Interop, WebGL

HTML layout, dependency management,

etc.

Page 132: My adventure with Elm
Page 133: My adventure with Elm

8 segments

Page 134: My adventure with Elm

direction

Page 135: My adventure with Elm

change

change

no changenot allowed

direction

Page 136: My adventure with Elm

direction

Page 137: My adventure with Elm

direction

Page 138: My adventure with Elm

direction

Page 139: My adventure with Elm

direction

Page 140: My adventure with Elm

direction

cherry

Page 141: My adventure with Elm

direction

YUM YUM YUM!

Page 142: My adventure with Elm

direction

+1 segment

Page 143: My adventure with Elm
Page 144: My adventure with Elm
Page 145: My adventure with Elm
Page 146: My adventure with Elm
Page 147: My adventure with Elm
Page 148: My adventure with Elm
Page 149: My adventure with Elm

Demo

Page 150: My adventure with Elm
Page 151: My adventure with Elm
Page 152: My adventure with Elm
Page 153: My adventure with Elm
Page 154: My adventure with Elm
Page 155: My adventure with Elm
Page 156: My adventure with Elm
Page 157: My adventure with Elm
Page 158: My adventure with Elm
Page 159: My adventure with Elm
Page 160: My adventure with Elm
Page 161: My adventure with Elm
Page 162: My adventure with Elm
Page 163: My adventure with Elm
Page 164: My adventure with Elm
Page 165: My adventure with Elm
Page 166: My adventure with Elm

github.com/theburningmonk/elm-snake

github.com/theburningmonk/elm-missile-command

Missile Command

Snake

Page 167: My adventure with Elm

elm-lang.org/try

debug.elm-lang.org/try

Page 168: My adventure with Elm

the����������� ������������������  not����������� ������������������  so����������� ������������������  great����������� ������������������  things

Page 169: My adventure with Elm

Type error between lines 63 and 85: case gameState of

NotStarted -> if | userInput == Space -> Started (defaultSnake,Nothing)

| True -> gameState Started ({segments,direction},cherry) -> let arrow = case userInput

of Arrow arrow -> arrow

_ -> {x = 0, y = 0} newDirection = getNewDirection

arrow direction newHead = getNewSegment

(List.head segments) newDirection newTail = List.take

((List.length segments) - 1) segments

(w,h) = windowDims isGameOver = (List.any

(\t -> t == newHead) newTail) ||

(((fst newHead) > ((toFloat w) / 2)) || (((snd newHead) > ((toFloat h) / 2)) || (((fst newHead) <

((toFloat (-w)) / 2)) || ((snd newHead) <

((toFloat (-h)) / 2))))) in if | isGameOver -> NotStarted

| True -> Started

({segments = newHead :: newTail, direction = newDirection},

cherry) ! Expected Type: {}

Actual Type: Snake.Input

cryptic error messages

Page 170: My adventure with Elm

breaking changes

Page 171: My adventure with Elm
Page 172: My adventure with Elm
Page 173: My adventure with Elm
Page 174: My adventure with Elm
Page 175: My adventure with Elm
Page 176: My adventure with Elm
Page 177: My adventure with Elm
Page 178: My adventure with Elm

@theburningmonk

github.com/theburningmonk

theburningmonk.com