[GUI] Re: Know you backends
Daan Leijen
daanleijen@xs4all.nl
Fri, 31 Jan 2003 18:36:36 +0100
This is a multi-part message in MIME format.
------=_NextPart_000_00A5_01C2C957.AF965D40
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
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=20
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=20
...=20
close w -- window identified by w destroyed=20
...=20
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). =20
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 ()> (=3D:) :: Attr w a -> a -> Prop w>> =
test =3D do w <- window []> set w [domain =3D: 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.=20
> do w <- window [title =3D: "Demo"]> q <- button [text =3D: "Quit", =
on command =3D: close w]> set w [layout =3D: 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.
------=_NextPart_000_00A5_01C2C957.AF965D40
Content-Type: text/html;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=3DContent-Type content=3D"text/html; =
charset=3Diso-8859-1">
<META content=3D"MSHTML 6.00.2800.1126" name=3DGENERATOR>
<STYLE></STYLE>
</HEAD>
<BODY bgColor=3D#ffffff>
<DIV><FONT face=3DArial size=3D2>Dear Peter</FONT></DIV>
<DIV style=3D"FONT: 10pt arial"> </DIV>
<BLOCKQUOTE=20
style=3D"PADDING-RIGHT: 0px; PADDING-LEFT: 5px; MARGIN-LEFT: 5px; =
BORDER-LEFT: #000000 2px solid; MARGIN-RIGHT: 0px">
<DIV type=3D"cite">This issue has nothing to do with monads, but is =
rather a=20
design decision how to identify GUI elements. One particular =
disadvantage of=20
the <BR><TT></TT></DIV>
<DIV type=3D"cite"><TT> w <- window =
[]<BR><BR></TT>style is that=20
GUI creation is an (opaque) action, rather than a first-class =
description of=20
the GUI. Identification values become available only after creation of =
GUI=20
elements, which is less convenient than knowing them beforehand. The =
only=20
'danger' is forgetting to associate the identification value with the =
window,=20
but the same problem exists with the other style:<BR></DIV>
<DL><TT>
<DD>w <- window [] =
--=20
window identified by w=20
<DD>...=20
<DD>close=20
=
w =
=20
-- window identified by w destroyed=20
<DD>...=20
<DD>setTitle w "Hi there" -- =
unbound</DD></DL></BLOCKQUOTE>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2>There is more to this. Since ID's are =
untyped, it=20
happened to me a few times that I attached the wrong id to =
the</FONT></DIV>
<DIV><FONT face=3DArial size=3D2>wrong widget. Each widget is later =
accessed with an=20
untyped ID, that may be connected</FONT></DIV>
<DIV><FONT face=3DArial size=3D2>to a completely different widget =
kind. With=20
the monadic style, not an identifier</FONT></DIV>
<DIV><FONT face=3DArial size=3D2>is returned but the typed entity itself =
and thus=20
all further access is also checked.</FONT></DIV>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2>The other problem (closing and =
accessing) seems=20
only solvable within a higher-level =
framework.</FONT></DIV></TT><TT><FONT=20
face=3DArial size=3D2>
<BLOCKQUOTE=20
style=3D"PADDING-RIGHT: 0px; PADDING-LEFT: 5px; MARGIN-LEFT: 5px; =
BORDER-LEFT: #000000 2px solid; MARGIN-RIGHT: 0px">
<DIV>The above is not a critique on Port or GIO (honest!). I just =
wanted to=20
explain part of the design reasons for Object I/O and show that it =
supports=20
elegant ideas such as advocated by Daan.</DIV>
<DIV> </DIV></BLOCKQUOTE>
<DIV><FONT face=3DArial size=3D2>Off course it does :-) Since ObjectIO =
is in this=20
respect on a lower-level, GIO could be implemented on top of Object/IO =
-- that=20
is also </FONT><FONT face=3DArial size=3D2>why one could implement =
the=20
attribute approach on top of the ObjectIO constructors. The whole issue=20
</FONT>is probably more of a question of what should be part of a =
medium-level=20
GUI library (and what not). </DIV>
<DIV> </DIV>
<DIV>In this respect, I believe that a medium-level library =
should</DIV>
<UL>
<LI>Offer as much type safety as possible without sacrificing =
flexibility or=20
give incomprehensible type errors. That's why GIO doesn't use multiple =
parameter type classes to to implement attributes as in your example =
:-). This=20
is also what I like ObjectIO -- it uses little overloading =
(but=20
a fairly difficult GUI type).</LI>
<LI>Identify entities by value instead of by ID.</LI>
<LI>Not offer a model for structuring state but stay in the IO monad =
and use=20
simple mutable variables. Even though a model like that of ObjectIO is =
convenient, I believe this belongs to a higher-level =
library.</LI>
<LI>Be fully dynamic -- each attribute should be changeable later (if=20
sensible), like layout, titles and event handlers. The advantage of =
having=20
attributes that can be set or read at any time, is that we don't need =
to=20
double functionality, like <FONT face=3DCourier>WindowViewDomain,=20
getWindowViewDomain, setWindowViewDomain, ControlViewDomain,=20
setControlViewDomain <FONT face=3DArial>and</FONT>=20
getControlViewDomain. </FONT> For a user we just have an =
attribute=20
"domain" that can be get or set.</LI></UL></FONT></TT>
<BLOCKQUOTE><PRE>> class HasDomain w where<BR>> domain =
:: Attr w Size <BR>> instance HasDomain Window<BR>> instance =
HasDomain CompoundControl<BR>><BR>> get :: w -> Attr w a -> =
IO a<BR>> set :: w -> [Prop w] -> IO ()<BR>> (=3D:) :: Attr =
w a -> a -> Prop w<BR>><BR>> test =3D do w <- window =
[]<BR>> =
set w [domain =3D: Size 100 =
100]<BR>> =
....</PRE><FONT=20
face=3DArial><TT>
<DIV><FONT face=3DArial size=3D2>(btw. Allthough fairly =
straightforward, this nice=20
attribute stuff is invented (I think) by Koen Claessen -- I =
read his=20
lecture notes).</FONT></DIV>
<DIV><FONT face=3DArial size=3D2></FONT> </DIV>
<DIV><FONT face=3DArial size=3D2>The "dynamic" ness of the library is =
also used to=20
circumvent the problem that one wants to refer to a window before it =
is=20
created, for example while specifying the layout of buttons in a =
window. In=20
GIO, the layout can be set later, after the creation of the child =
controls.=20
</FONT></DIV></FONT></TT><PRE>> do w <- window [title =3D: =
"Demo"]<BR>> q <- button [text =3D: "Quit", on command =3D: =
close w]<BR>> set w [layout =3D: center q]</PRE>
<DIV><TT><FONT face=3DArial size=3D2>Same story for event handlers. I =
am not sure=20
though how inconvenient this can be when creating larger applications=20
</FONT></TT><TT><FONT face=3DArial size=3D2>but I feel that the extra =
type safety=20
is worth the inconvenience. (Btw, layout is another dark area: what =
does it=20
mean for example when a button is put next to itself (q <<< =
q), or=20
when a button</FONT></TT></DIV>
<DIV><TT><FONT face=3DArial size=3D2>created in some window is layed =
out in=20
another?)</FONT></TT></DIV></BLOCKQUOTE>
<DIV><TT><FONT face=3DArial size=3D2>All the best,</FONT></TT></DIV>
<DIV><TT><FONT face=3DArial size=3D2> Daan.</FONT></TT></DIV>
<DIV><TT><FONT face=3DArial size=3D2></FONT></TT> </DIV>
<DIV><TT><FONT face=3DArial size=3D2>ps. I have been thinking about more =
'improvements' for the (haskell?) ObjectIO library. For example, getting =
rid of=20
state transformers in the GUI monad, like the GUIFun for example: =
"(ls,ps) ->=20
GUI ps (ls,ps)". The idea is put every state in a "UI ls ps a" =
monad with=20
"getLS :: UI ls ps ls" etc. functions. I believe that the types =
become=20
much </FONT></TT><TT><FONT face=3DArial size=3D2>easier to comprehend =
but it is a=20
fairly extensive change. Maybe we should talk about this off-line some=20
time.</FONT></TT></DIV></BODY></HTML>
------=_NextPart_000_00A5_01C2C957.AF965D40--