:+: and all that (was Re: Do we need a mouse trap?)

Peter Achten peter88@cs.kun.nl
Mon, 08 Mar 1999 10:28:48 +0100


At 09:44 AM 3/4/99 +0000, Adrian Hey wrote:

>I'm not sure your message really was 'Re: Do we need a mouse trap',so you'll
>have to excuse me for replying on a new thread. I'm afraid I'm such a novice
>when it comes to ObjectIO I can't answer your question, but I can offer some
>sympathy. I also find ObjectIO's use of type constructor classes and the
>dreaded :+: operator one of the hardest things to understand. Presumably
>this has been done to enable us to do something that we couldn't do with a
>simple algebraic data type (but I'm not sure what).

The major reason to use type constructor classes is to support future extensions of the object I/O library without changing the library interface (only extending it). Type constructor classes allow you to add new modules with new instances of existing classes. 

>I suppose my biggest problem is that its hard to write type signatures with
>type constructor classes. I find that if I don't do this as a matter of
>routine I get hopelessly confused later on. For example here's a snippet
>from one of my test programs:
>
>//menuItems :: some weird class thing
>menuItems =
>      MenuItem "New..."  [MenuShortKey 'a', MenuFunction (noLS (appPIO beep))]
>  :+: MenuItem "Open..." [MenuShortKey 'b']
>  :+: MenuItem "Close"   [MenuShortKey 'c']
>  :+: MenuSeparator      []
>  :+: MenuItem "Quit"    [MenuShortKey 'd', MenuFunction (noLS (appPIO beep))]
>
>You can probably tell I haven't got very far yet, because all these either
>beep or do nothing :-)
>
>If I get the compiler to tell me the type of menuItems I get:
>
>menuItems :: :+: MenuItem (:+: MenuItem (:+: MenuItem (:+: MenuSeparator
>              MenuItem))) a *(PSt b c)
>
>Eeeeeek! I wouldn't want to try to figure that out myself, type it in, and
>maintain it whenever I changed the menu. 

The types in the object I/O library are set up in such a way that it is easy to derive the type of such expressions. Each type constructor (such as MenuItem and MenuSeparator) has an identical alternative constructor. So one has:

::  MenuItem      ... = MenuItem      ...
::  MenuSeparator ... = MenuSeparator ...

Adding layout to your example makes this relationship between type and expression clearer:

menuItems :: :+: MenuItem
            (:+: MenuItem
            (:+: MenuItem
            (:+: MenuSeparator
                 MenuItem
            ))) a *(PSt b c)
menuItems =
      MenuItem "New..."  [MenuShortKey 'a', MenuFunction (noLS (appPIO beep))]
  :+: MenuItem "Open..." [MenuShortKey 'b']
  :+: MenuItem "Close"   [MenuShortKey 'c']
  :+: MenuSeparator      []
  :+: MenuItem "Quit"    [MenuShortKey 'd', MenuFunction (noLS (appPIO beep))]


>I would much rather write something
>like:
>
>menuItems = ListLS
>  [MenuItem "New..."  [MenuShortKey 'a', MenuFunction (noLS (appPIO beep))]
>  ,MenuItem "Open..." [MenuShortKey 'b']
>  ,MenuItem "Close"   [MenuShortKey 'c']
>  ,MenuSeparator      []
>  ,MenuItem "Quit"    [MenuShortKey 'd', MenuFunction (noLS (appPIO beep))]
>  ]

But you could also have written (again observe the equivalence between the expression and its type):

menuItems :: :+: (ListLS MenuItem)
            (:+: MenuSeparator
                 (ListLS MenuItem)
            ) .ls (PSt .l .p)
menuItems
  = ListLS
       [MenuItem "New..."  [MenuShortKey 'a', MenuFunction (noLS (appPIO beep))]
       ,MenuItem "Open..." [MenuShortKey 'b']
       ,MenuItem "Close"   [MenuShortKey 'c']
       ]
    :+: MenuSeparator      []
    :+: ListLS
       [MenuItem "Quit"    [MenuShortKey 'd', MenuFunction (noLS (appPIO beep))]
       ]

Hope this will help you,

Peter