Getting Started | Documentation | Glish | Learn More | Programming | Contact Us |
Version 1.9 Build 1556 |
|
For an idea of the sorts of problems Glish is meant for and how it's used to solve them, consider a simple example where you want to repeatedly view readings generated by an instrument attached to a remote computer called ``mon". Suppose you have a program measure that reads values from the special hardware device and converts them into two floating-point arrays, x and y and another program, display, for plotting the x/y data, which you want to run on your local workstation. Therefore measure needs to run on the remote host ``mon", because that's where the special hardware resides, and you need to use display, that has a ``Take Measurements" button, to click on to instruct the hardware to take a new set of measurements.
The first problem is simply to connect measure and display so that when measure produces new values they're shown by display, and when you click the display's button measure goes off and reads new values. Figure 2.1 illustrates the flow of control and data: display tells measure to take measurements, and measure informs display when new measurements are available.
To implement this in UNIX requires constructing a session-layer protocol that then has to be implemented on top of sockets or RPC. When using Glish, though, the protocol and the communication mechanism are built-in. Every program in a Glish system communicates by generating events, messages with a name and a value. For this simple system you might write measure so that whenever new readings become available it generates an event called ``new_data". The value of this event will be a record with two elements, x and y, the two arrays of numbers it has computed from the raw measurements. You write display so that when it receives a new_data event it expects the value of the event to be a record with at least x and y fields; it then plots those values. Similarly, when you push the ``Take Measurements" button display will generate a take_data event, and whenever measure receives a take_data event it will get a new set of readings and generate a new new_data event.
Here is a Glish script that creates the two processes when executed (one process remotely) and conveys messages to each other:
m := client("measure", host="mon") d := client("display") whenever m->new_data do d->new_data( $value ) whenever d->take_data do m->take_data( $value )When Glish executes the first two lines of this script it creates instances of measure (running on the host ``mon") and display (running locally) and assigns to the variables m and d values corresponding to these Glish clients. Executing the next line:
whenever m->new_data dospecifies that whenever the client associated with m generates a new_data event, execute the following statement:
d->new_data( $value )This statement says to send a new event to the client associated with d. The event's name will be new_data and the event's value is specified by whatever comes inside the parentheses; in this case, the special expression $value, indicating the value of the most recently received event (measure's new_data event).
The last two lines of the script are analogous; they say that whenever display generates a take_data event an event with the same name and value should be sent to measure.
This system could easily be a bit more complicated. Suppose prior to viewing the measurements with display, you first want to perform some transformation on them. The transformation might, for example, calibrate the values and scale them into different units, filter out part of the values, or FFT the values to convert them into frequency spectra. Rather than building the transformation into measure, you would like your system to be modular, so you use a separate program called transform.
Figure 2.2 shows the flow of control and data in this new system. The measure values are sent to transform; transform derives some transformed values and sends them to display; and display tells measure when to take more measurements. With Glish it's easy to accommodate this change:
m := client("measure", host="mon") d := client("display") t := client("transform") whenever m->new_data do t->new_data( $value ) whenever t->transformed_data do d->new_data( $value ) whenever d->take_data do m->take_data( $value )The third line runs transform on the local host and assigns a corresponding value to the variable t. The first whenever forwards new_data events from measure to transform. The second whenever forwards transform's transformed_data events to display, but changes the event name to new_data, because that's what display expects. The third whenever is the same as before.
An important point in this example is that conceptually control and data flow directly from one program to another, in reality all events pass through the Glish interpreter as Figure 2.3 illustrates. Here solid lines show the paths events actually travel, while dashed lines indicate the conceptual flow. Although this centralized architecture doubles the cost of simple ``point-to-point" communication, it buys enormous flexibility. For example, suppose you sometimes want to use transform before viewing the data and other times you don't. You add to display another button that lets you choose between the two. It generates a set_transform event with a boolean value. If the value is true then you first pass the measurements through transform, otherwise you don't.
To accommodate this change in our Glish program you can add a global variable do_transform to control whether or not you use transform:
m := client("measure", host="mon") t := client("transform") d := client("display") do_transform := T whenever m->new_data do { if ( do_transform ) t->new_data( $value ) else d->new_data( $value ) } whenever t->transformed_data do d->new_data( $value ) whenever d->take_data do m->take_data( $value ) whenever d->set_transform do do_transform := $valueYou initialize do_transform to T, the boolean ``true" constant. You change it whenever display generates a set_transform event (see the last two lines). When measure generates a new_data event you test the variable to determine whether to pass the event's value along to transform or directly to display.
Furthermore, if the data transformation done by transform is fairly simple, you can skip writing a program to do the work and instead just use Glish. For example, suppose the transformation is to find all of the x measurements that are larger than some threshold, and then to set those x measurements to the threshold value and the corresponding y measurements to 0. You can do the transformation in Glish using:
m := client("measure", host="mon") d := client("display") do_transform := T if ( len(argv) > 0 ) thresh := as_double(argv[1]) else thresh := 1e6 whenever m->new_data do { if ( do_transform ) { too_big := $value.x > thresh $value.x[too_big] := thresh $value.y[too_big] := 0 } d->new_data( $value ) } whenever d->take_data do m->take_data( $value ) whenever d->set_transform do do_transform := $valueHere you first check to see whether any arguments were passed to the Glish script and if so you initialize thresh to be the first argument interpreted as a double precision value. If no arguments were given then you use a default value of one million.
Now whenever measure generates a new_data event and you want to do the transformation, you set too_big to a boolean mask selecting those x elements that were larger than thresh. You then set those x elements to the threshold, zero the corresponding y elements, and pass the result to display as a new_data event. You have eliminated the need for transform.
Finally, for situations in which performance is vital Glish provides point-to-point links between programs. The link statement connects events generated by one program directly to another program. The unlink statement suspends such a link (further events are sent to the central Glish interpreter) until another link. Here is the last example written to use point-to-point links:
m := client("measure", host="mon") d := client("display") link m->new_data to d->new_data if ( len(argv) > 0 ) thresh := as_double(argv[1]) else thresh := 1e6 whenever m->new_data do { too_big := $value.x > thresh $value.x[too_big] := thresh $value.y[too_big] := 0 d->new_data( $value ) } whenever d->take_data do m->take_data( $value ) whenever d->set_transform do { if ( $value ) unlink m->new_data to d->new_data else link m->new_data to d->new_data }Now you no longer need the do_transform variable. Instead you create a link for measure's new_data events directly to display. Whenever display sends a set_transform event requesting that the transformation be activated, you break the direct link between measure and display. Now when measure generates new_data events they are sent to Glish, which then transforms the data and passes it along to display.
One other important point is that because measure, transform, and display are all written in an event-driven style, each can be easily replaced by a different program that has the same ``event interface". As scientific programmers you will often want to replace measure with simulate (a program that simulates the quantity being measured), display with a non-interactive program once the measurement cycle is ironed out, and transform with a variety of different transformations. You also might want to run measure and simulate together, so you can compare simulate's model with the actual phenomenon measured by measure. The ability to quickly ``plug in" different programs in this fashion is one of Glish's main benefits. These examples illustrate the main goals of Glish: making it easy to dynamically connect processes in a distributed system and providing powerful ways to manipulate data sent between processes.