Jump to content
Main menu
Main menu
move to sidebar
hide
Navigation
Haskell
Wiki community
Recent changes
Random page
HaskellWiki
Search
Search
Create account
Log in
Personal tools
Create account
Log in
Pages for logged out editors
learn more
Contributions
Talk
Editing
Phooey
(section)
Page
Discussion
English
Read
Edit
View history
Tools
Tools
move to sidebar
hide
Actions
Read
Edit
View history
General
What links here
Related changes
Special pages
Page information
Warning:
You are not logged in. Your IP address will be publicly visible if you make any edits. If you
log in
or
create an account
, your edits will be attributed to your username, along with other benefits.
Anti-spam check. Do
not
fill this in!
== Event Examples == The shopping examples above demonstrate the simple case of outputs (<hask>total</hask>) as functions of varying inputs (<hask>apples</hask> and <hask>bananas</hask>). Events were hidden inside the implementation of [[Reactive#Data.Reactive|reactive values]]. This section shows two classic functional GUI examples involving a visible notion of [[Reactive#Data.Reactive|events]]. === Counter === Here is simple counter, which increments or decrements when the "up" or "down" button is pressed. The example is from "[http://www.citeulike.org/user/conal/article/1617415 Structuring Graphical Paradigms in TkGofer]" : [[Image:Phooey-UpDown.png]] The first piece in making this counter is a button, having a specified value and a label. The button GUI's value is an ''event'' rather than a source: <haskell> smallButton :: a -> String -> UI (Event a) </haskell> To make the up/down counter, we'll want two such buttons, labeled "up" and "down". But with what values? The buttons won't know what the counter value is, but they will know how to change the value, so the events will be function-valued. The two events resulting from the two buttons are then merged into a single function-valued event via <hask>mappend</hask>. (If you're curious about events at this point, take a detour and [[Reactive#Data.Reactive|read about them]].) The pair of buttons and combined event could be written as follows: <haskell> upDown :: Num a => UIE (a -> a) upDown = do up <- smallButton (+ 1) "up" down <- smallButton (subtract 1) "down" return (up `mappend` down) </haskell> If you've been hanging around with monad hipsters, you'll know that we can write this definition more simply: <haskell> upDown = liftM2 mappend (smallButton (+ 1) "up") (smallButton (subtract 1) "down") </haskell> Personally, I'm on an <hask>Applicative</hask> kick lately, so I prefer <hask>liftA2</hask> in place of <hask>liftM2</hask>. Still more sleekly, let's hide the <hask>liftM2</hask> (or <hask>liftA2</hask>) by using the <hask>Monoid (UI o) </hask> instance, which holds whenever <hask>Monoid o</hask>. <haskell> upDown = smallButton (+ 1) "up" `mappend` smallButton (subtract 1) "down" </haskell> To finish the counter, use the <hask>accumR</hask> function, which makes a source from an initial value and an function-valued event. The source begins as the initial value and grows by applying the functions generated by the event. <haskell> accumR :: a -> UI (Event (a -> a)) -> UI (Source a) counter :: UI () counter = title "Counter" $ fromLeft $ do e <- upDown showDisplay (0 `accumR` e) </haskell> === Calculator === The second event example is a calculator, as taken from "[http://citeseer.ist.psu.edu/vullinghs95lightweight.html Lightweight GUIs for Functional Programming]". : [[Image:Calc.png]] The basic structure of this example is just like the previous one. Each key has a function-valued event, and the keys are combined (visually and semantically) using <hask>mappend</hask>. First a single key. For variety, we'll postpone interpreting the key's event as a function. <haskell> key :: Char -> UIE Char key c = button' c [c] </haskell> We'll combine keys with the help of a friend of <hask>concatMap</hask>: <haskell> mconcatMap :: Monoid b => (a -> b) -> [a] -> b mconcatMap f = mconcat . map f </haskell> With this helper, it's especially easy to turn several keys into a row and several rows into a keyboard. <haskell> row :: [Char] -> UIE Char row = fromLeft . mconcatMap key rows :: [[Char]] -> UIE Char rows = fromTop . mconcatMap row calcKeys :: UIE Char calcKeys = rows [ "123+" , "456-" , "789*" , "C0=/" ] </haskell> Next, let's turn <hask>calcKeys</hask>'s character-valued event into a function-valued event. While the state of the [[Phooey#Counter|counter]] was a single number, the calculator state is a little more complicated. It consists of a number being formed and a continuation. <haskell> type CState = (Int, Int -> Int) startCS :: CState startCS = (0, id) </haskell> Keyboard characters have interpretations as state transitions. <haskell> cmd :: Char -> CState -> CState cmd 'C' _ = startCS cmd '=' (d,k) = (k d, const (k d)) cmd c (d,k) | isDigit c = (10*d + ord c - ord '0', k) | otherwise = (0, op c (k d)) op :: Char -> Int -> Int -> Int op c = fromJust (lookup c ops) where ops :: [(Char, Binop Int)] ops = [('+',(+)), ('-',(-)), ('*',(*)), ('/',div)] </haskell> To compute the (reactive) value, from a key-generating event, accumulate transitions, starting with the initial state, and extract the value. <haskell> compCalc :: Event Char -> Source Int compCalc key = fmap fst (startCS `accumR` fmap cmd key) </haskell> Show the result: <haskell> showCalc :: Event Char -> UI () showCalc = title "result" . showDisplay . compCalc </haskell> The whole calculator then snaps together: <haskell> calc :: UI () calc = title "Calculator" $ calcKeys >>= showCalc </haskell>
Summary:
Please note that all contributions to HaskellWiki are considered to be released under simple permissive license (see
HaskellWiki:Copyrights
for details). If you don't want your writing to be edited mercilessly and redistributed at will, then don't submit it here.
You are also promising us that you wrote this yourself, or copied it from a public domain or similar free resource.
DO NOT SUBMIT COPYRIGHTED WORK WITHOUT PERMISSION!
Cancel
Editing help
(opens in new window)
Toggle limited content width