
Hi Anthony,
Very interesting to see your program -- it is neat how one
can separate the model from the interface! (allthough the wiring
still seems a bit convoluted.)
I don't know if you have seen Koen Claessens bouncing balls
demo, but I would be quite interested to see how that would look in
Fruit (I think that especially the behaviour of the balls can be modelled nicer
in Fruit). See http://www.cs.chalmers.se/Cs/Grundutb/Kurser/afp/yahu.html
for the demo.
Anyway, I am off re-reading your papers :-)
-- Daan.
----- Original Message -----
From: "Antony Courtney"
John Meacham wrote:
I propose a simple program which pops up a window saying
'Hello World' with a button saying 'Bye' which you click and it changes the message to 'Goodbye'. if you click the button again the program exits.
Although (as I have said repeatedly) Fruit is far too experimental to be suitable as a standardized, portable production GUI toolkit, I have, just for fun, implemented this little excercise in Fruit. Code is attached.
-antony
-- Antony Courtney Grad. Student, Dept. of Computer Science, Yale University antony@apocalypse.org http://www.apocalypse.org/pub/u/antony
--------------------------------------------------------------------------------
-- -- HelloGoodbye -- simple "Hello/Goodbye" example for GUI API comparison -- proposed by John Meacham -- -- Author: Antony Courtney, 1/31/03
module HelloGoodbye where
import GUI import Arrow import AFRP import Haven import GAUtils
-- The code here is short, but heavily commented. I've chosen to err -- on the side of verbosity with comments to try and describe a little -- of the design methodology and clarify many of the combinators that are -- relatively newer than published papers on Fruit or AFRP/Yampa.
-- First, let's try to seperate the "model" from the interface:
-- Our application model is a signal function that accepts button -- press events as input, and produces a String to display paired with -- an event that occurs when the application is done executing:
appModel :: SF (Event ()) (String, Event ())
-- Our model is a simple state machine. We'll use kSwitch to switch -- from the "hello" state to the "goodbye" state on a button press: appModel = kSwitch helloSF -- initial signal function (arr fst) -- switch when input event occurs (\_ _ -> goodbyeSF) -- SF to switch in to
-- The "hello" state just outputs "hello" and does not cause -- a program termination event: helloSF :: SF (Event ()) (String,Event ()) helloSF = (constant ("Hello World", noEvent))
-- The "goodbye" state outputs "Goodbye", and produces a program termination -- event when the input event occurs: goodbyeSF :: SF (Event ()) (String,Event ()) goodbyeSF = (constant "Goodbye") &&& identity
-- Now for the GUI itself:
-- The window content (or view) consists of a button and label. The -- String to display in the label is taken as an external signal (from -- the model), and the button press events are an output signal (to be -- fed to the model) -- Some notes: -- - "GA" is the type of a GUI Arrow, which uses the order in which GUIs -- appear in the Arrow notation to determine the order in which the -- GUI components will be layed out. -- - We use "hbox" to create a horizontal layout of GUI components. -- - The use of "id" as the input signal to the button specifies default -- configuration options for the button. -- - Since there is no returnA at the end of the proc, the output signal -- from the button is the output signal of the entire GUI viewGA :: GA (String) (Event ()) viewGA = hbox $ proc s -> do ltext s >- label -> _ id >- button (btext "Bye")
-- Use feedback to wire the view to the model. The one subtlety here -- is that we must use 'iPre' to introduce an infinitesimal delay between -- the output signal of the view and the input signal of the model to ensure -- that the feedback loop is well-formed. appGUI :: GUI () (Event ()) appGUI = proc (gin,_) -> do rec (gin,s) >- unGA viewGA -> (pic,clickE) clickE >- iPre noEvent -> dClickE dClickE >- appModel -> (s,termE) (pic,termE) >- returnA
-- Full Disclosure: The above code assumes that 'runGUI' observes the -- output event of the GUI, and exits the program when the event occurs. -- The current implementation doesn't actually support this, but adding -- it would be trivial. -- -- Main :: IO () -- main = runGUI appGUI