questions about the IO library

Peter Achten peter88@cs.kun.nl
Thu, 10 Jun 1999 10:40:13 +0200


At 03:06 PM 6/9/99 +0100, Matt Fairtlough wrote:
>
>
>/* some questions about the IO library 0.8
>   I will be most grateful for any illumination!
>   These are in the form of comments in the following module.
>*/
Here they are. I hope this will help you out.

Greetings,

Peter Achten


**************************************
Q: My main question is this: how can you display a component of state
   without simultaneously overwriting the state with a value that does not
   depend on the original state, i.e. by performing a destructive read?
   Is it even _possible_ with CommandDialogs?  The functions lookDestroy
   and lookSave (misnamed!) below are two attempts, but both reset the
   state to fixed values.
   I've thought hard about this and tried a number of approaches; it looks
   impossible to me but the database example should explain how, only I have
   not been able to fathom it.  

A: It is actually not so difficult. Suppose you have a function g that wants
   to display the string component of your State in some way:
   
g :: String (IOState .s) -> IOState .s
   
   then this can be achieved easily as follows by a function f:

f :: *State (IOState *State) -> (*State,IOState *State)
f s=:{string} io
    = (s,g string io)

   You can see immediately from the definition of f that the state s is not 
   altered nor updated. The function g is applied to the string component.

   What you can't write down is the following, almost similar code:

f :: *State (IOState *State) -> (*State,IOState *State)
f s io
    = (s,g s.string io)
   
   Here there are two references to s which is supposed to be unique, hence the
   type checker will complain:
Type error [questions.icl,...,f]:
   "(_,_)" * attribute required but not offered at the indicated position: (^ State,IOState State)

**************************************
Q: Is there any way of recovering the state from
   a value of type *IO?

A: No. IOState represents only the current state of your GUI elements. 

Q: Why is *IO parameterised over the type of states in
   the first place?  

A: The reason is to ensure that GUI elements that are created dynamically 
   work on the very same type of state as the one that is in use. Consider
   for instance the OpenDialog function (deltaDialog). It has this type:

OpenDialog :: !(DialogDef s (IOState s)) !(IOState s) -> IOState s

   By letting DialogDef be parameterised on the same type s of state as
   IOState we can ensure that the callback functions in the dialogue work
   on the same type of state. 

Q: Is it possible to explain in a few words how the callback 
   functions in menus, windows and dialogs actually work, or do I need to read
   the sources?

A: Every interactive program that you create with the function StartIO is a
   _state transition system_. So there must be a _state_ and _state transitions_.
   The _state_ consists of two components: one that is 'invented' by the programmer,
   (which we usually refer to as 'program state') and one that is maintained 
   entirely by the I/O system: the IOState (pronounce: 'I/O state'). These two
   components show up all the time in the state transition functions which are
   actually the callback functions. They change the current state (so, a pair of
   program state and I/O state) into a new state (again, a pair of program state
   and I/O state). If f is such a state transition, then it has this type:
   
   f :: *s (IOState *s) -> (*s, IOState *s)
   
   These components are required by StartIO. Here is its type:
   
   StartIO :: !(IOSystem *s (IOState *s)) !*s !(InitialIO *s) !*World -> (!*s, !*World)
   
   Its second argument is the initial value of the program state, which must be
   provided by the programmer. As said above, the IOState is maintained entirely
   by the I/O system. The programmer can not denote a value of that type, but instead
   describes it by the initial GUI elements that should be created at start. This is
   the role of the first argument of StartIO. The I/O system creates an IOState value
   and fills it with the GUI elements as described in the IOSystem. 
   
   StartIO now has all arguments to evaluate the state transitions that are described
   by your program. What it does is to handle all events generated by the user (mouse
   clicks, keyboard input, menu selections...) and call the appropriate callback 
   functions. Each callback function is applied to the _current state_ and returns
   a _new state_. This is done by strict evaluation of both the program state and I/O
   state. 
   This recursion terminates only when a callback function applies QuitIO to its 
   IOState argument. QuitIO closes all current GUI elements and turns the I/O state
   into the _empty_ IOState. An empty IOState causes StartIO to terminate. It's result
   is the final value of the program state.

**************************************
Q: My next question is: is it possible to adopt a monadic style of 
   interface programming with the IO library 0.8 (or 1.01)?  If so, it would
   be nice to see some examples of whole programs. It is mentioned in
   the Clean book, but I don't think the examples there work with either library.
   
A: Yes: it is possible, but no: we don't have examples using the 0.8 I/O library.
   A monadic style _is_ employed inside the new Clean IDE that we are currently working
   on. This application uses the Clean Object I/O library, development version 1.0.2. 
   It is our intention to release it as open source as soon as both are sufficiently
   stable. 
   
**************************************
Q: Why do I get lots of messages of the form:
   Warning [questions.icl,_match5]: function may fail
   when compiling this module?

A: These warnings are generated because of the following pieces of Clean code:

 [dlgId,nameId,inputId,okId : _] = [2000..]

   On the left-hand side of this local definition you specify that values 
   dlgId,nameId,inputId,okId can be taken from a list with atleast four elements. 
   However, the Clean compiler only knows that the right-hand side is a list. It 
   does not know anything about the length of the list. So, from its point of view
   it is indeed possible that taking each of the values might fail and therefore it
   generates for each value a warning.
   As a more extreme example of this case, one can write down in Clean the following, 
   type correct, always failing function:
   
f = (a,b)
where
	[a,b:_] = [] 
**************************************
Q: Finally, why is the Ok button highlighted in the dialog called by lookSave 
   below, which does not specify the button Id of the Ok button, but another 
   instead?  It does not seem to be possible to specify that _no_ button be
   highlighted in the dialog for selection when return is pressed.

A: The reason is a design decision: if a program does not specify a default
   button, then the library will make the first button in the dialogue the default 
   button. (This won't occur in the Object I/O library.)
**************************************