This is a multi-part message in MIME format.
--------------CC6BE9D09E68F38B026006D9
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
Havoc Pennington wrote:
>
> On Mon, 27 Sep 1999, Antony Courtney wrote:
> >
> > First, it is possible to write applications in Haskell using event loops
> > and mutable state. Most of the simple toolkit bindings (such as
> > TclHaskell, which is really a binding to Tk) do this.
> >
>
> Is it possible to do this without implicitly keeping all your data in some
> written-in-C component (such as the GUI components themselves)? If so,
> how? (Because if not it's going to be a pretty huge spaghetti mess, it
> seems to me - plus there's not much advantage to Haskell if you can't use
> Haskell types for your data.)
The Tk binding for Haskell provides for a GUI Monad that encapsulates
performing GUI actions, and a GUIRef monad for maintaining and updating
references to mutable state. It's a flagrant violation of the
functional style, but it works, and lets you use Haskell to write GUI
applications in the same old event-driven spaghetti style we all know
and hate.
I've attached a source file example to this message that illustrates a
non-trivial example of programming in this style in Haskell. It's a
simple Tic Tac Toe game. (WARNING: This worked with the version of
TclHaskell available as of 9/98. I haven't tested it recently. It also
needs three bitmap files in order to actually run. I'm just providing
it to illustrate what the event-driven / mutable state programming style
looks like.)
> It seems to me that the event-driven model requires "keeping your data" in
> a way that Haskell does not provide for, because you need to access "the
> same" data structure in all your event handlers over time, yet there is no
> way to communicate between event handlers without updating some fixed
> memory location...
Right. The GUIRef monad provided by TclHaskell allows the programmer to
perform get and set operations to access/update mutable state. This at
least enables the programmer to cleanly seperate the mutable state part
of the program from the purely functional part.
> The
> problem of immediate interest to me is whether it's possible to write a
> GTK+ or GNOME application in Haskell, I've been playing with Manuel's
> bindings; if Tk can be bent into a Haskell-friendly model then perhaps
> GTK+ also could be.
That would be great! For more information on the Tk binding for
Haskell, check out:
http://www.dcs.gla.ac.uk/~meurig/TclHaskell/
Again, hope this helps.
-antony
--
Antony Courtney
[EMAIL PROTECTED]
http://www.apocalypse.org/pub/u/antony
--------------CC6BE9D09E68F38B026006D9
Content-Type: Haskell Program;
name="ttt.lhs"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
filename="ttt.lhs"
An attempt at implementing a simple Tic Tac Toe game in Haskell
right now, this doesn't actually play a computer game; it just maintains
the state of each square and flips the square from blank to X to O in
response to mouse button events. Still, quite sufficient to get a feel
for programming in TclHaskell...
Antony Courtney, 9/19/98
> import Tcl
The "state" of each square is kept as a GUIRef. This is a stateful style,
but it seems to be the way things are done in TclHaskell.
> data Square = IsX | IsO | IsBlank
> data State = State Square
> type Status = GUIRef State
The Game UI consists of a grid of buttons, where each button has a "current
state".
> type GameUI = [[(Button,Status)]]
> type Row = Int
> type Col = Int
drawUI constructs the UI. We construct a row at a time, creating a frame
for each row. Within each row, we place three buttons.
> drawUI :: GUI GameUI
> drawUI =
drawButton creates a button inside the given row frame, at the given
(row, col) position.
> let drawButton :: Frame -> Row -> Col -> GUI (Button,Status)
> drawButton f row col =
> let butName = (".b" ++ show row ++ show col)
makeCmd constructs an action to invoke which is attached as the "command"
for this button, performed when the button is pressed. In our case, we
just change the bitmap displayed on the button, and update the button
state.
> makeCmd :: Row -> Col -> Button -> Status -> GUI ()
> makeCmd row col but st =
> do (State square) <- readState st
> case square of
> IsX -> do cset but [bitmap "@ttto.xbm", foreground "blue"]
> writeState st (State IsO)
> _ -> do cset but [bitmap "@tttx.xbm", foreground "red"]
> writeState st (State IsX)
body of drawButton:
> in do b <- button butName [bitmap "@empty.xbm",foreground "white"]
> -- Have to do a seperate cset here, because b doesn't
> -- exist until *after* button creation.
> st <- newState (State IsBlank)
> cset b [command (makeCmd row col b st)]
> tcl_ ["pack " ++ butName ++ " -side left" ++ " -in " ++ (wpath f)]
> return (b,st)
> drawRow :: Row -> GUI [(Button,Status)]
> drawRow row =
> let frName = ".f" ++ (show row)
> in do f <- frame frName []
> tcl_ ["pack " ++ frName ++ " -side top"]
N.B.: Cool use of higher-order monadic and list operators accumulate and
map to cons up the UI:
> accumulate (map (drawButton f row) [0..2])
> in accumulate (map drawRow [0..2])
> main :: IO ()
> main = start $
> do title dot "Tic Tac Toe"
> gameui <- drawUI
> return ()
--------------CC6BE9D09E68F38B026006D9--