:+: and all that (was Re: Do we need a mouse trap?)
Peter Achten
peter88@cs.kun.nl
Thu, 04 Mar 1999 15:25:05 +0100
Ana Maria Abrao wrote:
>...ask for
>help myself. ... My problem is
>the following: I want to glue controls and menu items
>at runtime. My idea was to make a list of strings, each
>one representing a control. Then, I would use a recursive
>program to sweep through this list, and glue the
>controls together. Since the controls are not of the same
>type, it is not possible to use ListLS. The program that
>I want to write is something like this:
>
>MNU1 (MENU nm entries) ids ps
># (error, ps)= openMenu undef (mkM entries) ps
>| error <> NoError= closeProcess ps
>| otherwise= ps
>where
>{ //mkM xs= Menu nm ( ListLS [MenuItem item []\\ item <- xs]) []
> mkM xs= Menu nm (menuM xs) []
>; menuM [x]= MenuItem x []
>; menuM [x:xs]= (MenuItem x []) :+: (menuM xs)
>};
The reason that the function menuM is rejected by the type system is because of the different derived types of the two alternatives. The first alternative returns a value of type (MenuItem .ls .ps) while the second alternative returns a value of type (:+: MenuItem ??). These types can't be coerced.
However, it is possible to write a function like menuM if you use overloading. But finding a solution for menuM is not sufficient. Let's assume that such a function exists (an example is given below the line starting with #######):
menuM :: [String] -> Maybe (m .ls .ps)
instance menuM MenuItem
instance menuM MenuSeparator
instance menuM (SubMenu m) | MenuElements m
instance menuM (:+: m1 m2) | MenuElements m1 & MenuElements m2
Then you still can't pass the result of menuM (MENU "menu" ["1","2"]) to openMenu because of internal overloading problems. This is similar to the classic example:
class toString a :: a -> String
class fromString a :: String -> a
f x = toString (fromString x)
This function can't be given a type because it is unknown what the intermediate type will be.
It seems to me that this is the wrong direction. Here are some suggestions that might help:
* use the dynamic properties of the object I/O library:
every object in the object I/O library can be constructed at run-time (open and
close menus, menu elements, and so on). Use the IODEVICE type and object I/O functions that create the proper elements. This might also fit better to your dynamic setting of a visual editor.
* use overloading all the way:
declare IODEVICE as an instance of the proper object I/O classes. (It should have two additional type variables, and probably also some more details).
Hope this helped,
Peter
###########################################################################
module typeval
// Consider the following algebraic type TYPE_VAL which pairs types
// and values of that type:
:: TYPE_VAL
= INT Int
| BOOL Bool
| PAIR TYPE_VAL TYPE_VAL
| LIST [TYPE_VAL]
/* So a TYPE_VAL is either an Int, a Bool, a pair of two TYPE_VALs,
or a list of TYPE_VALs. Examples are:
*/
e1 = INT 0
e2 = BOOL True
e3 = PAIR e1 e2
e4 = LIST [e1,e2,e3]
/* Now we write an overloaded function fromTYPE_VAL that removes the
type tags. Because a TYPE_VAL can be of the wrong type tag it
returns a Maybe result.
*/
class fromTYPE_VAL a :: TYPE_VAL -> Maybe a
// The Int and Bool instances are trivial:
instance fromTYPE_VAL Int where
fromTYPE_VAL (INT i) = Just i
fromTYPE_VAL _ = Nothing
instance fromTYPE_VAL Bool where
fromTYPE_VAL (BOOL b) = Just b
fromTYPE_VAL _ = Nothing
/* A (PAIR a b) will be mapped to (a` :^: b`) if a can be mapped to a`
and b can be mapped to b`.
*/
instance fromTYPE_VAL (:^: a b) | fromTYPE_VAL a & fromTYPE_VAL b where
fromTYPE_VAL (PAIR a b)
| isJust a` && isJust b` = Just (fromJust a` :^: fromJust b`)
| otherwise = Nothing
where
a` = fromTYPE_VAL a
b` = fromTYPE_VAL b
fromTYPE_VAL _ = Nothing
/* Analogously for (LIST [x0..xn]): it is mapped to [x0`..xn`] if x0
can be mapped to x0`, x1 can be mapped to x1`, and so on:
*/
instance fromTYPE_VAL [a] | fromTYPE_VAL a where
fromTYPE_VAL (LIST ls)
| any isNothing ls` = Nothing
| otherwise = Just (map fromJust ls`)
where
ls` = map fromTYPE_VAL ls
fromTYPE_VAL _ = Nothing
// With these definitions one can write:
Start :: (Maybe Int,Maybe Bool,Maybe (:^: Int Bool),Maybe [Int])
Start
= ( fromTYPE_VAL e1 // yields (Just 0)
, fromTYPE_VAL e2 // yields (Just True)
, fromTYPE_VAL e3 // yields (Just (0 :^: True))
, fromTYPE_VAL e4 // yields Nothing
)
import StdEnv, StdIOCommon, StdMaybe