Apart from describing how to use the Fudget Library, we try to describe the underlying ideas in such a way that the reader should be able to use them in his/her own favourite functional language.
The design of the Fudget Library started with the desire to find a good abstraction of GUI building blocks, i.e., an abstraction that makes use of the powerful abstraction mechanisms found in functional languages (higher order functions, polymorphism, etc.) and thereby, hopefully, is better than the abstractions you find in typical GUI toolkits for conventional, imperative languages. We consider an abstraction to be better if it simplifies programming, e.g., by making programs more concise and thereby easier to write, read and maintain.
An additional consideration is that in today's programming language implementations, there usually is a conflict between efficiency and high level of abstraction, so a good abstraction is one that can have a reasonably efficient implementation. If we can not have that, we have lost contact with the real world. To summarise:
It is important not to lose contact with the real world, but this does not imply that one must pass around the world explicitly.The main abstraction used in the Fudget Library is the fudget. A fudget is a process which can, via message passing, communicate with other concurrently running fudgets and with the outside world. A fudget is a first class value of a type that reflects what types of messages the fudget sends and receives. This makes communication type safe. A fudget may have an internal state, which is not visible in the type of the fudget. Fudget programming in this respect resembles object oriented programming, where state information is distributed and hidden within objects rather than centralized and exposed to arbitrary use or misuse. But the encapsulation of state information also makes fudgets easy to compose, like functions in functional languages.
Fudgets are implemented on top of stream processors, a simpler kind of process that communicates with its surroundings through an input stream and an output stream of values.
For reactive programming (Section 2.3), a more attractive program structure is a set of concurrent processes, so we introduce stream processors (Section 3). A stream processor is a process that consumes an input stream and produces an output stream. Combinators for serial composition, parallel composition and loops allow programs to be structured as a network of stream processors. Stream processors can be programmed purely functionally.
In addition to communicating with neighbours in a network, in a reactive programming context many stream processors will also need to communicate with external entities through the I/O system. We therefore introduce fudgets (Section 4), stream processors which have access to the I/O system in addition to streams for communication with other stream processors or fudgets. Reactive programs can be built as networks of fudgets.
The main use of fudgets is the construction of Graphical User Interfaces (GUIs). The building blocks in GUIs (buttons, menus, sliders, etc.) are reminiscent of physical devices in that they are self-contained units that operate more or less independently and in parallel. The reactive programming model is thus very natural for GUIs. In the Fudget Library, GUI elements are represented as fudgets. Complex user interfaces are built by combining fudgets representing GUI elements and other stream processors (Section 5).
There are two aspects in the design of GUI programs with fudgets: the computational aspect and the visual aspect. The fudget system allows you to worry about them one at a time. Thanks to the automatic layout system you can concentrate on the computational aspect during the initial development stage. You can later add layout information to the program, if the default layout isn't adequate (Section 5.5).
When designing software libraries, e.g., GUI toolkits, there is often a tension between generality and simplicity. Generality is often achieved by using many parameters. Having to give values for a lot of parameters clearly makes library components more difficult to use. In some programming languages there is a mechanism that allows a function parameter to be omitted if the function definition specifies a default value for it. This makes functions easy to use and customizable at the same time. The language that we use (Haskell) does not have such a mechanism, but the scheme used in the fudget library comes pretty close. It uses Haskell's type class system to avoid proliferation of names (Section 5.6).
The second use of fudgets that we cover is the construction of network based client/server programs. A typical server must be able to handle connections from several simultaneous clients, so it is useful to structure a server with a handler process (i.e., a handler fudget) for each client. Programs written in Haskell with the fudget library can communicate with programs written in other languages, but for the case where all programs involved are written in Haskell we show a simple way to make sure that the communication is type safe (Section 6).
The core of the program is the definition of
counterF, where two fudgets implementing
the two user interface elements and a stream processor implementing the click counter are
connected using the serial composition operator
>==<. Data flow from right to left. The
button outputs clicks that are fed to the counter. For every click, the counter increments its
internal integer state and outputs the new value to the display.
Readers mainly interested in GUI construction may want to skip directly to Section 5 and then go back to the earlier sections to learn more about what stream processors and fudgets really are.
module Main(main) where -- A simple counter import Fudgets main :: Dialogue main = fudlogue (shellF "Counter" counterF) counterF = intDispF >==< absF countSP >==< incButtonF incButtonF :: F Click Click incButtonF = buttonF "Increment" countSP :: SP Click Int countSP = putSP startstate $ mapAccumlSP inc startstate where inc n Click = (n+1,n+1) startstate = 0
|Fig. 1. The Counter Example|