_The Father Time Language (FrTime)_ The `frtime' collection contains the implementation of FrTime, a language that supports declarative construction of reactive systems through signals, or time-varying values. Signals are classified as either behaviors, which have a value at any point in (conceptually continuous) time, or events, which are streams of discrete occurrences. Unlike (for example) boxes, the time-varying nature of signals propagates automatically to expressions in which they are used. To interact with FrTime, set the language level to FrTime. You can also make FrTime the language for a module: (module frtime/frtime ) For the animation library and demo programs, set the language level to (module ...), open the file, and execute. The demos are perhaps the best way to learn about FrTime. Note that FrTime is experimental, and various aspects of it may change significantly, though we will try our best to maintain backwards compatibility. _Primitive Signals_ > seconds : behavior[num] This behavior updates approximately every second with the value of (current-seconds). > milliseconds : behavior[num] This behavior updates approximately every 20 milliseconds with the value of (current-milliseconds). Future versions of FrTime will provide an interface through which the programmer can create timers with arbitrary update frequencies. _Creating New Signals_ > (new-cell [behavior]) -> cell (a special behavior) The returned cell can be used as a behavior; initially its value is determined by the optional behavior given to the constructor (default is ). For example, (new-cell seconds) behaves like _seconds_. > (set-cell! cell behavior) -> void This procedure changes the value of the cell to that of the new behavior. > (event-receiver) -> event-rcvr (a special event source) The returned value can be used as an event source. Specifically, it emits an event occurrence whenever it is used as the first argument to the following procedure: > (send-event event-rcvr any) -> void Emits the second argument as an occurrence on the given event source. _Signal Processors_ > (value-now behavior[a]) -> a This procedure projects the current value of the given behavior. > (delay-by behavior[a] behavior[num]) -> behavior[a] This procedure delays the given behavior by the given number of milliseconds (which need not be constant). > (integral behavior[num] {behavior[num] = 20}) -> behavior[num] Computes a numeric approximation of the integral of the first argument with respect to time, at a minimum rate given by the second argument (interpreted in milliseconds). This procedure will probably be rewritten soon. > (derivative behavior[num]) -> behavior[num] Computes a numeric approximation of the derivative of the first argument with respect to time. This procedure needs to be rewritten. > (map-e proc event) -> event > (event . ==> . proc) -> event Returns an event source isomorphic to the given one, except that each occurrence is the result of applying the given procedure to the input event occurrence. > (filter-e pred event) -> event > (event . =#> . pred) -> event Returns a filtered version of the given event source. Only occurrences that satisfy the given predicate survive. > (merge-e event ...) -> event Merges all of the input event sources into a single event source. > (once-e event) -> event Returns an event source that carries only the first occurrence of the argument event source. (The rest are filtered out.) > (changes behavior) -> event Returns an event source which occurs each time the argument behavior changes. The value of the occurrence is the behavior's new value. > (hold event [init]) -> behavior Constructs a behavior from a given initial value (defaults to ) and an event source. The value of the behavior is the value of the most recent event occurrence. > (switch event[behavior] [behavior]) -> behavior Returns a behavior that "switches" each time the argument event occurs. The second argument is an optional initializer. > (accum-e event[a -> a] a) -> event[a] Constructs an event source by accumulating changes (carried by the given event source) over an initial value. > (accum-b event[a -> a] a) -> behavior[a] Combines functionality of accum-e and hold to construct a behavior. (accum-b ev init) = (hold init (accum-e ev init)). > (collect-e event[a] b (a b -> b)) -> event[b] Like accum-e, except the transformer function is fixed and is applied to the current accumulator and the event occurrence. > (collect-b event[a] b (a b -> b)) -> behavior[b] collect-b : collect-e :: accum-b : accum-e > (when-e behavior) -> event The returned event source carries an occurrence each time the argument behavior changes from false to true (non-false). > ( behavior ...) -> behavior FrTime provides "lifted" versions of most of Racket. This means that these procedures may be applied to behaviors, and the result automatically recomputes whenever any of the arguments changes. _Special Syntax for FrTime_ > (if boolean-behavior then-expr else-expr) SYNTAX FrTime permits the use of behaviors in conditional statements. By definition, if the condition is , then the result is also . Otherwise, the expression reflects the branch currently selected by the condition. FrTime also provides 'cond', 'and', 'or', and 'case'. > (snapshot (var ...) expr) SYNTAX This projects the current values of the named variables within the given expression. > (require (lifted module-spec proc-name ...) ...) SYNTAX > (require (lifted:nonstrict module-spec proc-name ...) ...) SYNTAX > (require (as-is module-spec proc-name ...) ...) SYNTAX > (require (as-is:unchecked module-spec proc-name ...) ...) SYNTAX FrTime provides several new 'require' forms, which can be used to import optionally modified definitions from existing libraries. The 'lifted' form takes a module specification and names of any number of primitive procedures. The imported versions of the primitives can be applied to behaviors. The 'lifted:nonstrict' form is similar, except that, when given arguments, FrTime will still perform the application, instead of simply forcing the output value to be . The 'as-is' forms do not lift imported procedures, so these should not generally be used with signals. The 'unchecked' version permits application to signals, while the plain 'as-is' always reports an error if the user attempts to apply to a signal. _Graphical Demo Programs_ To run the following animation/GUI demos, simply set the language level to FrTime, open the corresponding file, and Execute. See the demo source code for more information. orbit-mouse.ss : A collection of balls that move in circles around the mouse pointer. piston.ss : Simulation of a piston/cylinder. rotation.ss : Balls moving in circles. delay-mouse.ss : A trail of balls following the mouse. ball-on-string.ss : A ball chasing the mouse. pong.ss : A simple pong/air-hockey game. The left paddle moves with numeric keypad; the right paddle moves with the mouse. The 'r' key resets the score. net-pong-*.ss : A networked version of the pong/air-hockey game. Currently known to work under Linux. To play, open the client on one machine and the server on another. Execute both (and require if necessary, depending on language level). Evaluate (self) on each. Results will be something like: [client] > (self) #3(tid 128.148.38.2:1180 main) and [server] > (self) #3(tid 128.148.33.71:1178 main) Now tell each machine about the other: [client] > (set-cell! server (make-tid '128.148.33.71 1178 'frtime-heart)) [server] > (set-cell! client (make-tid '128.148.38.2 1180 'frtime-heart)) Note the differences between the #3(tid ...) output and the (make-tid ...) commands---there is no colon (:) between the host and port, and main becomes 'frtime-heart. After setting the cells, complete the connection by clicking the left mouse button in both animation windows. The player running the server can reset the score by pressing 'r'. pizza.ss : A simple "pizza ordering" user interface based on an HtDP exercise. calculator.ss : A simple calculator interface, also based on an HtDP exercise except that the result updates continuously as the arguments and operator change. Robb Cutler's Examples analog-clock.ss : An animated real-time clock. A slider adjusts the radius of the face. Click and drag to move the face around. growing-points.ss : A field of points that grow as the mouse approaches. needles.ss : A field of needles that point at the mouse. _FtA (Forte GUI library)_ MACROS ------ To get the macros: (require (lib "mixin-macros.ss" "frtime" "demos" "gui")) > (behavior->callbacks field-name update-call) Generates a mixin for allowing a behavior to control a widget. The mixin has two arguments. The mixin's first argument is the default value for field-name, and the seconds argument is the class being mixed. > (events->callbacks field-name update-call) Generates a mixin for allowing an event stream to drive callbacks. The one argument to the resulting mixin is the class to be extended > (callbacks->args-evts stream-name callback (args-to-callback ...)) Generates a mixin that sends an event on stream-name when callback is called. The class has an init field called [stream-name]-event-processor, which is a function. The function is applied to an event stream that has an occurrence every time callback is called, and the value of the events is a list of the arguments to the callback. The public method (get-[stream-name]) is a public method of the resulting class that gets the result of applying [stream-name]-event-processor to the stream of args-evts. FtA provides event-is-val, split-mouse-events/type, and split-key-events/type for use as initialization arguments. event-is-val can be used for [stream-name]-event-processor when the event should just be the value of the first argument of the callback. split-*-events/type sets up an appropriate split (see FrTime docs for split information, GRacket docs for key-event codes and mouse-event types). MIXINS ------ Some common mixins have already been defined and applied. To get these: (require "fred.ss" "frtime" "demos" "gui") > (add-mouse-access super-class) Derived from callbacks->args-evts. stream-name: mouse-events > (add-focus-access super-class) Derived from callbacks->args-evts stream-name: focus-events > (add-keypress-split super-class) Derived from callbacks->args-evts. stream-name: key-events > (add-callback-access value-extractor default-value super-class) value-extractor is a method of two arguments (a widget and a control event) that gets a value for the widget. default-value is the default value for the widget. Adds (get-value-e) and (get-value-b) to super-class, where get-value-e returns an event stream representing the value of the widget, and get-value-b returns a behavior representing the value of the widget. > (add-callback-access/loop value-extractor default-value super-class) does the work of add-callback-access, but also adds an initialization argument value-set, which is an event stream that sets the value of the widget at each event. > (add-focus-on-event super-class) Derived from events->callbacks. field-name: focus-when UTILITY ------- > (standard-lift widget value-method value-default) standard-lift applys a common set of mixins to the widget. It applies add-mouse-access, add-focus-access, and it applys the result of behavior->callback for label and enable. It also applies add-callback-access with value-method as the value-extractor, and value-default as the default-value. Widgets that have been standard-lift'ed: ft-button% ft-radio-box% ft-choice% ft-list-box% > (standard-lift/loop widget value-method value-default) standard-lift/loop is the same as standard-lift, except thatit applies add-callback-access/loop instead of add-callback-access. Widgets that have been standard-lift/loop'ed: ft-check-box% ft-slider% ft-text-field% simple.ss --------- Many useful utilities have been put in (require "simple.ss" "frtime" "demos" "gui") feel free to look around and use the ones you think are useful.