:+: 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