Note - This series of postings might reference the Reactive Swing With Observables series, since both techniques are attempting to solve the same problem - reactive programs - in a functional context.
Functional Reactive Programming (FRP) is a fancy name for a fairly simple - yet powerful - concept. After reading, re-reading, and cross-referencing multiple blog posts/papers/book chapters, I feel I “finally” have an understanding of what FRP is, and more importantly, why it’s potentially useful. This blog post is my attempt to convey what I’ve learned, to remove FRP from what was (to me) a bit of an ivory tower, and to show an implementation example in Clojure.
What is FRP? FRP is a method for mapping a set of inputs to a set of outputs. Eh….that’s the definition of a function isn’t it? Why is “reactive” in the name? The trick with FRP is that the inputs usually involve some context, like time or IO (GUI events, user interaction), and transform this context into output. The consequence of this contextual information is that we can define a sort of pure functional pipeline that evaluates relative to a specific, possibly event-varying or time-varying context. It’s a way to handle things like GUIs, Animation, and a whole host of other “dirty” operations in a “mostly” pure functional environment. Of higher practical utility, FRP is conceptually based around functions that map a context to some useful output. Since we’re using functions, we get all of the wonderful functional programming tools for free, which allows us to take pure functions and apply them, in a composable fashion, to special reactive contexts (this is called “lifting” in Haskell-speak). Just like we compose primitive functions to create more sophisticated functions, we get the compositional glue for composing reactive functions - for free - as well. I find that defining extremely sophisticated behaviors actually becomes fairly trivial, and has a naturally declarative feel in FRP, as opposed to stateful programming (duh! this is the intent of FRP and functional programming in general).
Paul Hudak and Conal Elliot are, so far as I know, the brains responsible for conceiving of FRP, and they have several informative articles (and books!) on the subject. Conal Elliot’s Functional Reactive Animation tutorial is a great reference. Most of the examples I’ll show in the Clojure implementation are derived from Tomas Petricek’s EXCELLENT book on functional programming “Real World Functional Programming, With Examples in F# and C#” Tomas Petricek does an excellent job implementing pieces of FRAN in F#, which really helped me gain a better understanding of FRP (since F# was my first functional programming language). Jon Skeet, the co-author of Real World Functional Programming, provides a C# implementation of the FRAN port as well, for anyone interested, but I just find it clunky to work in (due to the language rather than the author!) .
I ported and extended much of the F# examples into Clojure, which has significantly solidified my understanding of FRP. I found that I really didn’t “get it” until I’d actually ported the examples and evaluated them. I also found that actually seeing a reactive function evaluated as an animation REALLY drove home the concepts - and the attractiveness - of FRP. There were quite a few times where I found myself giggling like a 4 year old at 3 AM when I’d made two circles fly around the screen using composed behaviors. There’s something primitive and rewarding about that, something just plain fun. That’s why the title for this series has “fun” in it. I don’t want to engage in lofty, cerebral pursuits about the “meaning” of functional reactive programming, but to advertise how FRP can open up new doors to old problems, and possibly make programming fun.
The following pictures are still frames from a few sample animations. Note that I will be using “compose” a lot in the descriptions. There is a formal, and practical meaning to “compose,” but for now, just assume it means to combine. Also, the sole context for the animation behaviors that follows is time, therefore all of the behaviors are evaluated relative to some temporal context. This is not universal….reactive context is arbitrary. In this case, time is an appropriate choice for rendering simple animations.
The basis for the animation is a green and a blue circle. We define a simple behavior that evaluates, regardless of context, to a drawing of these two circles.
In this case, I”m actually using a Clojure scene-graph library I built under the hood, but the higher level concept is unchanged. We have, as our basis, a behavior that will, forever, render a pair of circles. For full disclosure, this behavior is actually the composition of two behaviors….a translation behavior that moves a drawing to the center/bottom-left of the screen, composed with a behavior that forever draws a green circle, and a translation behavior that moves a drawing to the center/top right of the screen, composed with a behavior that forever draws a blue circle. When composed, the end result is a behavior that will, forever, return a drawing of two circles mirrored about the x and y axes, centered at the middle of the screen.
We then compose our initial frame, the “forever” circles, with some other behaviors that alter them - rotation, translation, scale, alpha, etc. Since everything is composable, we can define complex actions pretty easily, and we can create really interesting effects….with little to no state, and with a pretty clear declaration of what the animation will look like. To convey the sense of motion in each frame, there are n circles that appear to be trailing each circle. This is actually a separate “trail” behavior that renders n circles in the “past”, i.e. n circles evaluated with a time context offset (slowed) by a uniform amount. Circles in the past are alpha-blended uniformly, approaching fully transparent the further they go into the past, and fully opaque as they near the current t. This stuff is, ultimately, much cooler to see in motion, but the shading conveys the trajectory pretty well. Note: all of these samples are derived from Real World Functional Programming, albeit transformed into Clojure and using a completely different substrate based on Java2D and a Clojure scene graph library.
To give you a quick, albeit ill-defined, idea of the what the calling code looks like, here’s the function for the separated animation:
(defn separate  (let [green (->> (forever (drawing greencircle)) (translate-b (wiggle-between 300) (waggle-between 300)) (n-trail 15)) blue (->> (forever (drawing bluecircle)) (translate-b (waggle-between 200) (wiggle-between 200)) (n-trail 15))] (->> (compose-b green blue) (animator))))
->> is a threading, or pipeline macro, which takes the first
argument and evaluates the next function with the first arg as the last
arg. It’s similar to the |> operator in F#, and is really useful for
showing the flow of data or computation, especially when we have an
operation that naturally looks like a pipeline or a series of data
You can see that “green” is a behavior that draws a green circle forever, which is then fed to a translation behavior, which has, as args, two other behaviors called wiggle-between and waggle-between. These basically apply a sin or cos function, respectively, scaled by n (200 in this case), to return values that smoothly vary between [-n, n] as a function of time. You may remember from trigonometry, that sin and cos can be used to define the Unit circle….well that’s exactly what’s happening here. We’re combining our green circle behavior with a translation behavior that varies its x coordinate by [-200, 200]nrelative to the sin(t), and its y coordinate by [-200, 200] relative to cos(t) [yes, I know cos normally defines the horizontal or x component, bear with me]. It’ll basically continually follow a circular path that is 200 units distant from the origin. Finally, we pass our rotating, green circle behavior into the “n-trail” behavior, with 15 as an argument. This means that we’ll uniformly generate 14 copies of the circle into the “past,” with transparency increasing with “age”.
The definition for the blue circle is nearly identical to green, except for the radius of its circular path is 100 units larger, and its x/y coordinates are defined by waggle/wiggle. That puts the Blue circle on an orbit outside of the green circle, with its x coordinate defined by cosine, and its y defined by sine. The net effect is that both circles should be rotating in opposite directions, due to the fact that their x/y translations are handled by sin/cos, and cos/sin respectively.
The last bit of the function composes both the green and the blue behaviors, and passes that behavior into an animator function. The animator handles all of the I/O (screen drawing, etc.) for us, and happily eats behaviors (technically behaviors of type::TimeContext -> SceneGraph), rendering them at 60 fps.
Bear in mind, reactive animation is only the start. We can define reactive functions for “any” context, and happily compose them to create greater and more sophisticated programs. I plan to branch out into functional-reactive games and simulation in the near future.
Well, that’s a quick sample of some stuff. I have a boatload of material (and a library) to share, so stay tuned for more. I will dissect plenty of code samples as well.