summaryrefslogtreecommitdiff
path: root/posts/guile-2d-frp.md
blob: 1970e732ecb466b9e33ce0b9599ccb59c7eb77fa (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
title: Functional Reactive Programming in Scheme with guile-2d
date: 2014-03-09 21:00:00
tags: foss, gnu, guile, scheme, gamedev, wsu
summary: Introduction to FRP in Scheme with guile-2d
---

Last month, the [GNU Guile](https://gnu.org/s/guile) project
celebrated the 3rd anniversary of its 2.0 release with a hacker
[potluck](http://savannah.gnu.org/forum/forum.php?forum_id=7887).
Guilers were encouraged to bring a tasty hack to the mailing list to
share with everyone.  My dish was a simple
[functional reactive programming](https://en.wikipedia.org/wiki/Functional_reactive_programming)
library.

Functional reactive programming (FRP) provides a way to describe
time-varying values and their relationships using a functional and
declarative programming style.  To understand what this means, let’s
investigate a typical variable definition in Scheme.  The expression
`(define c (+ a b))` defines the variable `c` to be the sum of
variables `a` and `b` at the time of evaluation.  If `a` or `b` is
assigned a new value, `c` remains the same.  However, for applications
that deal with state that transforms over time, it would be convenient
if we could define `c` to react to changes in `a` and `b` by
recomputing the sum.  Contrast this approach with the more traditional
style of modeling dynamic state via events and callbacks.  A lot of
programmers, myself included, have written code with so many callbacks
that the resulting program is unmaintainable spaghetti code.  Callback
hell is real, but if you accept FRP into your heart then you will be
saved!

By now you’re probably wondering: “What the hell does all this mean?”
So, here’s a real-world example of guile-2d’s FRP API:

```scheme
(define-signal position
  (signal-fold v+ (vector2 320 240)
               (signal-map (lambda (v)
                             (vscale v 4))
                           (signal-sample game-agenda 1 key-arrows))))
```

In guile-2d, time-varying values are called “signals”.  The above
signal describes a relationship between the arrow keys on the keyboard
and the position of the player.  `signal-sample` is used to trigger a
signal update upon every game tick that provides the current state of
the arrow keys.  `key-arrows` is a vector2 that maps to the current
state of the arrow keys, allowing for 8 direction movement.  This
vector2 is then scaled 4x to make the player move faster.  Finally,
the scaled vector is added to the previous player position via
`signal-fold`.  The player’s position is at `(320, 240)` initially.
As you can see, there are no callbacks and explicit mutation needed.
Those details have been abstracted away, freeing the programmer to
focus on more important things.

I think it’s helpful to see FRP in action to really appreciate the
magic.  So, check out this screencast!

![](/videos/guile-2d-frp-demo.webm)

To see all of the juicy implementation details, check out the
[git repository](https://gitorious.org/sly/sly/source/sly/signal.scm).
Thanks for following along!