9 Your first 8 Fudget programs

In the following, we present 8 simple GUI programming examples. For each example, we show a window snapshot, the program text and explain the major points of the example. To keep the size of the presentation reasonable, many unimportant details are deliberately left unexplained. The reader is referred to the Fudget Library Reference Manual [CH97] for full information. In addition, the WWW version of this thesis contains hyper links into the Reference Manual for many combinators and types. The WWW version is located at

http://www.cs.chalmers.se/~hallgren/Thesis/
The window snapshots were made on a Unix workstation running the X Windows system and a window manager providing Windows-95-like window frames.

For practical details, such as where to get the Fudget library, which platforms are supported, and how to compile programs, see Appendix A.

9.1 "Hello, world!"

We begin with a simple program that only displays a message in a window. This example illustrates what the main program should look like, as well as some other practical details. As the window dump shows, the window manager adds a title bar to the message.

Here is the source code:

import Fudgets

main = fudlogue (shellF "Hello" (labelF "Hello, world!"))
Note:Useful programs of course contain more than one GUI element. The next example will contain two GUI elements!

9.2 The factorial function

This program illustrates how data is communicated between different parts of a Fudget program. It illustrates a simple way to combine application-specific code (in this case the factorial function) with GUI elements from the Fudget library.

The program shows a numeric entry field at the bottom and a number display at the top. Whenever the user enters a number in the entry field and presses the Return key, the factorial of that number is computed and displayed in the number display.

Here is the source code:

import Fudgets

main = fudlogue (shellF "Factorial" facF)

facF = intDispF >==< mapF fac >==< intInputF

fac 0 = 1
fac n = n * fac (n-1)
Note:The types of the new library components used in this example are:

>==< :: F a b -> F c a -> F c b
intInputF :: F Int Int
mapF :: (a -> b) -> F a b
intDispF :: F Int a
Although this program does something useful (at least compared to the two previous examples), it could be made more user friendly, e.g., by adding some explanatory text to the user interface. The next example shows how to do this.

9.3 The factorial function, with improved layout

This program shows how to use layout combinators to improve the visual appearance of a Fudget program.

We have made the factorial function example from Section 9.2 more self documenting by adding labels to the entry field and the output display. We have also changed the order of the two parts: the entry field is now above the display.

Here is the source code:

import Fudgets

main = fudlogue (shellF "Factorial" facF)

facF = placerF (revP verticalP) (
        ("x! =" `labLeftOfF` intDispF) >==<
        mapF fac >==<
        ("x =" `labLeftOfF` intInputF))

fac 0 = 1
fac n = n * fac (n-1)
Note:The types of the new library components used in this example are:

labLeftOfF :: (Graphic a) => a -> F b c -> F b c
placerF :: Placer -> F a b -> F a b
revP :: Placer -> Placer
verticalP :: Placer

9.4 An up counter

This program illustrates a more general way to combine application-specific code with GUI elements from the Fudget library. It illustrates that state information can be encapsulated. State information is often considered as difficult to handle in pure functional languages; hopefully, this counter example shows how easy it is!

This program has a button and a numeric display. Pressing the button increments the number in the display.

The application-specific code in this example sits between the button and the display. It maintains an internal counter which is incremented and output to the display whenever a click is received from the button.

Here is the source code:

import Fudgets

main = fudlogue (shellF "Up Counter" counterF)

counterF = intDispF >==< mapstateF count 0 >==< buttonF "Up"

count n Click = (n+1,[n+1])
Note:The types of the new library components used in this example are:

buttonF :: (Graphic a) => a -> F Click Click
data Click = Click
mapstateF :: (a -> b -> (a, [c])) -> a -> F b c
This and the previous examples show how serial composition creates a communication channel from one fudget to another. But what if a fudget needs input from more than one source? The next example shows one possible solution.

9.5 An up/down counter

This example illustrates how to handle input from more than one source (Figure 6).

Figure 6. The up/down counter.

The two buttons affect the same counter.

Here is the source code:

import Fudgets

