Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Functional Reactive Programming: A Video Game and Piece of CRUD

Functional Reactive Programming: A Video Game and Piece of CRUD

Functional Reactive Programming (FRP) is the best way to think about systems that have to deal with input.

If you disagree, you're wrong and I'll fight you.

This is a crash course to FRP.

But, we won't use a functional language.

• We will make a video game
◦ Space Invaders.
• We won't make a CRUD website
◦ But we'll talk about how we could

Scott Robinson

March 17, 2012
Tweet

More Decks by Scott Robinson

Other Decks in Programming

Transcript

  1. Whilst We Wait
    1. Get LÖVE
    https://love2d.org/
    2. Code along
    git clone
    https://github.com/quad/invader.love.git

    View Slide

  2. Functional Reactivity
    A Video Game
    and
    A Piece of CRUD

    View Slide

  3. Reactive Programming
    a = 1
    b = 1
    c = a + b
    print c
    2
    b = 2
    print c

    View Slide

  4. Functional Reactive
    Programming

    View Slide

  5. View Slide

  6. Space Invaders
    git checkout -f WORLD

    View Slide

  7. function love.load()
    w = meta.apply(world)
    app = gvt.start(w)
    end
    main.lua

    View Slide

  8. Functional
    World
    ‏ 0.6s ‐ 0.2s

    View Slide

  9. function love.keypressed(key)
    if app.state ~= 'ready' then
    gvt.step(app, 'key.' .. key)
    end
    end
    main.lua

    View Slide

  10. Game Loops
    loop do
    input
    update
    draw
    wait
    end

    View Slide

  11. function love.update(dt)
    if app.state ~= 'ready' then
    gvt.step(app, 'dt', dt)
    elseif app.state ~= 'stopped' then
    love.event.push('q')
    end
    end
    main.lua

    View Slide

  12. world.lua
    return function()
    await(5, 'key.escape')
    end

    View Slide

  13. Demo

    View Slide

  14. Event Systems
    • Main loop
    • Events
    • Queue
    • Dispatcher
    • Handlers

    View Slide

  15. git checkout -f DRAW

    View Slide

  16. main.lua
    screen = {}
    function love.load()
    w = meta.apply(world, {Screen=screen})
    app = gvt.start(w)
    end

    View Slide

  17. Imperative World
    ‏ 0.6s ‐ 0.2s Imperative
    World
    Screen

    View Slide

  18. world.lua
    thing = {}
    function thing:_draw_list()
    return {{'rectangle', 'line',
    10, 10, 500, 500}}
    end
    __.extend(Screen, {thing})
    await('key.escape')

    View Slide

  19. main.lua
    function love.draw()
    __(screen):chain()
    :map(function(o)
    return o:_draw_list()
    end)
    :concat()
    :each(function(d)
    love.graphics[d[1]](unpack(d, 2))
    end)
    end

    View Slide

  20. Demo

    View Slide

  21. Time Check
    Is this boring?

    View Slide

  22. git checkout -f BULLET

    View Slide

  23. world.lua
    bullet = Bullet(500, 500,
    Consts.bullet.v)
    __.extend(Screen, {bullet})

    View Slide

  24. bullet.lua
    function draw_list(x, y)
    return {
    {'setLineWidth',
    Consts.bullet.width},
    {'line', x, y,
    x, y + Consts.bullet.height},
    {'setLineWidth', 1},
    }
    end

    View Slide

  25. bullet.lua
    return {
    _draw_list = L(draw_list)(_x, _y),
    }

    View Slide

  26. “Lift”
    • draw_list is a pure function
    • Same input, same output
    • _draw_list is a “lifted” function
    • Changed input, changed output

    View Slide

  27. bullet.lua
    function constructor(ix, iy, v)
    _x = ix
    _y = iy + S(v)

    View Slide

  28. That’s
    S(v)
    as in
    ∫v
    O
    H
    SN
    AP
    C
    ALC
    U
    LU
    S

    View Slide

  29. Demo

    View Slide

  30. Chain Reactions
    Bullet
    dt S(v) _y
    _draw_list
    _x

    View Slide

  31. git checkout -f SWARM

    View Slide

  32. world.lua
    swarm = Swarm(Consts.swarm.initial.x,
    Consts.swarm.initial.y)
    __.extend(Screen, {bullet, swarm})

    View Slide

  33. swarm.lua
    function constructor(ix, iy) ...
    _v = Consts.swarm.speed
    _x = ix + S(_v)
    _y = iy

    View Slide

  34. swarm.lua
    invaders = __.range(1, Consts.swarm.number)
    :map(function(n)
    return Invader(n - 1, _x, _y)
    end)

    View Slide

  35. invader.lua
    function constructor(n, sx, sy) ...
    col = n % Consts.swarm.columns
    row = math.floor(n / Consts.swarm.columns)
    _x = (col * (Consts.invader.side *
    Consts.invader.spacing.x)) + sx
    _y = (row * (Consts.invader.side *
    Consts.invader.spacing.y)) + sy

    View Slide

  36. invader.lua
    function draw_list(x, y)
    return {{
    'rectangle', 'line',
    x, y,
    Consts.invader.side,
    Consts.invader.side,
    }}
    end

    View Slide

  37. swarm.lua
    function draw_list(invaders, x, y)
    return __(invaders):chain()
    :map(function(i)
    return i:_draw_list()
    end)
    :concat()
    :value()
    end

    View Slide

  38. Demo

    View Slide

  39. Chain Reactions
    Swarm
    Invader
    dt S(v) _x
    _draw_list
    _x
    _y
    _y
    _draw_list

    View Slide

  40. git checkout -f BOUNCE

    View Slide

  41. View Slide

  42. invader.lua
    function bounced(x)
    return x <= 0 or
    (x + Consts.invader.side) >=
    Consts.screen.width
    end
    return {
    _draw_list=L(draw_list)(_x, _y),
    _bounced=L(bounced)(_x),
    }

    View Slide

  43. swarm.lua
    _bounced = __.reduce(
    invaders,
    false,
    function(c, i)
    return OR(c, i._bounced)
    end
    )

    View Slide

  44. swarm.lua
    function bounce()
    _v = _v() * -1
    _y = _y() + Consts.invader.close
    end
    link(cond(_bounced), bounce)

    View Slide

  45. Demo

    View Slide

  46. • Imperative exception to Functional graph
    traversal
    • Acts like a fork
    • “Reacts” to other Reactors
    • All reactive variables are Reactors
    The Reactor

    View Slide

  47. git checkout -f PLAYER

    View Slide

  48. world.lua
    player = Player(Consts.player.initial.x,
    Consts.player.initial.y)

    View Slide

  49. player.lua
    function constructor(ix, iy) ...
    _x = ix
    _y = iy
    return {
    _draw_list = L(draw_list)(_x, _y),
    }

    View Slide

  50. player.lua
    function draw_list(x, y)
    return {{
    'triangle', 'line',
    x, y,
    x + Consts.player.width,
    y + Consts.player.height,
    x - Consts.player.width,
    y + Consts.player.height,
    }}
    end

    View Slide

  51. Demo

    View Slide

  52. git checkout -f MOVE

    View Slide

  53. world.lua
    link('key.left', player.left)
    link('key.right', player.right)

    View Slide

  54. player.lua
    return {
    _draw_list = L(draw_list)(_x, _y),
    left = move(-1),
    right = move(1),
    }

    View Slide

  55. player.lua
    function move(d)
    return function()
    _dir = d
    end
    end

    View Slide

  56. player.lua
    _dir = 0
    _x = ix
    _y = iy
    _v = L(v)(_dir, delay(_x))
    _x = ix + S(_v)

    View Slide

  57. Delayed Evaluation
    _dir v
    _v
    _x
    _int_v

    View Slide

  58. Delayed Evaluation
    _dir v
    _v
    _x
    _int_v

    View Slide

  59. player.lua
    function v(dir, x)
    if x then
    ...
    else
    return 0
    end
    end

    View Slide

  60. player.lua
    if x then
    if (x <= C.player.width
    and dir < 0) or
    (x >= C.screen.width - C.player.width
    and dir > 0) then
    return 0
    else
    return Consts.player.speed * dir
    end
    end

    View Slide

  61. Demo

    View Slide

  62. git checkout -f SHOOT

    View Slide

  63. world.lua
    bullet = Bullet(500, 500, C.bullet.v)
    function shoot()
    bullet.shoot(
    player._x(),
    player._y() - C.bullet.height / 2
    )
    end
    link('key. ', shoot)
    link('key.up', shoot)

    View Slide

  64. bullet.lua
    function shoot(x, y)
    _x = x
    _y = y + S(v)
    end
    return {
    _draw_list = L(draw_list)(_x, _y),
    shoot = shoot,
    }

    View Slide

  65. Demo

    View Slide

  66. git checkout -f KILL

    View Slide

  67. Collision Detection
    • In pure implementions,
    handled “outside” as collision events.
    • In our hybrid,
    handled as a reactive state.

    View Slide

  68. world.lua
    swarm = Swarm(C.swarm.initial.x,
    C.swarm.initial.y,
    bullet)

    View Slide

  69. swarm.lua
    local function constructor(ix, iy, bullet) ...
    return Invader(n - 1, _x, _y, bullet)

    View Slide

  70. invader.lua
    _box = L(box)(_x, _y)
    _hit = L(colliding)(_box, bullet._box)
    _alive = true

    View Slide

  71. invader.lua
    function box(x, y)
    return {x=x, y=y,
    width=Consts.invader.side,
    height=Consts.invader.side}
    end

    View Slide

  72. invader.lua
    function colliding(abox, bbox)
    ax2, ay2 = abox.x + abox.width,
    abox.y + abox.height
    bx2, by2 = bbox.x + bbox.width,
    bbox.y + bbox.height
    return abox.x < bx2 and ax2 > bbox.x and
    abox.y < by2 and ay2 > bbox.y
    end

    View Slide

  73. invader.lua
    function die()
    _alive = false
    end
    link(cond(_hit), die)

    View Slide

  74. invader.lua
    function draw_list(alive, x, y)
    if alive then ... else
    return {}
    end
    function bounced(alive, x)
    if alive then ... else
    return false
    end

    View Slide

  75. View Slide

  76. Reactivity
    Huh! Good God!
    What is it good for?

    View Slide

  77. View Slide

  78. View Slide

  79. •Model - View - Controller
    •Browser ⁶ Server
    •Workflows

    View Slide

  80. Workflow?!
    complete := seen [
    submission
    review
    confirmation
    ]

    View Slide

  81. Event Sourcing
    Martin Fowler’s

    View Slide

  82. most productivity apps
    editors
    not useful?
    relational databases
    backup/restore
    filesystems (snapshot)
    git
    Redo
    Undo
    Event Based
    Event Based State Based
    State Based

    View Slide

  83. Bullet
    Swarm
    Invader
    Player
    key.left player_left
    player_move
    key.right player_right
    player_x
    shoot
    player_v
    player_draw_list
    bullet_shoot
    player_y
    bullet_y
    bullet_x
    key.
    key.up
    bullet_int_v
    bullet_box
    bullet_draw_list
    invader_hit
    screen
    swarm_int_v
    swarm_x
    swarm_draw_list
    invader_x
    invader_bounced
    swarm_bounced
    swarm_bounce swarm_v
    swarm_y
    invader_y
    invader_draw_list
    invader_box
    invader_die invader_alive
    player_dir
    player_int_v
    dt

    View Slide

  84. •Reactive Programming > ˎ
    •People demand interactivity
    •Fork and Fix Space Invaders

    View Slide

  85. fin

    View Slide

  86. LuaGravity

    View Slide

  87. Push-pull functional
    reactive programming
    http://conal.net/papers/push-pull-frp/

    View Slide

  88. The Fran Tutorial
    http://conal.net/fran/tutorial.htm

    View Slide