
Dear Peter This issue has nothing to do with monads, but is rather a design decision how to identify GUI elements. One particular disadvantage of the w <- window [] style is that GUI creation is an (opaque) action, rather than a first-class description of the GUI. Identification values become available only after creation of GUI elements, which is less convenient than knowing them beforehand. The only 'danger' is forgetting to associate the identification value with the window, but the same problem exists with the other style: w <- window [] -- window identified by w ... close w -- window identified by w destroyed ... setTitle w "Hi there" -- unbound There is more to this. Since ID's are untyped, it happened to me a few times that I attached the wrong id to the wrong widget. Each widget is later accessed with an untyped ID, that may be connected to a completely different widget kind. With the monadic style, not an identifier is returned but the typed entity itself and thus all further access is also checked. The other problem (closing and accessing) seems only solvable within a higher-level framework. The above is not a critique on Port or GIO (honest!). I just wanted to explain part of the design reasons for Object I/O and show that it supports elegant ideas such as advocated by Daan. Off course it does :-) Since ObjectIO is in this respect on a lower-level, GIO could be implemented on top of Object/IO -- that is also why one could implement the attribute approach on top of the ObjectIO constructors. The whole issue is probably more of a question of what should be part of a medium-level GUI library (and what not). In this respect, I believe that a medium-level library should a.. Offer as much type safety as possible without sacrificing flexibility or give incomprehensible type errors. That's why GIO doesn't use multiple parameter type classes to to implement attributes as in your example :-). This is also what I like ObjectIO -- it uses little overloading (but a fairly difficult GUI type). b.. Identify entities by value instead of by ID. c.. Not offer a model for structuring state but stay in the IO monad and use simple mutable variables. Even though a model like that of ObjectIO is convenient, I believe this belongs to a higher-level library. d.. Be fully dynamic -- each attribute should be changeable later (if sensible), like layout, titles and event handlers. The advantage of having attributes that can be set or read at any time, is that we don't need to double functionality, like WindowViewDomain, getWindowViewDomain, setWindowViewDomain, ControlViewDomain, setControlViewDomain and getControlViewDomain. For a user we just have an attribute "domain" that can be get or set.
class HasDomain w where> domain :: Attr w Size > instance HasDomain Window> instance HasDomain CompoundControl>> get :: w -> Attr w a -> IO a> set :: w -> [Prop w] -> IO ()> (=:) :: Attr w a -> a -> Prop w>> test = do w <- window []> set w [domain =: Size 100 100]> ....(btw. Allthough fairly straightforward, this nice attribute stuff is invented (I think) by Koen Claessen -- I read his lecture notes).
The "dynamic" ness of the library is also used to circumvent the problem that one wants to refer to a window before it is created, for example while specifying the layout of buttons in a window. In GIO, the layout can be set later, after the creation of the child controls.
do w <- window [title =: "Demo"]> q <- button [text =: "Quit", on command =: close w]> set w [layout =: center q]Same story for event handlers. I am not sure though how inconvenient this can be when creating larger applications but I feel that the extra type safety is worth the inconvenience. (Btw, layout is another dark area: what does it mean for example when a button is put next to itself (q <<< q), or when a button created in some window is layed out in another?) All the best, Daan.
ps. I have been thinking about more 'improvements' for the (haskell?) ObjectIO library. For example, getting rid of state transformers in the GUI monad, like the GUIFun for example: "(ls,ps) -> GUI ps (ls,ps)". The idea is put every state in a "UI ls ps a" monad with "getLS :: UI ls ps ls" etc. functions. I believe that the types become much easier to comprehend but it is a fairly extensive change. Maybe we should talk about this off-line some time.