|
Functional Reactive Programming
Simply put, FRP allows one to easily create time based programs (such as games and simulations) using functional languages, such as Haskell. These tutorials were developed by me and Tom Poliquin. Reactive is unfortunately not very friendly to new comers. It contains a large number of functions, many of which are of concern only to the internals of reactive, and are not of interest to the user. The user thus becomes lost in this vast sea of functionality. Another problem facing newbies is the esoteric naming structure of the system. Many of the names of functions (e.g. stepper) were derived from previous iterations of FRP, whose names were derived from still previous iterations, and so-on. We will attempt to present only the functions of concern to the user, and to sift out the unnecessary ones. We will use a variety of informal and perhaps inaccurate language in this discussion. To fully understand reactive, we suggest you study the formal papers found at Conal Elliot's website. Finally, we have created a forum for these tutorials.
In this discussion, we will first examine the functions and types of interest to the reactive user. Then, we will examine four sample reactive programs. The first of these programs is a furnace simulation, where a controller toggles a heater to maintain a constant temperature. The second program is a more advanced version where the user controls the temperature at which the furnace runs. The third program is robot simulation where a reactive program simulates the movement of a wheeled robot around a room. It's output is piped to a Haskell OpenGL viewer. At some later date, we may add a fourth program where the veiwer uses reactive field trip for visualization.
Types: Events and Behaviors
The two types commonly used in reactive are events and behaviors (or behaviours). An event represents discreet events, such as a mouse click or a key press. A behavior represents a continuously held value, such as the physical position of a robot or the temperature of a furnace. Each of these types packages up another type, which can be thought of as being "wrapped" in a event or behavior. For example, the temperature of a furnace might be a behavior Double, while the position of a robot might be a behaviour Point2D. It is also possible to have an event or behavior that contains a function. This is exploited in many places throught the API. Behaviors and events must interact to make up a reactive program. This often takes the form of events (such as mouse clicker and timesteps) changing the value of behaviors.
Function 1: Fmap
Fmap is a function from functors (don't worry about them). It allows you to change the contents of an event or behaviour. It takes two arguments: a function to change the event or behaviour, and the event or behaviour. An example would be: fmap (\(a,b) -> a) event. Where event is an event of type (Double,Double). This function is used extensively throught reactive, and one must be familiar with it before one can understand the other functions.
Function 2: Stepper
The stepper function is not so much a function as it is the constructor of a behaviour. Stepper is useful because it allows an event to be turned into a behaviour. It takes two arguments, a start state for the behaviour, and the event. One example would be: lastkey = stepper ' ' keyboard, where keyboard is the event that is generated when a key is pressed. The ' ' is the initial key. The behaviour lastkey would thus contain ' ' until a key was pressed, and then would contain the character of the last key pressed. If the event does not contain the desired type for the behaviour, one must use fmap to transform it.
Function 3: SnapshotWith
The snapshotWith function takes in a function, a behaviour, and an event. It returns and event of whatever the function returned. The every time the event occurs, the function is passed the current value of the behaviour, and the value of the event. An example would be furnaceAtKeyPress = snapshotWith (\temp key -> temp) furnaceTemperature keyboard, where the temp is a behaviour storing furnace temperature, and keypress is the keyboard event (see above). The result will be that furnaceAtKeyPress will be an event that contains the temperature of the furnace when a key was pressed. SnapshotWith is often used with timer events.
Function 4: AccumE
The accumE function is useful for time varying events where the last event effects the current one. An example would be any system updating the position of an object using its velocity. It takes as input a starting value, and an event containing functions. The function is a mapping from the previous value to the next value. An example of this would be myvalue = accumE 0 (fmap (\k -> (if k = ' ' then (\x -> x + 1) else (\x -> x))) keyboard). This will create the event myvalue which will grow by one whenever the space key is pressed, starting at zero. If one used stepper, one could transform myvalue into a behaviour. We used this approach in our furnace program.
Program 1: The Furnace
As Tony has become incredibly obsessed with high temperature chemical processes, we choose to create the furnace simulator. The program was designed to be extremely simple, yet still demonstrate a wide range of reactive functionality (all four functions are used). There are two behaviours in this program: the temperature and the controller. The temperature represents the temperature of the furnace. The controller represents the amount of heat which is feed to the furnace by an electric heater. Each of these behaviours is updated every time at tick occurs. The temperature is updated using a simple model of heat flow. The heater is turned on if the temperature is too low, and it is turned off if the temperature is too high. It will print out the temperature every tick.
Download the furnace
Program 2: The Furnace with Human Control
This upgraded furnace is now human controlled. Press the up and down keys to control the goal temperature of the furnace. The furnace will try to reach the temperature the human (you) supplied. There is now a new behavior which contains the goal temperature. AccumE is used on the keyboard events. Note that this also serves as an example of keyboard input in reactive.
Download the human-controlled furnace
Program 3: The Hybrid Robot Simulator
The robot simulator simulates a wall following robot that drives around a closed box. It follows the right wall using simple on-off algorithm. The robot will also turn to the left if a encounters a wall infront of it. The simulator is made up of two programs: 1. a reactive-based generator 2. a non-reactive opengl GUI. The reactive simulator prints out the position of the robot and pipes it to the opengl GUI, which recives the data and displays the robot. This program requires haskell opengl support. There have been problems with version 1 of the robot simulator due to breaking changes in the haskell opengl system. If you get errors about MatrixComponent, please try v2
Download the hybrid robot simulator
Download the hybrid robot simulator v2
Program 4: The FRP Robot Simulator
The robot simulator simulates a wall following robot that drives around a closed box. It follows the right wall using simple on-off algorithm. The robot will also turn to the left if a encounters a wall infront of it. This simulator consists of a single program that uses reactive GLUT to display the robot, handle camera view changes, and move the screen. This program requires haskell opengl support. Please note that reactive glut is incompatible with the new opengl changes, so we have attempted to patch it in version 2.
Download the FRP robot simulator
Download the FRP robot simulator v2
|