Fudget Solutions


  • Explode
  • Combination Lock
  • Viewports
  • Sliders
  • HTML Browser
  • Game of Life
  • Graph Editor
  • Changes and their Costs
  • Separation between GUI and application
  • Stream Merging
  • Dialog Boxes
  • Multiple Counters

  • Explode

    The explode game was implemented using a two basic combinators for board games. The first combinator is
    graphicButtonF :: F Drawing Click
    data Drawing = ...
    
    which creates buttons containing graphics that can be changed dynamically. graphicButtonF is used to implement the squares of the board.

    The second combinator is

    type Coord = (Int,Int)
    
    boardF :: Coord ->
              (Coord -> F a b) ->
              F (Coord,a) (Coord,b)
    
    which given the size of the board and a function from the coordinates of a square to a fudget implementing that square creates a parallel composition of square fudgets with the appropriate layout. The square fudgets are addressed with their coordinates.

    The implementation of the Explode game was done as follows (comments below):

    explodeBoardF =
        loopF (absF routeSP >==<
               boardF boardSize boardSize atomsSquareF)
      where
        routeSP = ... -- 8 lines
    
        atomsSquareF :: Coord -> F AtomColor SquareEvent
        atomsSquareF (x,y) =
            loopThroughRightF (absF cltrSP) atomsButtonF
          where
            ctrlSP = ... -- 18 lines
    
            atomsButtonF :: F (NumberOfAtoms,AtomColor) Click
            atomsButtonF = ... -- 30 lines
    
    Comments: Finally, (Thomas Hallgren)

    Combination Lock

    We define the fudget combinationLockF, which takes the combination as a string argument. The fudget has the same type (F Click Click) as buttonF, which means that we can use combinationLockF wherever a buttonF was used.
    
    
    Here is a program which displays a combination lock. The program quits when the right combination (123) is pressed.
    
    

    (Thomas Hallgren)

    Viewports

    The fudget library has a container called

    scrollF :: F a b -> F a b

    Any user interface fudget can be placed in such container, for example the Explode game.

    Sliders

    (Not implemented.)

    HTML browser

    Thomas Hallgren has written a WWW browser with the following features: About the implementation:
  • A picture of the browser in action.
  • The browser can be tested from the Fudget Demo Form.
  • Thomas has not started his own company yet...

    Game of Life

    Here is a running simulation.The simulation window has the following buttons:
    Run
    Starts/stops the simulation in this window.
    Status
    Shows the status window associated with this simulation.
    New
    Creates a new simulation window.
    Close
    Closes this simulation (and the associated status window, if it exists). If it is the last simulation, the program quits.
    Quit
    Quits the program.

    The status window shows that two simulations exist, and the first is running. The current size of the cells is 10 pixels.

    Answers to questions

    1. How is the dynamic behaviour of the program programmed?

      There are two dynamic features of the program:

      1. Simulation. Life windows compute new generations after a certain time interval. This is programmed by use of the fudget

        timerF :: F (Maybe (Int, Int)) Tick

        which, when it receives the message Just (i,0), will output a Tick every i seconds.

      2. Dynamic creation of new Life instances. This is programmed with dynListF, which accepts the messages

        (t,DynCreate f)
        spawn new fudget f, give it tag t
        (t,DynMsg m)
        send message m to fudget tagged t
        (t,DynDestroy)
        kill fudget tagged t

    2. How can each Game of Life instance keep track of all other Games of Life?

      The answer is that each Life instance does not "keep track" of other instances. Instances communicate by message passing. The messages are encoded with the data types LifeEvent and LifeCommand. Each Life instance is a fudget which accepts LifeEvent messages, and sends LifeCommand messages.

      The data types LifeEvent and LifeCommand are more or less direct encodings from the specification. Events correspond to user actions in lists, buttons and input fields:

      type Lno = Int
      data LifeEvent =
      
      NewGame |
      open a new Game of Life window.
      CloseMe |
      close itself, not quitting the program.
      QuitAll |
      close all windows, thereby quitting the program.
      BringToFrontEvt Lno |
      by selecting one of the Game of Life windows from this list, the user can bring the window 'to the front' (whatever is appropriate given the screen management of your system).
      NewSize CellSize |
      refresh display with new cell size.
      Computing Bool
      start/stop the computation and rendering of generations of life.
      Commands are used to control each life instance. In a non-distributed environment,we need 'bring window to front', 'change cell size', as well as updating the status list:
      data LifeCommand = BringToFrontCmd | SetSize CellSize 
               | LifeStatus [(Lno,String)]
      
    3. How achieves the program that a local change has a global effect? (Such as changing the size of all displayed life cells causes all Games of Life change display mode).

      `Global effects' is also achieved by message passing. In this case, a message is broadcast to all instances.

    4. Is the method used by the program in question (3) synchronous or asynchronous? With synchronous we mean a change that has immediate effect . With asynchronous we mean that it takes various, arbitrary delays for the change to have effect in all Games of Life.

      Since (3) includes a lot I/O (redrawing etc), there can be arbitrary delays (X-Windows is client/server based). But usually, changes are 'immediate', whatever that means.

    5. What is needed to turn this program in a distributed application in which Game of Life run in parallel on different machines?

      The fudget library has support for socket communication and client/server programming. The message passing technique is (almost) transparent in that any fudget in a program can be replaced by a transceiver fudget which is connected to another program. (Currently, functions cannot be passed between programs, unfortunately.)

    The main module defines LifeCommand and LifeEvent and the functions that deal with them.

    (Magnus Carlsson)

    Graph Editor

    (Not implemented.)

    Changes and their Costs

    (Not implemented.)

    Separation between GUI and application

    (Not implemented.)

    Stream Merging

    This challenge is basically a test of nondeterministic properties of the toolkit. The current implementation of stream processors used in Fudgets is purely functional and therefore deterministic. This implies that while some stream processor is computing output, input is queued. As a result, a fudget program cannot react on input if a stream processor goes into an infinite loop. The whole program deadlocks.

    On the other hand, many programming errors are avoided with deterministic merging. In most situations, it is desirable that the program is allowed to react completely to one event from the outside, before accepting the next event.

    Dialog Boxes

    This solution is not quite in the HCI school, rather I would put it in the PCI school: Programmer Computer Interaction. By this I mean programming GUI interfaces without bothering about the layout or look and feel at all. What I did was to define a class of dialog items:
    type DiaF a = F a a
    
    class DialogItem a where
        dialog :: DiaF a
    
    with instance declarations
    instance DialogItem Int where dialog = stripInputSP >^^=< intF
    instance DialogItem Bool where dialog = toggleButtonF ""
    
    for integer and boolean dialog items. Also, we would like to have
    instance DialogItem String where dialog = ?
    
    but in Haskell, we cannot use String in an instance declaration. We fix this by adding an extra method (confer the Text class) in DialogItem:
    class DialogItem a where
       dialog :: DiaF a
       dialogList :: DiaF [a]
    
    and the instance
    instance (DialogItem a) => DialogItem [a] where dialog = dialogList
    
    Now, we can add an instance for characters, which makes strings work as desired:
    instance DialogItem Char where dialogList = stripInputSP >^^=< stringF
    
    But this is of course not enough. We also want to combine dialog items when forming our dialogs. We use the product and sum types for this:
    instance (DialogItem a,DialogItem b) => DialogItem (Either a b) where
        dialog = vBoxF (dialog >+< dialog)
    
    instance (DialogItem a,DialogItem b) => DialogItem (a,b) where
        dialog = hBoxF (dialog >.< dialog)
    

    Here, we have made an arbitrary choice about the layout. Different choices of items have a vertical layout, whereas items that belong to the same choice are placed horizontally.

    Now, we have enough power to define a dialog that handles values as complicated as

    Either Int (String,Bool)
    
    for example! Here is a program that uses such a dialog:
    main = fudlogue $ shellF "T" $ f
    
    dia :: DiaF (Either Int (String,Bool))
    dia = dialog
    
    f =      labLeftOfF "Output" (displayF' (setBorderWidth 0) >=^< show)
        >==< dia
        >==< labLeftOfF "Input" (read >^=< inputDoneSP >^^=< stringF)
    
    (Here, we ignore the fact that it isn't a good idea to use read, since the program will crash if we input something which cannot be parsed. We should really use reads.)

    At the top, it displays the values output from the dialog. It also has an input field at the bottom, which let us change the value of the dialog (and therefore also the top display). The image shows the dialog after clicking on the toggle button, and entering the text "Hello" in input field to the right of it.
    We can also enter a number in the integer input field. Note how the output changes.
    Finally, we can simulate program control of the dialog by entering a value in the Input field at the bottom.

    Some notes

    class DialogItem t a where
        dialog :: t -> DiaF a
        dialogList :: t -> DiaF [a]
    
    instance (DialogItem t a) => DialogItem t [a] where
        dialog = dialogList
    
    instance (DialogItem t a,DialogItem u b) => DialogItem (t,u) (Either a b) where
        dialog = dialog2 (>+<) vBoxF
    
    instance (DialogItem t a,DialogItem u b) => DialogItem (t,u) (a,b) where
        dialog = dialog2 (>.<) hBoxF
    
    dialog2 c p (t,u) = p (dialog t `c` dialog u)
    
    instance DialogItem String Char where
        dialogList ls = label ls $ stripInputSP >^^=< stringF
    
    instance DialogItem String Int where
        dialog ls = label ls $ stripInputSP >^^=< intF
    
    label = labLeftOfF
    
    instance DialogItem String Bool where
        dialog ls = label ls $ toggleButtonF ""
    
    Here is a silly example of such a dialog:
    dia :: DiaF (Either Int (String,Bool))
    dia = dialog ("A number",("or a string","and a bool"))
    

    (Magnus Carlsson)

    Post Challenge: Multiple Counters

    The picture shows three counter views. The two views to the left are linked to the same counter, which is indicated by the number 0 in the title bar, whereas the right view is a copy, titled 1. The counter is controlled in a slightly different manner compared to the challenge, the ``auto-increment-mode'' is controlled by the toggle button Auto. It is activated in counter 0, as indicated by the black dot in the toggle button.

    The Increment button is used to manually increment the counter, independently of the state of the timer. The program is structured in two parts, one that does the counting and one for handling the copy and link operations. The latter is contained in the fudget multiF:

    multiF :: (s -> F v s) -> F s v -> s -> F a b
    
    In the expression
       multiF state_fudget view_fudget init_state
    
    state_fudget is a fudget that can maintain a state (type s). Whenever the user demands a new copy, a new state fudget will be spawned inside a dynListF. Note that the state fudget doesn't need to have a visual appearance. To start with, one state fudget will be applied to the initial state (init_state) and launched.

    Each launched state fudget resides inside a group fudget, together with at least one view. The argument view_fudget is used to create views. The view is decorated with the Copy and Link buttons, and wrapped in a shellF which adds a title bar with the group number. Whenever a new link is demanded, a new view will be launched in the group. Output from the state fudget is broadcast to all views in the group. The output from a view fudget (of type v) is fed back to the state fudget in the group.

    Here is the complete code for multiF.

    In the Multiple Counter example, the state fudget contains a counter and a timer. The view fudget is an integer display and a toggle button. The state that is output from the state fudget to the views has type

    type State = (Int,Bool)
    
    and the view fudgets send messages of type
    data View = Increment | Auto Bool
    
    to the state fudget. Here is the main program.

    (Magnus Carlsson)