In functional reactive programming (FRP), the type we call “behaviors” model non-interactive behavior.
To see why, just look at the semantic model:
t -> a, for some notion
t of time.
One can argue as follows that this model applies to interactive behavior as well. Behaviors interacting with inputs are functions of time and of inputs. Those inputs are also functions of time, so behaviors are just functions of time. I held this perspective at first, but came to see a lack of composability.
My original FRP formulations (Fran and its predecessors TBAG and ActiveVRML), as well as the much more recent library Reactive, can be and are used to describe interactive behavior. For simple sorts of things, this use works out okay. When applications get a bit richer, the interface and semantics strain. If you’ve delved a bit, you’ll have run into the signs of strain, with coping mechanisms like start times, user arguments and explicit aging of inputs, as you avoid the dreaded space-time leaks.
Suppose I define an object that pays attention to an input that’s interesting near a certain time. For instance, a cat chasing a bird in the back yard one morning last week. Now shift the behavior a week forward to this morning. The result is just a week-delayed form of the earlier behavior, displaying reactions to week-old input. The original interactive behavior (the cat) was fed input (bird and yard) from a week ago, the result was recorded, and that recording is replayed a week later.
laterObserveCat :: Behavior World -> Behavior Cat laterObserveCat = later week . cat
world :: Behavior World cat :: Behavior World -> Behavior Cat later :: Duration -> Behavior a -> Behavior a
We can also sit in the living room, five meters north of the back yard, and watch the cat via a camera.
livingRoomObserveCat :: Behavior World -> Behavior Cat livingRoomObserveCat = north (5 * meter) . cat
Typically recordings are moved both in space and in time. For example, record the cat in the back yard one week and watch the recording in the living room a week later:
replayLRL :: Behavior World -> Behavior Cat replayLRL = later week . north (5 * meter) . cat
I’ve just described transforming a non-interactive behavior (recording) in time and space. That non-interactive behavior resulted applying an interactive behavior (the cat) to some input (bird and yard). An interactive behavior is simply a function from non-interactive behavior (time-varying input) to non-interactive behavior (time-varying output).
Consider what it means to transform an interactive behavior in space. I pick up the cat and carry her five meters north, into the living room. The cat has a different perspective, which is that her environment moves five meters south. For instance, the mouse that was one meter south of her is now six meters sounth.
If I lift the cat four feet above the ground, she sees the ground now four below her. If I spin her clockwise, her environment spins counter-clockwise. If I point my shrinking ray at her, she sees her world as growing larger, and she flees from the giant mice.
We can play the perspective game in time as well as space. I point my speed-up ray at the cat, and she perceives her environment slow down.
When I say I move the cat north and she see her environment move south, I am not suggesting that there are two alternative perspectives and we might take either one. Rather, a complete picture combines both movements. The behavior my cat exhibits when I pick her up is composed of three transformations:
- The world moves south;
- The cat perceives this more southerly world and responds to it; and
- The resulting cat behavior is moved north.
livingRoomCat :: Behavior World -> Behavior Cat livingRoomCat = north (5 * meter) . cat . south (5 * meter)
Similarly with other spatial transformation, each with paired inverse transformations. And similarly in time, e.g.,
laterCat :: Behavior World -> Behavior Cat laterCat = later week . cat . earlier week
So there’s a semantically consistent model of transforming interactive behaviors in time or space, and this model explains the difference between transformation of non-interactive behavior and of interactive behavior.
Comparing these two definitions with the previous two suggests an alternative implementation. Refactoring,
laterCat :: Behavior World -> Behavior Cat laterCat = laterObserveCat . earlier week
Instead of carrying the interactive cat a week ahead in time, this definition suggests that we move the world (other than the cat) back one week, record the cat interacting with it, and hold onto that recording to watch a week later. My back-of-the-envelope calculations suggest that this second implementation is less resource-efficient than the first one, so I’ll not consider it further in this post.
When we use for an interactive setting the tools suited to non-interactive behaviors, we’re begging for space leaks (filling up recording media) and time leaks (fast-forwarding through old recorded matter to catch up with what we want to see). These problems are what people experience when programming explicitly with might call “classic FRP”, i.e., programming explicitly with (non-interactive) behaviors. Out of this experience was born “Arrow FRP”, as in Fruit and Yampa. The idea of Arrow FRP was to program with a type of “signal functions”, which can be thought of as mappings between behaviors:
type SF a b = Behavior a -> Behavior b
(Behaviors were also renamed to “signals”, but I’ll stick with “behaviors” for now.) We can just look at semantic model for signal transformers and see that they’re about interactive behavior.
A lot of work was done with this new model, mostly growing out of Paul Hudak’s group at Yale. It addressed the inherent awkwardness of the explicit-behavior style.
Despite this fundamental advantage of signal transformers, why am I still fooling around with the “classic” (pre-arrow) style?
Arrowstyle required multiple behaviors to be bunched up into one. That combination appears to thwart efficient, change-driven evaluation, which was the problem I tackled in Simply efficient functional reactivity.
- There was no room for the distinction between behaviors and events. I’d love to see how to combine them without losing expressiveness or semantic accuracy, and I haven’t yet.
- Arrow programming is very awkward without the special arrow notation and has more of a sequential style than I like with the arrow notation. I missed the functional feel of classic FRP.
Given the fundamental semantic fit between signal transformers for the problem domain of interactive behavior, I’d like to come up wh convenient and efficient packaging. The next posts will describe the packaging I’m trying out now.