main = fudlogue (shellF "Up/Down Counter" counterF)

counterF = intDispF >==< mapstateF count 0 >==<
           (buttonF filledTriangleUp >+< 
            buttonF filledTriangleDown)

count n (Left Click)   = (n+1,[n+1])
count n (Right Click)  = (n-1,[n-1])
Note:The types of the new library components used in this example are:

>+< :: F a b -> F c d -> F (Either a c) (Either b d)
filledTriangleUp :: FlexibleDrawing
filledTriangleDown :: FlexibleDrawing

9.6 An up/down/reset counter

This example shows how to create parallel compositions of many fudgets of the same type.

This program extends the counter example with yet another button. The counter can now be incremented, decremented and reset.

Here is the source code:

import Fudgets

main = fudlogue (shellF "Up/Down/Reset Counter" counterF)

counterF = intDispF >==< mapstateF count 0 >==< buttonsF

data Buttons = Up | Down | Reset deriving Eq

buttonsF = listF [(Up,     buttonF "Up"     ),
                  (Down,   buttonF "Down"   ),
                  (Reset,  buttonF "Reset"  )]

count n (Up,     Click) = (n+1,  [n+1])
count n (Down,   Click) = (n-1,  [n-1])
count n (Reset,  Click) = (0,    [0])
Note:The type of the new library component used in this example is:

listF :: (Eq a) => [(a, F b c)] -> F (a, b) (a, c)

9.7 A loadable up/down counter

This example illustrates the use of loops to handle user-interface elements that are used for both input and output (Figure 7).

Figure 7. The loadable up/down counter.

The program extends the up/down counter in Section 9.5 by allowing the user to set the counter to any value by entering it in the display field.

Here is the source code:

import Fudgets

main = fudlogue (shellF "Loadable Up/Down Counter" counterF)

counterF = loopThroughRightF (mapstateF count 0) intInputF >==<
           (buttonF filledTriangleUp >+< buttonF filledTriangleDown)

count n (Left n')              = (n',   [])
count n (Right (Left Click))   = (n+1,  [Left (n+1)])
count n (Right (Right Click))  = (n-1,  [Left (n-1)])
Note:The type of the new library component used in this example is:

loopThroughRightF :: F (Either a b) (Either c d) -> F c a -> F b d

9.8 A simple calculator

As a final example, we show how a slightly larger program, a simple calculcator, can be built using the ideas illustrated by the previous examples (Figure 8).

Figure 8. The calculator.

For simplicity, postfix notation is used, i.e., to compute 3+4 you enter 3 Ent 4 +. The source code can be found in Figure 9.

import Fudgets

main = fudlogue (shellF "Calculator" calcF)

calcF = intDispF >==< mapstateF calc [0] >==< buttonsF

data Buttons = Plus | Minus | Times | Div | Enter | Digit Int deriving Eq

buttonsF = placerF (matrixP 4) (
              listF [d 7,   d 8,  d 9,  op Div,
                     d 4,   d 5,  d 6,  op Times,
                     d 1,   d 2,  d 3,  op Minus,
                     hole,  d 0,  ent,  op Plus])
  where
    d n = (Digit n,buttonF (show n))
    ent = op Enter
    hole = (Enter,holeF)
    op o = (o,buttonF (opLabel o))
      where  opLabel Plus   = "+"
             opLabel Minus  = "-"
             opLabel Times  = "*"
             opLabel Div    = "/"
             opLabel Enter  = "Ent"

calc (n:s)    (Digit d,_)  = new (n*10+d) s
calc s        (Enter,_)    = (0:s,[])
calc (y:x:s)  (Plus,_)     = new (x+y) s
calc (y:x:s)  (Minus,_)    = new (x-y) s
calc (y:x:s)  (Times,_)    = new (x*y) s
calc (y:x:s)  (Div,_)      = new (x `div` y) s
calc s        _            = (s,[])

new n s = (n:s,[n])

Figure 9. Source code for the calculator.

Note:

The type of the new library component used in this example is:

matrixP :: Int -> Placer