[Top][All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Potluck dish - Simple functional reactive programming

From: David Thompson
Subject: Potluck dish - Simple functional reactive programming
Date: Sun, 16 Feb 2014 13:32:13 -0500
User-agent: Notmuch/0.17 ( Emacs/24.3.1 (x86_64-pc-linux-gnu)

Hello Guilers,

I didn't have time to put together a proper potluck dish, but I wanted
to find something to share anyway.

Lately I've been playing around with functional reactive programming
(FRP) applied to video games.  This style of programming allows for a
declarative, functional way of describing time-varying values.  Contrast
this method of programming with more traditional hooks and callbacks.
My FRP module can be used on top of hooks to escape callback hell.

And now for a simple example.  This morning I was writing a program
using my game engine, guile-2d, and I wanted to display the number of
times the GC has been run in the game window.  Without FRP I could have
done something like:

(define gc-label-position (vector2 0 40))
(define gc-counter 0)
(define (make-gc-label)
  (let ((text (format #f "GCs: ~d" counter)))
    (make-label font text gc-label-position)))
(define gc-label (make-gc-label))

(add-hook! after-gc-hook
           (lambda ()
             (set! gc-counter (1+ gc-counter))
             (set! gc-label (make-gc-label))))

This code isn't terrible, but wouldn't it be nice to declare that
'gc-label' will always contain a string with the number of GC runs in
it instead?  Enter FRP:

(define gc-label-position (vector2 0 40))
(define gc-counter (make-root-signal 0))
(define gc-label
  (signal-map (lambda (counter)
                (let ((text (format #f "GCs: ~d" counter)))
                  (make-label font text gc-label-position)))

(add-hook! after-gc-hook
           (lambda ()
             (signal-set! gc-counter (1+ (signal-ref gc-counter)))))

'gc-counter' and 'gc-label' both become 'signals', or time-varying
values.  Now, when the GC runs, the 'gc-counter' signal is incremented
by 1.  The act of setting 'gc-counter' triggers propagation of the
counter to the 'gc-label' signal which maps the counter to a new label
that prints the current number of GC runs.  Magic!  Note that
'after-gc-hook' is still needed to bootstrap the signal graph, but once
that is out of the way it's signals all the way down.

This example was fairly trivial, but what if the desired chain reaction
was more complicated?  Writing the logic using regular callback
procedures would become a nightmare.  The nightmare that JavaScript
programmers constantly find themselves in.

And that's my potluck dish!  I'm currently working on a new version of
this API that will allow the signal graph to handle the dynamic
environment of the REPL, but it's not ready yet.

Thanks to all of the Guile maintainers and contributors for the great
work these past 3 years!

- David Thompson

Attachment: signals.scm
Description: Binary data

reply via email to

[Prev in Thread] Current Thread [Next in Thread]