https://wiki.haskell.org/api.php?action=feedcontributions&user=AndreaRossato&feedformat=atomHaskellWiki - User contributions [en]2023-02-07T04:19:40ZUser contributionsMediaWiki 1.31.7https://wiki.haskell.org/index.php?title=Xmonad/Config_archive/Andrea_Rossato%27s_xmonad.hs&diff=16836Xmonad/Config archive/Andrea Rossato's xmonad.hs2007-11-13T16:35:07Z<p>AndreaRossato: removed useless imports</p>
<hr />
<div><haskell><br />
<br />
module Main (main) where<br />
<br />
import XMonad<br />
import XMonad.Config.Arossato (arossatoConfig)<br />
<br />
main :: IO ()<br />
main = xmonad arossatoConfig<br />
<br />
</haskell></div>AndreaRossatohttps://wiki.haskell.org/index.php?title=Xmonad/Config_archive/Andrea_Rossato%27s_xmonad.hs&diff=16708Xmonad/Config archive/Andrea Rossato's xmonad.hs2007-11-07T19:22:07Z<p>AndreaRossato: add haskell tags!</p>
<hr />
<div><haskell><br />
<br />
module Main (main) where<br />
<br />
import XMonad<br />
import XMonad.Config<br />
import Graphics.X11.Xlib<br />
import XMonad.Config.Arossato (arossatoConfig)<br />
<br />
main :: IO ()<br />
main = xmonad arossatoConfig<br />
<br />
</haskell></div>AndreaRossatohttps://wiki.haskell.org/index.php?title=Xmonad/Config_archive/Andrea_Rossato%27s_xmonad.hs&diff=16707Xmonad/Config archive/Andrea Rossato's xmonad.hs2007-11-07T19:21:21Z<p>AndreaRossato: my xmonad.hs</p>
<hr />
<div>module Main (main) where<br />
<br />
import XMonad<br />
import XMonad.Config<br />
import Graphics.X11.Xlib<br />
import XMonad.Config.Arossato (arossatoConfig)<br />
<br />
main :: IO ()<br />
main = xmonad arossatoConfig</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=Xmonad/Config_archive&diff=16706Xmonad/Config archive2007-11-07T19:20:15Z<p>AndreaRossato: added my xmonad.hs</p>
<hr />
<div>{{xmonad}}<br />
<br />
==xmonad configuration examples==<br />
<br />
Configuration files (Config.hs for xmonad < 0.5, xmonad.hs for xmonad >= 0.5)<br />
<br />
For more screenshots see the [[Xmonad/Screenshots]] archive.<br />
<br />
;[[/Don's Config.hs]] (0.4), [[/Don's xmonad.hs]] (0.5)<br />
:colours, use custom terminal, dynamicLogDzen<br />
[[Image:dons-config.png|center|200px]]<br />
<br />
;[[/Gwern's Config.hs]] (0.5)<br />
:[[/Gwern's Config.hs (0.4)]] (old)<br />
:Ratpoison-y keybindings; example usage of XSelection.<br />
<br />
;[[/twifkak's Config.hs]] (0.4)<br />
:modMask = mod4Mask; noBorders tabbed layout; keybindings for dzen, rotview, swapworkspaces, windowbringer, and windownavigation; urgencyhook (only in darcs xmonad).<br />
<br />
;[[/nomeatas Config.hs]] (0.4)<br />
:modMaks = mod4Mask; gnome-stuff<br />
<br />
;[[/David Roundy's xmonad.hs]] (0.5)<br />
:Combo config for small screen + xclock. Requires the xmonad-library branch, so not for the faint of heart. This config also keeps mod=mod1, and therefore moves a number of key bindings to non-standard locations (my laptop has no spare modifier keys).<br />
[[Image:droundy-config.png|center|200px]]<br />
<br />
;[[/Brent Yorgey's Config.hs]] (0.4)<br />
:probably only works with darcs xmonad. modMask = mod4Mask; DynamicLog, RotView, ViewPrev, WindowNavigation, ToggleLayouts (toggle full screen mode), UrgencyHook + dzen, FlexibleManipulate, and a few others, with keybindings for all! (Warning: lots of non-standard keybindings. =)<br />
[[Image:byorgey-config.png|center|200px]]<br />
<br />
;[[/Robert Manea's Config.hs and support scripts]] (0.4)<br />
:Customized DynamicLog, ShellPrompt, some efforts to make the colors of all components go together well<br />
[[Image:rob-config.png|center|400px]]<br />
<br />
;[[/Eric Mertens's Config.hs]] (0.4) [[/Eric Mertens' xmonad.hs]] (0.5)<br />
:Customized DynamicLog, ShellPrompt, TilePrime, Dual-head and an effort to make the statusbar similar to DWM.<br />
[[Image:glguy-config.jpg|center|400px]]<br />
<br />
;[[/vvv's Config.hs]] (0.4)<br />
:DynamicWorkspaces, (Shell|Ssh|Man)Prompt, Submap, and ion-like status bar (written in nonkosher Perl).<br />
[[Image:vvv-config.png|center|200px]]<br />
<br />
;[[/arossato's Config.hs]] (0.4) - [[/Andrea Rossato's xmonad.hs]] (0.5)<br />
:Tabbed, Xmobar with DynamicLog, (Shell|Ssh|Man)Prompt.<br />
[[Image:Arossato-config.png|center|200px]]<br />
<br />
;[[/Octoploid's xmonad.hs]] (0.5)<br />
:Xmobar with customized DynamicLog, RotView with custom keys, colors, terminal, golden ratio<br />
[[Image:Octoploid_conf.png|center|200px]]<br />
[[Image:Octoploid_conf2.png|center|200px]]<br />
<br />
;[[/Ray's xmonad.hs]] (0.5)<br />
:DynamicLog, custom manageHook, changed from default terminal, border colors, and layouts<br />
[[Image:Ray-config.png|center|200px]]<br />
<br />
== Note on uploading ==<br />
<br />
To upload your config file, create some text on this page of the form:<br />
<br />
<nowiki>; [[/you Config.hs]] (0.4)</nowiki><br />
<nowiki>: description of your setup</nowiki><br />
<br />
and save the page. This will create a new page under /you into which you<br />
can paste your Config.hs text. Wrap them in <nowiki><haskell></nowiki> and <nowiki></haskell></nowiki><br />
tags, to enable nice markup; add a nice category like <nowiki>[[Category:XMonad configuration]]</nowiki>, and upload. If you have an xmonad.hs for xmonad 0.5,<br />
upload that to<br />
<br />
<nowiki>; [[/you xmonad.hs]] (0.5)</nowiki><br />
<br />
Images can be uploaded by clicking on the 'Upload file' link, and then<br />
referring to the uploaded image as, e.g.<br />
<br />
<nowiki>[[Image:you-config.png|center|200px]]</nowiki><br />
<br />
which will scale the image correctly for a thumbnail.<br />
<br />
[[Category:XMonad configuration]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=Xmonad/Screenshots&diff=16430Xmonad/Screenshots2007-10-29T14:57:04Z<p>AndreaRossato: added my screenshot</p>
<hr />
<div>[[Image:Xmonad-logo-small.png]]<br />
<br />
'''xmonad screenshot gallery'''<br />
<br />
<gallery><br />
Image:dons-config.png|Default wide<br />
Image:droundy-config.png|Combo mode<br />
Image:byorgey-config.png|Byorgey's dzen<br />
Image:rob-config.png|Shellprompt + dzen<br />
<br />
Image:glguy-config.jpg|TilePrime + dualhead<br />
Image:vvv-config.png|DynamicWorkspaces<br />
Image:Screen-atomb-apple.png|Apple OSX<br />
Image:Screen-dishes-nornagon.png|Dishes tiling<br />
<br />
Image:Screen-dons-4x4.png|Inc master<br />
Image:Screen-dons-color-dzen.png|dzen example<br />
Image:Screen-dons-floating.png|Floating layer<br />
Image:Screen-droundy-combo.png|Combined layouts<br />
<br />
Image:Screen-droundy-mosaic2.png|Mosaic<br />
Image:Screen-ejt-spiral-dzen.png|Spiral<br />
Image:Screen-gattocarlo-tabbed.png|Ion-like tabbing<br />
Image:Screen-nomeata-ewhm.png|Gnome support<br />
<br />
Image:Screen-peter-circle.png|Circle<br />
Image:Screen-rob-dzen-cpubar.png|Nice dzen<br />
Image:Screen-rob-dzen.png|More dzen examples<br />
Image:Screen-sjanssen-kde-kicker.png|KDE support<br />
<br />
Image:Screen-dons-grid.jpg|Grid mode<br />
Image:Screen-xj2106-three-column.png|ThreeColumn<br />
Image:Screen-rickard-resize.png|ReizableTile<br />
Image:Photo-cjb-olpc.jpg|The OLPC laptop<br />
<br />
Image:Screen-emertens-gray.png|dzen with bitmaps<br />
Image:Screen-mauke-dzen.png|More dzen<br />
Image:Screen-ohmega-tab-gnome-twopane.jpg|twopane+tabbing<br />
Image:Arossato-config.png|tabbed+xmobar<br />
<br />
</gallery></div>AndreaRossatohttps://wiki.haskell.org/index.php?title=Xmonad/Config_archive/arossato%27s_Config.hs&diff=16429Xmonad/Config archive/arossato's Config.hs2007-10-29T14:52:36Z<p>AndreaRossato: my xmonad config</p>
<hr />
<div><haskell><br />
-----------------------------------------------------------------------------<br />
-- |<br />
-- Module : Config.hs<br />
-- Copyright : (c) Spencer Janssen 2007<br />
-- License : BSD3-style (see LICENSE)<br />
--<br />
-- Maintainer : dons@galois.com<br />
-- Stability : stable<br />
-- Portability : portable<br />
--<br />
-- This module specifies configurable defaults for xmonad. If you change<br />
-- values here, be sure to recompile and restart (mod-q) xmonad,<br />
-- for the changes to take effect.<br />
--<br />
------------------------------------------------------------------------<br />
<br />
module Config where<br />
<br />
--<br />
-- Useful imports<br />
--<br />
import XMonad<br />
import Operations<br />
import qualified StackSet as W<br />
import Data.Ratio<br />
import Data.Bits ((.|.))<br />
import qualified Data.Map as M<br />
import System.Exit<br />
import Graphics.X11.Xlib<br />
<br />
-- Extension-provided imports<br />
import XMonadContrib.Accordion<br />
import XMonadContrib.DynamicLog<br />
import XMonadContrib.Tabbed<br />
import XMonadContrib.NoBorders<br />
import XMonadContrib.CycleWS<br />
import XMonadContrib.XPrompt<br />
import XMonadContrib.XMonadPrompt<br />
import XMonadContrib.ShellPrompt<br />
import XMonadContrib.SshPrompt<br />
import XMonadContrib.WindowPrompt<br />
<br />
--import XMonadContrib.LayoutHelpers<br />
<br />
myXPConfig :: XPConfig<br />
myXPConfig = defaultXPConfig <br />
<br />
-- ion3 clean style<br />
myTabConfig :: TConf<br />
myTabConfig = defaultTConf {<br />
activeColor = "#8a999e" <br />
, inactiveColor = "#545d75"<br />
, activeBorderColor = "white"<br />
, inactiveBorderColor = "grey"<br />
, activeTextColor = "white"<br />
, inactiveTextColor = "grey"<br />
, tabSize = 15<br />
}<br />
<br />
-- | The default number of workspaces (virtual screens) and their names.<br />
-- By default we use numeric strings, but any string may be used as a<br />
-- workspace name. The number of workspaces is determined by the length<br />
-- of this list.<br />
--<br />
-- A tagging example:<br />
--<br />
-- > workspaces = ["web", "irc", "code" ] ++ map show [4..9]<br />
--<br />
workspaces :: [WorkspaceId]<br />
workspaces = map show [1 .. 9 :: Int]<br />
<br />
-- | modMask lets you specify which modkey you want to use. The default<br />
-- is mod1Mask ("left alt"). You may also consider using mod3Mask<br />
-- ("right alt"), which does not conflict with emacs keybindings. The<br />
-- "windows key" is usually mod4Mask.<br />
--<br />
modMask :: KeyMask<br />
modMask = mod1Mask<br />
<br />
-- |<br />
-- numlock handling:<br />
--<br />
-- The mask for the numlock key. You may need to change this on some systems.<br />
--<br />
-- You can find the numlock modifier by running "xmodmap" and looking for a<br />
-- modifier with Num_Lock bound to it:<br />
--<br />
-- $ xmodmap | grep Num<br />
-- mod2 Num_Lock (0x4d)<br />
--<br />
numlockMask :: KeyMask<br />
numlockMask = mod2Mask<br />
<br />
-- |<br />
-- Border colors for unfocused and focused windows, respectively.<br />
--<br />
normalBorderColor, focusedBorderColor :: String<br />
normalBorderColor = "black"<br />
focusedBorderColor = "white"<br />
<br />
-- |<br />
-- Width of the window border in pixels<br />
--<br />
borderWidth :: Dimension<br />
borderWidth = 1<br />
<br />
-- | Default offset of drawable screen boundaries from each physical<br />
-- screen. Anything non-zero here will leave a gap of that many pixels<br />
-- on the given edge, on the that screen. A useful gap at top of screen<br />
-- for a menu bar (e.g. 15)<br />
--<br />
-- An example, to set a top gap on monitor 1, and a gap on the bottom of<br />
-- monitor 2, you'd use a list of geometries like so:<br />
--<br />
-- > defaultGaps = [(18,0,0,0),(0,18,0,0)] -- 2 gaps on 2 monitors<br />
--<br />
-- Fields are: top, bottom, left, right.<br />
--<br />
defaultGaps :: [(Int,Int,Int,Int)]<br />
defaultGaps = [(15,0,0,0)] -- 15 for default dzen font<br />
<br />
-- |<br />
-- Execute arbitrary actions and WindowSet manipulations when<br />
-- managing a new window.<br />
------------------------------------------------------------------------<br />
-- Window rules<br />
<br />
-- | Execute arbitrary actions and WindowSet manipulations when managing<br />
-- a new window. You can use this to, for example, always float a<br />
-- particular program, or have a client always appear on a particular<br />
-- workspace.<br />
--<br />
manageHook :: Window -- ^ the new window to manage<br />
-> String -- ^ window title<br />
-> String -- ^ window resource name<br />
-> String -- ^ window resource class<br />
-> X (WindowSet -> WindowSet)<br />
<br />
-- Always float various programs:<br />
manageHook w _ _ c | c `elem` floats = fmap (W.float w . snd) (floatLocation w)<br />
where floats = ["MPlayer", "Gimp","Realplay.bin","realplay.bin"]<br />
<br />
-- Desktop panels and dock apps should be ignored by xmonad:<br />
manageHook w _ n _ | n `elem` ignore = reveal w >> return (W.delete w)<br />
where ignore = ["gnome-panel", "desktop_window", "kicker", "kdesktop"]<br />
<br />
-- Automatically send Firefox windows to the "web" workspace:<br />
-- If a workspace named "web" doesn't exist, the window will appear on the<br />
-- current workspace.<br />
manageHook _ _ "Gecko" _ = return $ W.shift "web"<br />
<br />
-- The default rule: return the WindowSet unmodified. You typically do not<br />
-- want to modify this line.<br />
manageHook _ _ _ _ = return id<br />
<br />
------------------------------------------------------------------------<br />
-- Extensible layouts<br />
<br />
-- |<br />
-- The default Layout, a selector between the layouts listed below in<br />
-- defaultLayouts.<br />
--<br />
-- |<br />
-- The list of selectable layouts<br />
layouts :: [Layout Window]<br />
layouts = [ Layout $ noBorders $ tabbed shrinkText myTabConfig<br />
, Layout $ noBorders $ Full<br />
, Layout $ tiled<br />
, Layout $ Mirror tiled<br />
, Layout Accordion<br />
-- Extension-provided layouts<br />
]<br />
where<br />
-- default tiling algorithm partitions the screen into two panes<br />
tiled = Tall nmaster delta ratio<br />
<br />
-- The default number of windows in the master pane<br />
nmaster = 1<br />
<br />
-- Default proportion of screen occupied by master pane<br />
ratio = 1%2<br />
<br />
-- Percent of screen to increment by when resizing panes<br />
delta = 3%100<br />
<br />
-- | The top level layout switcher. Most users will not need to modify this binding.<br />
--<br />
-- By default, we simply switch between the layouts listed in `layouts'<br />
-- above, but you may program your own selection behaviour here. Layout<br />
-- transformers, for example, would be hooked in here.<br />
--<br />
layoutHook :: Layout Window<br />
layoutHook = Layout $ Select layouts<br />
<br />
-- | Register with xmonad a list of layouts whose state we can preserve over restarts.<br />
-- There is typically no need to modify this list, the defaults are fine.<br />
--<br />
serialisedLayouts :: [Layout Window]<br />
serialisedLayouts = layoutHook : layouts<br />
<br />
------------------------------------------------------------------------<br />
-- Logging<br />
<br />
-- | Perform an arbitrary action on each internal state change or X event.<br />
-- Examples include:<br />
-- * do nothing<br />
-- * log the state to stdout<br />
--<br />
-- See the 'DynamicLog' extension for examples.<br />
--<br />
logHook :: X ()<br />
logHook = dynamicLog --return ()<br />
<br />
------------------------------------------------------------------------<br />
-- Key bindings:<br />
<br />
-- | The xmonad key bindings. Add, modify or remove key bindings here.<br />
--<br />
-- (The comment formatting character is used when generating the manpage)<br />
--<br />
keys :: M.Map (KeyMask, KeySym) (X ())<br />
keys = M.fromList $<br />
-- launching and killing programs<br />
[ ((modMask .|. shiftMask, xK_Return), spawn "xterm") -- %! Launch an xterm<br />
, ((modMask, xK_p ), spawn "exe=`dmenu_path | dmenu` && eval \"exec $exe\"") -- %! Launch dmenu<br />
, ((modMask .|. shiftMask, xK_p ), spawn "gmrun") -- %! Launch gmrun<br />
, ((modMask .|. shiftMask, xK_c ), kill) -- %! Close the focused window<br />
<br />
, ((modMask, xK_space ), sendMessage NextLayout) -- %! Rotate through the available layout algorithms<br />
, ((modMask .|. shiftMask, xK_space ), setLayout layoutHook) -- %! Reset the layouts on the current workspace to default<br />
<br />
, ((modMask, xK_n ), refresh) -- %! Resize viewed windows to the correct size<br />
<br />
-- move focus up or down the window stack<br />
, ((modMask, xK_Tab ), windows W.focusDown) -- %! Move focus to the next window<br />
-- , ((modMask, xK_j ), windows W.focusDown) -- %! Move focus to the next window<br />
-- , ((modMask, xK_k ), windows W.focusUp ) -- %! Move focus to the previous window<br />
, ((modMask, xK_m ), windows W.focusMaster ) -- %! Move focus to the master window<br />
<br />
-- modifying the window order<br />
, ((modMask, xK_Return), windows W.swapMaster) -- %! Swap the focused window and the master window<br />
, ((modMask .|. shiftMask, xK_j ), windows W.swapDown ) -- %! Swap the focused window with the next window<br />
, ((modMask .|. shiftMask, xK_k ), windows W.swapUp ) -- %! Swap the focused window with the previous window<br />
<br />
-- resizing the master/slave ratio<br />
, ((modMask, xK_h ), sendMessage Shrink) -- %! Shrink the master area<br />
, ((modMask, xK_l ), sendMessage Expand) -- %! Expand the master area<br />
<br />
-- floating layer support<br />
, ((modMask, xK_t ), withFocused $ windows . W.sink) -- %! Push window back into tiling<br />
<br />
-- increase or decrease number of windows in the master area<br />
, ((modMask , xK_comma ), sendMessage (IncMasterN 1)) -- %! Increment the number of windows in the master area<br />
, ((modMask , xK_period), sendMessage (IncMasterN (-1))) -- %! Deincrement the number of windows in the master area<br />
<br />
-- toggle the status bar gap<br />
, ((modMask , xK_b ), modifyGap (\i n -> let x = (defaultGaps ++ repeat (0,0,0,0)) !! i in if n == x then (0,0,0,0) else x)) -- %! Toggle the status bar gap<br />
<br />
-- quit, or restart<br />
-- , ((modMask .|. shiftMask, xK_q ), io (exitWith ExitSuccess)) -- %! Quit xmonad<br />
-- , ((modMask , xK_q ), broadcastMessage ReleaseResources >> restart Nothing True) -- %! Restart xmonad<br />
<br />
-- % Extension-provided key bindings<br />
]<br />
++<br />
-- mod-[1..9] %! Switch to workspace N<br />
-- mod-shift-[1..9] %! Move client to workspace N<br />
[((m .|. modMask, k), windows $ f i)<br />
| (i, k) <- zip workspaces [xK_1 ..]<br />
, (f, m) <- [(W.greedyView, 0), (W.shift, shiftMask .|. controlMask)]]<br />
++<br />
-- mod-{w,e,r} %! Switch to physical/Xinerama screens 1, 2, or 3<br />
-- mod-shift-{w,e,r} %! Move client to screen 1, 2, or 3<br />
[((m .|. modMask, key), screenWorkspace sc >>= flip whenJust (windows . f))<br />
| (key, sc) <- zip [xK_w, xK_e, xK_r] [0..]<br />
, (f, m) <- [(W.view, 0), (W.shift, shiftMask)]]<br />
<br />
-- % Extension-provided key bindings lists<br />
++ mykeys<br />
-- | Mouse bindings: default actions bound to mouse events<br />
--<br />
mouseBindings :: M.Map (KeyMask, Button) (Window -> X ())<br />
mouseBindings = M.fromList $<br />
-- mod-button1 %! Set the window to floating mode and move by dragging<br />
[ ((modMask, button1), (\w -> focus w >> mouseMoveWindow w))<br />
-- mod-button2 %! Raise the window to the top of the stack<br />
, ((modMask, button2), (\w -> focus w >> windows W.swapMaster))<br />
-- mod-button3 %! Set the window to floating mode and resize by dragging<br />
, ((modMask, button3), (\w -> focus w >> mouseResizeWindow w))<br />
-- you may also bind events to the mouse scroll wheel (button4 and button5)<br />
<br />
-- % Extension-provided mouse bindings<br />
]<br />
<br />
-- Extension-provided definitions<br />
mykeys :: [((KeyMask, KeySym), (X ()))]<br />
mykeys = <br />
[ ((modMask , xK_F12 ), xmonadPrompt myXPConfig)<br />
, ((modMask , xK_F3 ), shellPrompt myXPConfig)<br />
, ((modMask , xK_F4 ), sshPrompt myXPConfig )<br />
, ((modMask , xK_F5 ), windowPromptGoto myXPConfig )<br />
, ((modMask .|. shiftMask , xK_F5 ), windowPromptBring myXPConfig )<br />
-- mod . mod ,<br />
, ((modMask , xK_comma ), prevWS ) <br />
, ((modMask , xK_period), nextWS ) <br />
-- mod let mod right<br />
, ((modMask , xK_Right ), windows W.focusDown ) <br />
, ((modMask , xK_Left ), windows W.focusUp )<br />
-- altre configurazioni<br />
, ((modMask , xK_F2 ), spawn "urxvt -fg white -bg black +sb")<br />
, ((modMask .|. shiftMask , xK_F4 ), spawn "~/bin/dict.sh")<br />
, ((modMask .|. shiftMask , xK_F5 ), spawn "~/bin/urlOpen.sh")<br />
, ((modMask , xK_c ), kill) -- @@ Close the focused window<br />
]<br />
<br />
-- LocalWords: controlMask<br />
<br />
</haskell></div>AndreaRossatohttps://wiki.haskell.org/index.php?title=Xmonad/Config_archive&diff=16428Xmonad/Config archive2007-10-29T14:50:23Z<p>AndreaRossato: </p>
<hr />
<div>[[Image:Xmonad-logo-small.png|xmonad logo]]<br />
<br />
==xmonad configuration examples==<br />
<br />
* [[/Don's Config.hs]]. colours, use custom terminal, dynamicLogDzen<br />
[[Image:dons-config.png|center|200px]]<br />
<br />
* [[/Gwern's Config.hs]]. Ratpoison-y keybindings.<br />
<br />
* [[/twifkak's Config.hs]]. modMask = mod4Mask; noBorders tabbed layout; keybindings for dzen, rotview, swapworkspaces, windowbringer, and windownavigation; urgencyhook (only in darcs xmonad).<br />
<br />
* [[/nomeata’s Config.hs]]. modMaks = mod4Mask; gnome-stuff<br />
<br />
* [[/David Roundy's Config.hs]]. Combo config for small screen + xclock. Requires changes to core xmonad, so not for the faint of heart. This config also keeps mod=mod1, and therefore moves a number of key bindings to non-standard locations (my laptop has no spare modifier keys).<br />
[[Image:droundy-config.png|center|200px]]<br />
<br />
* [[/Brent Yorgey's Config.hs]]. probably only works with darcs xmonad. modMask = mod4Mask; DynamicLog, RotView, ViewPrev, WindowNavigation, ToggleLayouts (toggle full screen mode), UrgencyHook + dzen, FlexibleManipulate, and a few others, with keybindings for all! (Warning: lots of non-standard keybindings. =)<br />
[[Image:byorgey-config.png|center|200px]]<br />
<br />
* [[/Robert Manea's Config.hs and support scripts]]. Customized DynamicLog, ShellPrompt, some efforts to make the colors of all components go together well<br />
[[Image:rob-config.png|center|400px]]<br />
<br />
* [[/Eric Mertens's Config.hs]]. Customized DynamicLog, ShellPrompt, TilePrime, Dual-head and an effort to make the statusbar similar to DWM.<br />
[[Image:glguy-config.jpg|center|400px]]<br />
<br />
* [[/vvv's Config.hs]]. DynamicWorkspaces, (Shell|Ssh|Man)Prompt, Submap, and ion-like status bar (written in nonkosher Perl).<br />
[[Image:vvv-config.png|center|200px]]<br />
<br />
* [[/arossato's Config.hs]]. Tabbed, Xmobar with DynamicLog, (Shell|Ssh|Man)Prompt.<br />
[[Image:Arossato-config.png|center|200px]]<br />
<br />
== Note on uploading ==<br />
<br />
To upload your config file, create some text on this page of the form:<br />
<br />
* [ [ /you Config.hs ] ], description of your setup<br />
<br />
and save the page. This will create a new page under /you into which you<br />
can paste your Config.hs text. Wrap them in < haskell > and < / haskell ><br />
tags, to enable nice markup.<br />
<br />
Images can be uploaded by clicking on the 'Upload file' link, and then<br />
referring to the uploaded image as, e.g.<br />
<br />
[ [ Image:you-config.png|center|200px ] ]<br />
<br />
which will scale the image correctly for a thumbnail.</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=File:Arossato-config.png&diff=16427File:Arossato-config.png2007-10-29T14:47:12Z<p>AndreaRossato: </p>
<hr />
<div></div>AndreaRossatohttps://wiki.haskell.org/index.php?title=HaskellWiki_talk:Guidelines&diff=14699HaskellWiki talk:Guidelines2007-07-26T08:02:24Z<p>AndreaRossato: style consistency is far from being a bad thing: clarifying my thoughts</p>
<hr />
<div>==Headlines==<br />
<br />
[[User:BrettGiles|Brett]], this is not to offend you. It just expresses my conviction. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]]<br />
<br />
:Not to worry [[User:Wolfgang Jeltsch|Wolfgang]] - I never take offence :) - My thoughts on the usage of level 2 only were primarily due to the size balance, but also I note this is a guideline for the original mediawiki site. Does anyone know why that is? [[User:BrettGiles|BrettGiles]] 22:11, 25 February 2006 (UTC)<br />
<br />
::No idea, but it seems loose to me. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 22:40, 25 February 2006 (UTC)<br />
<br />
:::I did some research on [http://meta.wikimedia.org/wiki/Help_talk:Section metawiki] and this is the reason they start at level 2. ''"So that when a user without CSS, or a text-mode browser, or a screen reader visits, they'll be presented with a page that at least has a logical document flow."'' From what I can tell, by default, the page title is rendered as a '''h1''' element. So are single '''=''' headings. Double '''==''' are '''h2''' and so on. So from that point of view it does make sense to start at two equals. I think the real issue is that the software is slightly broken in not generating an '''h2''' for a single equals sign. So, what do you think? [[User:BrettGiles|BrettGiles]] 22:36, 26 February 2006 (UTC)<br />
<br />
::::I prefer starting with ==, like Wikipedia. &mdash;[[User:Ashley Y|Ashley Y]] 00:52, 2 March 2006 (UTC)<br />
<br />
::::MediaWiki is broken in not creating an '''h2''' element for a headline with single equal signs. So we should work around this brokeness by making our top-level headlines <tt>==</tt> headlines. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:22, 8 March 2006 (UTC)<br />
<br />
:::::I've added a respective section to [[HaskellWiki:Guidelines]]. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:40, 8 March 2006 (UTC)<br />
<br />
==Page nameing and renaming==<br />
Apparently the last rename I did so offended the author that they quit the wiki. I used to follow the guideline I just added on the main page, but had never got a response before. However, considering the size of the wiki and the number of new users (especially those that seem to not know there are guidelines :), perhaps it is time to re-institute the practice. --[[User:BrettGiles|BrettGiles]] 18:04, 24 July 2007 (UTC)<br />
<br />
==Official guidelines?==<br />
<br />
What makes a guidline an "official" guidline? What's the process? &mdash;[[User:Ashley Y|Ashley Y]] 00:53, 2 March 2006 (UTC)<br />
<br />
:I suspect that a large number of the users / contributors to this site have an academic background. My experience with academia is that some kind of consensus based decision is the most successful at getting the decision adopted. In a community like this wiki, I’m not sure how to get that. We could send out notices on the various mailing lists, we could post notes on the front page and probably other ways as well. Like any consensus decision making it would take a long time. On the other hand, those that support the site could just be autocratic, set the rule and if there is too much flak, change it. <br />
:As to what actually makes an "official" guidline, I think it is up to the '''sysops''' whether we even have such a thing. The word tends to mean that '''sysops''' are required to monitor and fix the site when these are not followed. So, we might not even want to go to official, stay at unofficial and just let the community grow the guidlines (and do the monitoring) as they see fit. Sometimes, a certain level of anarchy works fine.[[User:BrettGiles|BrettGiles]] 16:06, 2 March 2006 (UTC)<br />
::I think that the traditional consensus decision making process is too slow. In addition, not everyone is interested in every guideline question, so it is not sensible to bother everyone with each of these questions. Therefore, it seems to me that it is better to follow your “autocracy approach”. And I also doubt that it is sensible to let some sysops decide about what guidelines are official. At least, this would be a bit contrary to the wiki approach, in my opinion. So I propose that users are allowed to set up guidelines they think of as sensible—possibly after some short discussion—and to declare them as official. If others don't like the guidelines, the guidelines can still be discussed (again) and changed. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:22, 8 March 2006 (UTC)<br />
:::So this is how all that started: with an abuse of power. Is this real? I mean, it really started this way? Do you understand that autocracy is against the very wiki philosophy and is an abuse against every other wiki users? Users are not allowed to set rules and start enforcing them '''against''' other users. This is going to create tension and, in the long run, to destroy a wiki. --AndreaRossato Thu 26 2007 07:37 CEST <br />
<br />
OK. Everything on the project page should have a consensus. Everything else should be discussed here. I have adjusted the project page accordingly. &mdash;[[User:Ashley Y|Ashley Y]] 06:34, 3 March 2006 (UTC)<br />
:What is the “project page”? -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:22, 8 March 2006 (UTC)<br />
::Ah, I understand. It's [[HaskellWiki:Guidelines]]. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:40, 8 March 2006 (UTC)<br />
<br />
== Headlines ==<br />
<br />
Each page has a title which is automatically shown near the top of the page (currently centered and in red). So it doesn't make sense to put a single level 1 headline at the top of the page. (At the moment, you will see this on a few pages of this wiki. I suppose this is a legacy which comes from copying content from the old website to this wiki.)<br />
<br />
For top-level structuring, level 1 headlines (the ones denoted by single equals signs in the source) should be used. It is semantically incorrect to use level 2 headlines for this purpose. I note that level 1 headlines are currently rather large and in a font that might not be preferable. However, the solution to this is not to refrain from using level 1 headlines but to change the stylesheet appropriately. ''&mdash;says [[User:Wolfgang Jeltsch]]''<br />
<br />
:Note that is is currently being discussed. Level 1 (single equals) create an <tt>&lt;h1&gt;</tt> heading, which is the same level as the page title. We may (or may not:) switch to the standard of starting at level 2 (double equals) ''&mdash;says [[User:BrettGiles]]''<br />
<br />
:I prefer starting with level 2 as per Wikipedia. I consider the page title to be implicitly level 1 (whether it gets rendered as h1 or not). &mdash;[[User:Ashley Y|Ashley Y]] 06:34, 3 March 2006 (UTC)<br />
<br />
<br />
== Wiki Bug ==<br />
Try giving a Haskell example with the greater than or the less than sign in it. It clashes with the HTML rendering! -- says [[User:Uchchwhash|Pirated Dreams]] 04:22, 17 March 2006 (UTC)<br />
<br />
==What's Wrong With This Guidelines==<br />
I don't know how these guidelines became the official policy of this<br />
wiki, and I cannot find out how some of us decided to become the<br />
housekeepers, or the [http://c2.com/cgi/wiki?WikiGnome WikiGnome]s, of<br />
it.<br />
<br />
Still I've been involved in wiki development and I have some ideas on<br />
how a wiki should work. I tried to discuss it in the<br />
[http://www.haskell.org/pipermail/haskell-cafe/2007-July/029483.html haskell-café mailing list].<br />
<br />
Probably I should do that here too.<br />
<br />
[http://c2.com/cgi/wiki?HowToReactToGoodStyleTransgressions How to react to good style transgression?]<br />
Are these guidelines to be enforced? Aren't enforced guidelines a<br />
contradiction in terms? Sure they are...<br />
<br />
I don't think that cosmetic edits done just to enforce these<br />
guidelines are going to help this wiki on the long run. They piss<br />
newcomers off. And you are pissing off right the newcomers you would<br />
like to keep, the ones who are willing to contribute.<br />
<br />
You could start by reading the new material, and doing some edit to<br />
the content: if you collaborate the wiki way, probably your<br />
suggestions on style issues would be followed.<br />
<br />
Anyway, these are guidelines, or they pretend they are, and so you<br />
must always think about the fact the new paths can be taken.<br />
<br />
Instead what you have been doing, in the last year, is setting some<br />
rules here and reshaping the wiki in the name of those rules.<br />
<br />
===And what is right with these guidelines===<br />
So, when the "new" wiki started, there was quite a bit of discussion here and on the haskell list about what was an appropriate style for the wiki pages, considering that the site is meant to be a resource for new and exsisting Haskell users and as such, were desired to be reasonably consistent in style. These guidelines were the result. A variety of people edit pages to bring them into the stylistic guidelines, but if anyone is not happy with the current set, this seems like a good place to discuss changing them. <br />
<br />
Andrea's link to the "WikiGnome" has some very good pointers about stylistic changes being applied to the edits of ''newcomers'', and an even better one about dropping the argument. So this is my last edit about the subject :) --[[User:BrettGiles|BrettGiles]] 21:58, 25 July 2007 (UTC)<br />
:: Style consistency is not a primary goal of a wiki. A primary goal for the wiki is always having new people contribute to its grow. If you think that stylistic guidelines enforcing must be achieved regardless of the consequences than you should drop wiki technologies and use a blogging system or other kind of content management system. Instead, if you want to use a wiki, you should try to stick to the wiki philosophy as close as you can. --AndreaRossato Thu 26 2007 07:27 CEST <br />
::I'd like to point out that I perfectly understand that your effort has the aim of improving this wiki, and I don't think that style consistency is something to be avoided. What I'm trying to do, is to help finding a better way of achieving style consistency without increasing the level of entrance for new users. I hope you - Brett - will understand I have nothing against you. But I would like you to take into account the perspective of a newcomer too. I hope you are going to keep on discussing these issues with me. That would be helpful for the both of us. Thanks for your kind attention. --AndreaRossato Jul 26 2007 10:05 CEST. <br />
<br />
And that leads me to the second real big issue: page renaming.<br />
<br />
==Why Page Renaming Is BAD==<br />
<br />
Wiki page titles are [http://en.wikipedia.org/wiki/URI URI], and URI<br />
should be persistent over time.<br />
<br />
When you rename a page you are breaking URI persistence. A redirect is<br />
only a work around. A bad work around.<br />
<br />
You did a lot of page renaming lately. By doing so you broke a lot of<br />
URIs.<br />
<br />
You are also breaking the structure of this wiki, by creating a false<br />
hierarchy of pages. In a wiki all pages are top level. Hierarchy and<br />
wikis belong to different worlds.<br />
<br />
I do believe that this guidelines are used in a vary bad way. I would<br />
like you to stop. Or at least to seriously reconsider your way of<br />
enforcing them.<br />
<br />
AndreaRossato -- Wed Jul 25 2007 19:57 CEST<br />
<br />
==Amendments Proposal==<br />
I think we could use this episode to grow, as a community. I suggest some amendments to these guidelines:<br />
# enforcement: we could add something about guidelines enforcement, referring to the link I [http://c2.com/cgi/wiki?HowToReactToGoodStyleTransgressions posted before]: the user's Talk page should be used instead, and cosmetic edits should be ruled out as a bad practice, incompatible with the wiki philosopy;<br />
# page renaming: we should adopt a very strict policy for page renaming. URI persistence should be the primary goal, so page names can be changed only within a very short period of time after page creation, only for compelling reasons and only with the consent of other users, after discussing it in the talk page;<br />
# wiki structure: I'm not going to make any proposal but I would like you to think about the fact that wiki pages should be all top level, with no (false) hierarchy. This means that using slashes in page name should be considered bad practice.<br />
I hope I will find people willing to discuss these proposals.<br />
Thanks for your kind attention. --AndreaRossato Jul 26 07:59 CEST</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=HaskellWiki_talk:Guidelines&diff=14697HaskellWiki talk:Guidelines2007-07-26T05:53:16Z<p>AndreaRossato: and now some proposal</p>
<hr />
<div>==Headlines==<br />
<br />
[[User:BrettGiles|Brett]], this is not to offend you. It just expresses my conviction. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]]<br />
<br />
:Not to worry [[User:Wolfgang Jeltsch|Wolfgang]] - I never take offence :) - My thoughts on the usage of level 2 only were primarily due to the size balance, but also I note this is a guideline for the original mediawiki site. Does anyone know why that is? [[User:BrettGiles|BrettGiles]] 22:11, 25 February 2006 (UTC)<br />
<br />
::No idea, but it seems loose to me. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 22:40, 25 February 2006 (UTC)<br />
<br />
:::I did some research on [http://meta.wikimedia.org/wiki/Help_talk:Section metawiki] and this is the reason they start at level 2. ''"So that when a user without CSS, or a text-mode browser, or a screen reader visits, they'll be presented with a page that at least has a logical document flow."'' From what I can tell, by default, the page title is rendered as a '''h1''' element. So are single '''=''' headings. Double '''==''' are '''h2''' and so on. So from that point of view it does make sense to start at two equals. I think the real issue is that the software is slightly broken in not generating an '''h2''' for a single equals sign. So, what do you think? [[User:BrettGiles|BrettGiles]] 22:36, 26 February 2006 (UTC)<br />
<br />
::::I prefer starting with ==, like Wikipedia. &mdash;[[User:Ashley Y|Ashley Y]] 00:52, 2 March 2006 (UTC)<br />
<br />
::::MediaWiki is broken in not creating an '''h2''' element for a headline with single equal signs. So we should work around this brokeness by making our top-level headlines <tt>==</tt> headlines. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:22, 8 March 2006 (UTC)<br />
<br />
:::::I've added a respective section to [[HaskellWiki:Guidelines]]. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:40, 8 March 2006 (UTC)<br />
<br />
==Page nameing and renaming==<br />
Apparently the last rename I did so offended the author that they quit the wiki. I used to follow the guideline I just added on the main page, but had never got a response before. However, considering the size of the wiki and the number of new users (especially those that seem to not know there are guidelines :), perhaps it is time to re-institute the practice. --[[User:BrettGiles|BrettGiles]] 18:04, 24 July 2007 (UTC)<br />
<br />
==Official guidelines?==<br />
<br />
What makes a guidline an "official" guidline? What's the process? &mdash;[[User:Ashley Y|Ashley Y]] 00:53, 2 March 2006 (UTC)<br />
<br />
:I suspect that a large number of the users / contributors to this site have an academic background. My experience with academia is that some kind of consensus based decision is the most successful at getting the decision adopted. In a community like this wiki, I’m not sure how to get that. We could send out notices on the various mailing lists, we could post notes on the front page and probably other ways as well. Like any consensus decision making it would take a long time. On the other hand, those that support the site could just be autocratic, set the rule and if there is too much flak, change it. <br />
:As to what actually makes an "official" guidline, I think it is up to the '''sysops''' whether we even have such a thing. The word tends to mean that '''sysops''' are required to monitor and fix the site when these are not followed. So, we might not even want to go to official, stay at unofficial and just let the community grow the guidlines (and do the monitoring) as they see fit. Sometimes, a certain level of anarchy works fine.[[User:BrettGiles|BrettGiles]] 16:06, 2 March 2006 (UTC)<br />
::I think that the traditional consensus decision making process is too slow. In addition, not everyone is interested in every guideline question, so it is not sensible to bother everyone with each of these questions. Therefore, it seems to me that it is better to follow your “autocracy approach”. And I also doubt that it is sensible to let some sysops decide about what guidelines are official. At least, this would be a bit contrary to the wiki approach, in my opinion. So I propose that users are allowed to set up guidelines they think of as sensible—possibly after some short discussion—and to declare them as official. If others don't like the guidelines, the guidelines can still be discussed (again) and changed. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:22, 8 March 2006 (UTC)<br />
:::So this is how all that started: with an abuse of power. Is this real? I mean, it really started this way? Do you understand that autocracy is against the very wiki philosophy and is an abuse against every other wiki users? Users are not allowed to set rules and start enforcing them '''against''' other users. This is going to create tension and, in the long run, to destroy a wiki. --AndreaRossato Thu 26 2007 07:37 CEST <br />
<br />
OK. Everything on the project page should have a consensus. Everything else should be discussed here. I have adjusted the project page accordingly. &mdash;[[User:Ashley Y|Ashley Y]] 06:34, 3 March 2006 (UTC)<br />
:What is the “project page”? -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:22, 8 March 2006 (UTC)<br />
::Ah, I understand. It's [[HaskellWiki:Guidelines]]. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:40, 8 March 2006 (UTC)<br />
<br />
== Headlines ==<br />
<br />
Each page has a title which is automatically shown near the top of the page (currently centered and in red). So it doesn't make sense to put a single level 1 headline at the top of the page. (At the moment, you will see this on a few pages of this wiki. I suppose this is a legacy which comes from copying content from the old website to this wiki.)<br />
<br />
For top-level structuring, level 1 headlines (the ones denoted by single equals signs in the source) should be used. It is semantically incorrect to use level 2 headlines for this purpose. I note that level 1 headlines are currently rather large and in a font that might not be preferable. However, the solution to this is not to refrain from using level 1 headlines but to change the stylesheet appropriately. ''&mdash;says [[User:Wolfgang Jeltsch]]''<br />
<br />
:Note that is is currently being discussed. Level 1 (single equals) create an <tt>&lt;h1&gt;</tt> heading, which is the same level as the page title. We may (or may not:) switch to the standard of starting at level 2 (double equals) ''&mdash;says [[User:BrettGiles]]''<br />
<br />
:I prefer starting with level 2 as per Wikipedia. I consider the page title to be implicitly level 1 (whether it gets rendered as h1 or not). &mdash;[[User:Ashley Y|Ashley Y]] 06:34, 3 March 2006 (UTC)<br />
<br />
<br />
== Wiki Bug ==<br />
Try giving a Haskell example with the greater than or the less than sign in it. It clashes with the HTML rendering! -- says [[User:Uchchwhash|Pirated Dreams]] 04:22, 17 March 2006 (UTC)<br />
<br />
==What's Wrong With This Guidelines==<br />
I don't know how these guidelines became the official policy of this<br />
wiki, and I cannot find out how some of us decided to become the<br />
housekeepers, or the [http://c2.com/cgi/wiki?WikiGnome WikiGnome]s, of<br />
it.<br />
<br />
Still I've been involved in wiki development and I have some ideas on<br />
how a wiki should work. I tried to discuss it in the<br />
[http://www.haskell.org/pipermail/haskell-cafe/2007-July/029483.html haskell-café mailing list].<br />
<br />
Probably I should do that here too.<br />
<br />
[http://c2.com/cgi/wiki?HowToReactToGoodStyleTransgressions How to react to good style transgression?]<br />
Are these guidelines to be enforced? Aren't enforced guidelines a<br />
contradiction in terms? Sure they are...<br />
<br />
I don't think that cosmetic edits done just to enforce these<br />
guidelines are going to help this wiki on the long run. They piss<br />
newcomers off. And you are pissing off right the newcomers you would<br />
like to keep, the ones who are willing to contribute.<br />
<br />
You could start by reading the new material, and doing some edit to<br />
the content: if you collaborate the wiki way, probably your<br />
suggestions on style issues would be followed.<br />
<br />
Anyway, these are guidelines, or they pretend they are, and so you<br />
must always think about the fact the new paths can be taken.<br />
<br />
Instead what you have been doing, in the last year, is setting some<br />
rules here and reshaping the wiki in the name of those rules.<br />
<br />
===And what is right with these guidelines===<br />
So, when the "new" wiki started, there was quite a bit of discussion here and on the haskell list about what was an appropriate style for the wiki pages, considering that the site is meant to be a resource for new and exsisting Haskell users and as such, were desired to be reasonably consistent in style. These guidelines were the result. A variety of people edit pages to bring them into the stylistic guidelines, but if anyone is not happy with the current set, this seems like a good place to discuss changing them. <br />
<br />
Andrea's link to the "WikiGnome" has some very good pointers about stylistic changes being applied to the edits of ''newcomers'', and an even better one about dropping the argument. So this is my last edit about the subject :) --[[User:BrettGiles|BrettGiles]] 21:58, 25 July 2007 (UTC)<br />
:: Style consistency is not a primary goal of a wiki. A primary goal for the wiki is always having new people contribute to its grow. If you think that stylistic guidelines enforcing must be achieved regardless of the consequences than you should drop wiki technologies and use a blogging system or other kind of content management system. Instead, if you want to use a wiki, you should try to stick to the wiki philosophy as close as you can. --AndreaRossato Thu 26 2007 07:27 CEST <br />
<br />
And that leads me to the second real big issue: page renaming.<br />
<br />
==Why Page Renaming Is BAD==<br />
<br />
Wiki page titles are [http://en.wikipedia.org/wiki/URI URI], and URI<br />
should be persistent over time.<br />
<br />
When you rename a page you are breaking URI persistence. A redirect is<br />
only a work around. A bad work around.<br />
<br />
You did a lot of page renaming lately. By doing so you broke a lot of<br />
URIs.<br />
<br />
You are also breaking the structure of this wiki, by creating a false<br />
hierarchy of pages. In a wiki all pages are top level. Hierarchy and<br />
wikis belong to different worlds.<br />
<br />
I do believe that this guidelines are used in a vary bad way. I would<br />
like you to stop. Or at least to seriously reconsider your way of<br />
enforcing them.<br />
<br />
AndreaRossato -- Wed Jul 25 2007 19:57 CEST<br />
<br />
==Amendments Proposal==<br />
I think we could use this episode to grow, as a community. I suggest some amendments to these guidelines:<br />
# enforcement: we could add something about guidelines enforcement, referring to the link I [http://c2.com/cgi/wiki?HowToReactToGoodStyleTransgressions posted before]: the user's Talk page should be used instead, and cosmetic edits should be ruled out as a bad practice, incompatible with the wiki philosopy;<br />
# page renaming: we should adopt a very strict policy for page renaming. URI persistence should be the primary goal, so page names can be changed only within a very short period of time after page creation, only for compelling reasons and only with the consent of other users, after discussing it in the talk page;<br />
# wiki structure: I'm not going to make any proposal but I would like you to think about the fact that wiki pages should be all top level, with no (false) hierarchy. This means that using slashes in page name should be considered bad practice.<br />
I hope I will find people willing to discuss these proposals.<br />
Thanks for your kind attention. --AndreaRossato Jul 26 07:59 CEST</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=HaskellWiki_talk:Guidelines&diff=14696HaskellWiki talk:Guidelines2007-07-26T05:31:51Z<p>AndreaRossato: autocracy is bad and is against the wiki philosopy. wiki = collaboration</p>
<hr />
<div>==Headlines==<br />
<br />
[[User:BrettGiles|Brett]], this is not to offend you. It just expresses my conviction. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]]<br />
<br />
:Not to worry [[User:Wolfgang Jeltsch|Wolfgang]] - I never take offence :) - My thoughts on the usage of level 2 only were primarily due to the size balance, but also I note this is a guideline for the original mediawiki site. Does anyone know why that is? [[User:BrettGiles|BrettGiles]] 22:11, 25 February 2006 (UTC)<br />
<br />
::No idea, but it seems loose to me. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 22:40, 25 February 2006 (UTC)<br />
<br />
:::I did some research on [http://meta.wikimedia.org/wiki/Help_talk:Section metawiki] and this is the reason they start at level 2. ''"So that when a user without CSS, or a text-mode browser, or a screen reader visits, they'll be presented with a page that at least has a logical document flow."'' From what I can tell, by default, the page title is rendered as a '''h1''' element. So are single '''=''' headings. Double '''==''' are '''h2''' and so on. So from that point of view it does make sense to start at two equals. I think the real issue is that the software is slightly broken in not generating an '''h2''' for a single equals sign. So, what do you think? [[User:BrettGiles|BrettGiles]] 22:36, 26 February 2006 (UTC)<br />
<br />
::::I prefer starting with ==, like Wikipedia. &mdash;[[User:Ashley Y|Ashley Y]] 00:52, 2 March 2006 (UTC)<br />
<br />
::::MediaWiki is broken in not creating an '''h2''' element for a headline with single equal signs. So we should work around this brokeness by making our top-level headlines <tt>==</tt> headlines. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:22, 8 March 2006 (UTC)<br />
<br />
:::::I've added a respective section to [[HaskellWiki:Guidelines]]. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:40, 8 March 2006 (UTC)<br />
<br />
==Page nameing and renaming==<br />
Apparently the last rename I did so offended the author that they quit the wiki. I used to follow the guideline I just added on the main page, but had never got a response before. However, considering the size of the wiki and the number of new users (especially those that seem to not know there are guidelines :), perhaps it is time to re-institute the practice. --[[User:BrettGiles|BrettGiles]] 18:04, 24 July 2007 (UTC)<br />
<br />
==Official guidelines?==<br />
<br />
What makes a guidline an "official" guidline? What's the process? &mdash;[[User:Ashley Y|Ashley Y]] 00:53, 2 March 2006 (UTC)<br />
<br />
:I suspect that a large number of the users / contributors to this site have an academic background. My experience with academia is that some kind of consensus based decision is the most successful at getting the decision adopted. In a community like this wiki, I’m not sure how to get that. We could send out notices on the various mailing lists, we could post notes on the front page and probably other ways as well. Like any consensus decision making it would take a long time. On the other hand, those that support the site could just be autocratic, set the rule and if there is too much flak, change it. <br />
:As to what actually makes an "official" guidline, I think it is up to the '''sysops''' whether we even have such a thing. The word tends to mean that '''sysops''' are required to monitor and fix the site when these are not followed. So, we might not even want to go to official, stay at unofficial and just let the community grow the guidlines (and do the monitoring) as they see fit. Sometimes, a certain level of anarchy works fine.[[User:BrettGiles|BrettGiles]] 16:06, 2 March 2006 (UTC)<br />
::I think that the traditional consensus decision making process is too slow. In addition, not everyone is interested in every guideline question, so it is not sensible to bother everyone with each of these questions. Therefore, it seems to me that it is better to follow your “autocracy approach”. And I also doubt that it is sensible to let some sysops decide about what guidelines are official. At least, this would be a bit contrary to the wiki approach, in my opinion. So I propose that users are allowed to set up guidelines they think of as sensible—possibly after some short discussion—and to declare them as official. If others don't like the guidelines, the guidelines can still be discussed (again) and changed. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:22, 8 March 2006 (UTC)<br />
:::So this is how all that started: with an abuse of power. Is this real? I mean, it really started this way? Do you understand that autocracy is against the very wiki philosophy and is an abuse against every other wiki users? Users are not allowed to set rules and start enforcing them '''against''' other users. This is going to create tension and, in the long run, to destroy a wiki. --AndreaRossato Thu 26 2007 07:37 CEST <br />
<br />
OK. Everything on the project page should have a consensus. Everything else should be discussed here. I have adjusted the project page accordingly. &mdash;[[User:Ashley Y|Ashley Y]] 06:34, 3 March 2006 (UTC)<br />
:What is the “project page”? -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:22, 8 March 2006 (UTC)<br />
::Ah, I understand. It's [[HaskellWiki:Guidelines]]. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:40, 8 March 2006 (UTC)<br />
<br />
== Headlines ==<br />
<br />
Each page has a title which is automatically shown near the top of the page (currently centered and in red). So it doesn't make sense to put a single level 1 headline at the top of the page. (At the moment, you will see this on a few pages of this wiki. I suppose this is a legacy which comes from copying content from the old website to this wiki.)<br />
<br />
For top-level structuring, level 1 headlines (the ones denoted by single equals signs in the source) should be used. It is semantically incorrect to use level 2 headlines for this purpose. I note that level 1 headlines are currently rather large and in a font that might not be preferable. However, the solution to this is not to refrain from using level 1 headlines but to change the stylesheet appropriately. ''&mdash;says [[User:Wolfgang Jeltsch]]''<br />
<br />
:Note that is is currently being discussed. Level 1 (single equals) create an <tt>&lt;h1&gt;</tt> heading, which is the same level as the page title. We may (or may not:) switch to the standard of starting at level 2 (double equals) ''&mdash;says [[User:BrettGiles]]''<br />
<br />
:I prefer starting with level 2 as per Wikipedia. I consider the page title to be implicitly level 1 (whether it gets rendered as h1 or not). &mdash;[[User:Ashley Y|Ashley Y]] 06:34, 3 March 2006 (UTC)<br />
<br />
<br />
== Wiki Bug ==<br />
Try giving a Haskell example with the greater than or the less than sign in it. It clashes with the HTML rendering! -- says [[User:Uchchwhash|Pirated Dreams]] 04:22, 17 March 2006 (UTC)<br />
<br />
==What's Wrong With This Guidelines==<br />
I don't know how these guidelines became the official policy of this<br />
wiki, and I cannot find out how some of us decided to become the<br />
housekeepers, or the [http://c2.com/cgi/wiki?WikiGnome WikiGnome]s, of<br />
it.<br />
<br />
Still I've been involved in wiki development and I have some ideas on<br />
how a wiki should work. I tried to discuss it in the<br />
[http://www.haskell.org/pipermail/haskell-cafe/2007-July/029483.html haskell-café mailing list].<br />
<br />
Probably I should do that here too.<br />
<br />
[http://c2.com/cgi/wiki?HowToReactToGoodStyleTransgressions How to react to good style transgression?]<br />
Are these guidelines to be enforced? Aren't enforced guidelines a<br />
contradiction in terms? Sure they are...<br />
<br />
I don't think that cosmetic edits done just to enforce these<br />
guidelines are going to help this wiki on the long run. They piss<br />
newcomers off. And you are pissing off right the newcomers you would<br />
like to keep, the ones who are willing to contribute.<br />
<br />
You could start by reading the new material, and doing some edit to<br />
the content: if you collaborate the wiki way, probably your<br />
suggestions on style issues would be followed.<br />
<br />
Anyway, these are guidelines, or they pretend they are, and so you<br />
must always think about the fact the new paths can be taken.<br />
<br />
Instead what you have been doing, in the last year, is setting some<br />
rules here and reshaping the wiki in the name of those rules.<br />
<br />
===And what is right with these guidelines===<br />
So, when the "new" wiki started, there was quite a bit of discussion here and on the haskell list about what was an appropriate style for the wiki pages, considering that the site is meant to be a resource for new and exsisting Haskell users and as such, were desired to be reasonably consistent in style. These guidelines were the result. A variety of people edit pages to bring them into the stylistic guidelines, but if anyone is not happy with the current set, this seems like a good place to discuss changing them. <br />
<br />
Andrea's link to the "WikiGnome" has some very good pointers about stylistic changes being applied to the edits of ''newcomers'', and an even better one about dropping the argument. So this is my last edit about the subject :) --[[User:BrettGiles|BrettGiles]] 21:58, 25 July 2007 (UTC)<br />
:: Style consistency is not a primary goal of a wiki. A primary goal for the wiki is always having new people contribute to its grow. If you think that stylistic guidelines enforcing must be achieved regardless of the consequences than you should drop wiki technologies and use a blogging system or other kind of content management system. Instead, if you want to use a wiki, you should try to stick to the wiki philosophy as close as you can. --AndreaRossato Thu 26 2007 07:27 CEST <br />
<br />
And that leads me to the second real big issue: page renaming.<br />
<br />
==Why Page Renaming Is BAD==<br />
<br />
Wiki page titles are [http://en.wikipedia.org/wiki/URI URI], and URI<br />
should be persistent over time.<br />
<br />
When you rename a page you are breaking URI persistence. A redirect is<br />
only a work around. A bad work around.<br />
<br />
You did a lot of page renaming lately. By doing so you broke a lot of<br />
URIs.<br />
<br />
You are also breaking the structure of this wiki, by creating a false<br />
hierarchy of pages. In a wiki all pages are top level. Hierarchy and<br />
wikis belong to different worlds.<br />
<br />
I do believe that this guidelines are used in a vary bad way. I would<br />
like you to stop. Or at least to seriously reconsider your way of<br />
enforcing them.<br />
<br />
AndreaRossato -- Wed Jul 25 2007 19:57 CEST</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=HaskellWiki_talk:Guidelines&diff=14695HaskellWiki talk:Guidelines2007-07-26T05:24:09Z<p>AndreaRossato: Style consistency is not a primary goal of a wiki: getting new contribution and impoving contet is the goal.</p>
<hr />
<div>==Headlines==<br />
<br />
[[User:BrettGiles|Brett]], this is not to offend you. It just expresses my conviction. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]]<br />
<br />
:Not to worry [[User:Wolfgang Jeltsch|Wolfgang]] - I never take offence :) - My thoughts on the usage of level 2 only were primarily due to the size balance, but also I note this is a guideline for the original mediawiki site. Does anyone know why that is? [[User:BrettGiles|BrettGiles]] 22:11, 25 February 2006 (UTC)<br />
<br />
::No idea, but it seems loose to me. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 22:40, 25 February 2006 (UTC)<br />
<br />
:::I did some research on [http://meta.wikimedia.org/wiki/Help_talk:Section metawiki] and this is the reason they start at level 2. ''"So that when a user without CSS, or a text-mode browser, or a screen reader visits, they'll be presented with a page that at least has a logical document flow."'' From what I can tell, by default, the page title is rendered as a '''h1''' element. So are single '''=''' headings. Double '''==''' are '''h2''' and so on. So from that point of view it does make sense to start at two equals. I think the real issue is that the software is slightly broken in not generating an '''h2''' for a single equals sign. So, what do you think? [[User:BrettGiles|BrettGiles]] 22:36, 26 February 2006 (UTC)<br />
<br />
::::I prefer starting with ==, like Wikipedia. &mdash;[[User:Ashley Y|Ashley Y]] 00:52, 2 March 2006 (UTC)<br />
<br />
::::MediaWiki is broken in not creating an '''h2''' element for a headline with single equal signs. So we should work around this brokeness by making our top-level headlines <tt>==</tt> headlines. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:22, 8 March 2006 (UTC)<br />
<br />
:::::I've added a respective section to [[HaskellWiki:Guidelines]]. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:40, 8 March 2006 (UTC)<br />
<br />
==Page nameing and renaming==<br />
Apparently the last rename I did so offended the author that they quit the wiki. I used to follow the guideline I just added on the main page, but had never got a response before. However, considering the size of the wiki and the number of new users (especially those that seem to not know there are guidelines :), perhaps it is time to re-institute the practice. --[[User:BrettGiles|BrettGiles]] 18:04, 24 July 2007 (UTC)<br />
<br />
==Official guidelines?==<br />
<br />
What makes a guidline an "official" guidline? What's the process? &mdash;[[User:Ashley Y|Ashley Y]] 00:53, 2 March 2006 (UTC)<br />
<br />
:I suspect that a large number of the users / contributors to this site have an academic background. My experience with academia is that some kind of consensus based decision is the most successful at getting the decision adopted. In a community like this wiki, I’m not sure how to get that. We could send out notices on the various mailing lists, we could post notes on the front page and probably other ways as well. Like any consensus decision making it would take a long time. On the other hand, those that support the site could just be autocratic, set the rule and if there is too much flak, change it. <br />
:As to what actually makes an "official" guidline, I think it is up to the '''sysops''' whether we even have such a thing. The word tends to mean that '''sysops''' are required to monitor and fix the site when these are not followed. So, we might not even want to go to official, stay at unofficial and just let the community grow the guidlines (and do the monitoring) as they see fit. Sometimes, a certain level of anarchy works fine.[[User:BrettGiles|BrettGiles]] 16:06, 2 March 2006 (UTC)<br />
::I think that the traditional consensus decision making process is too slow. In addition, not everyone is interested in every guideline question, so it is not sensible to bother everyone with each of these questions. Therefore, it seems to me that it is better to follow your “autocracy approach”. And I also doubt that it is sensible to let some sysops decide about what guidelines are official. At least, this would be a bit contrary to the wiki approach, in my opinion. So I propose that users are allowed to set up guidelines they think of as sensible—possibly after some short discussion—and to declare them as official. If others don't like the guidelines, the guidelines can still be discussed (again) and changed. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:22, 8 March 2006 (UTC)<br />
<br />
OK. Everything on the project page should have a consensus. Everything else should be discussed here. I have adjusted the project page accordingly. &mdash;[[User:Ashley Y|Ashley Y]] 06:34, 3 March 2006 (UTC)<br />
:What is the “project page”? -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:22, 8 March 2006 (UTC)<br />
::Ah, I understand. It's [[HaskellWiki:Guidelines]]. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:40, 8 March 2006 (UTC)<br />
<br />
== Headlines ==<br />
<br />
Each page has a title which is automatically shown near the top of the page (currently centered and in red). So it doesn't make sense to put a single level 1 headline at the top of the page. (At the moment, you will see this on a few pages of this wiki. I suppose this is a legacy which comes from copying content from the old website to this wiki.)<br />
<br />
For top-level structuring, level 1 headlines (the ones denoted by single equals signs in the source) should be used. It is semantically incorrect to use level 2 headlines for this purpose. I note that level 1 headlines are currently rather large and in a font that might not be preferable. However, the solution to this is not to refrain from using level 1 headlines but to change the stylesheet appropriately. ''&mdash;says [[User:Wolfgang Jeltsch]]''<br />
<br />
:Note that is is currently being discussed. Level 1 (single equals) create an <tt>&lt;h1&gt;</tt> heading, which is the same level as the page title. We may (or may not:) switch to the standard of starting at level 2 (double equals) ''&mdash;says [[User:BrettGiles]]''<br />
<br />
:I prefer starting with level 2 as per Wikipedia. I consider the page title to be implicitly level 1 (whether it gets rendered as h1 or not). &mdash;[[User:Ashley Y|Ashley Y]] 06:34, 3 March 2006 (UTC)<br />
<br />
<br />
== Wiki Bug ==<br />
Try giving a Haskell example with the greater than or the less than sign in it. It clashes with the HTML rendering! -- says [[User:Uchchwhash|Pirated Dreams]] 04:22, 17 March 2006 (UTC)<br />
<br />
==What's Wrong With This Guidelines==<br />
I don't know how these guidelines became the official policy of this<br />
wiki, and I cannot find out how some of us decided to become the<br />
housekeepers, or the [http://c2.com/cgi/wiki?WikiGnome WikiGnome]s, of<br />
it.<br />
<br />
Still I've been involved in wiki development and I have some ideas on<br />
how a wiki should work. I tried to discuss it in the<br />
[http://www.haskell.org/pipermail/haskell-cafe/2007-July/029483.html haskell-café mailing list].<br />
<br />
Probably I should do that here too.<br />
<br />
[http://c2.com/cgi/wiki?HowToReactToGoodStyleTransgressions How to react to good style transgression?]<br />
Are these guidelines to be enforced? Aren't enforced guidelines a<br />
contradiction in terms? Sure they are...<br />
<br />
I don't think that cosmetic edits done just to enforce these<br />
guidelines are going to help this wiki on the long run. They piss<br />
newcomers off. And you are pissing off right the newcomers you would<br />
like to keep, the ones who are willing to contribute.<br />
<br />
You could start by reading the new material, and doing some edit to<br />
the content: if you collaborate the wiki way, probably your<br />
suggestions on style issues would be followed.<br />
<br />
Anyway, these are guidelines, or they pretend they are, and so you<br />
must always think about the fact the new paths can be taken.<br />
<br />
Instead what you have been doing, in the last year, is setting some<br />
rules here and reshaping the wiki in the name of those rules.<br />
<br />
===And what is right with these guidelines===<br />
So, when the "new" wiki started, there was quite a bit of discussion here and on the haskell list about what was an appropriate style for the wiki pages, considering that the site is meant to be a resource for new and exsisting Haskell users and as such, were desired to be reasonably consistent in style. These guidelines were the result. A variety of people edit pages to bring them into the stylistic guidelines, but if anyone is not happy with the current set, this seems like a good place to discuss changing them. <br />
<br />
Andrea's link to the "WikiGnome" has some very good pointers about stylistic changes being applied to the edits of ''newcomers'', and an even better one about dropping the argument. So this is my last edit about the subject :) --[[User:BrettGiles|BrettGiles]] 21:58, 25 July 2007 (UTC)<br />
:: Style consistency is not a primary goal of a wiki. A primary goal for the wiki is always having new people contribute to its grow. If you think that stylistic guidelines enforcing must be achieved regardless of the consequences than you should drop wiki technologies and use a blogging system or other kind of content management system. Instead, if you want to use a wiki, you should try to stick to the wiki philosophy as close as you can. --AndreaRossato Thu 26 2007 07:27 CEST <br />
<br />
And that leads me to the second real big issue: page renaming.<br />
<br />
==Why Page Renaming Is BAD==<br />
<br />
Wiki page titles are [http://en.wikipedia.org/wiki/URI URI], and URI<br />
should be persistent over time.<br />
<br />
When you rename a page you are breaking URI persistence. A redirect is<br />
only a work around. A bad work around.<br />
<br />
You did a lot of page renaming lately. By doing so you broke a lot of<br />
URIs.<br />
<br />
You are also breaking the structure of this wiki, by creating a false<br />
hierarchy of pages. In a wiki all pages are top level. Hierarchy and<br />
wikis belong to different worlds.<br />
<br />
I do believe that this guidelines are used in a vary bad way. I would<br />
like you to stop. Or at least to seriously reconsider your way of<br />
enforcing them.<br />
<br />
AndreaRossato -- Wed Jul 25 2007 19:57 CEST</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=HaskellWiki_talk:Guidelines&diff=14685HaskellWiki talk:Guidelines2007-07-25T17:57:39Z<p>AndreaRossato: does a wiki really need rules?</p>
<hr />
<div>==Headlines==<br />
<br />
[[User:BrettGiles|Brett]], this is not to offend you. It just expresses my conviction. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]]<br />
<br />
:Not to worry [[User:Wolfgang Jeltsch|Wolfgang]] - I never take offence :) - My thoughts on the usage of level 2 only were primarily due to the size balance, but also I note this is a guideline for the original mediawiki site. Does anyone know why that is? [[User:BrettGiles|BrettGiles]] 22:11, 25 February 2006 (UTC)<br />
<br />
::No idea, but it seems loose to me. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 22:40, 25 February 2006 (UTC)<br />
<br />
:::I did some research on [http://meta.wikimedia.org/wiki/Help_talk:Section metawiki] and this is the reason they start at level 2. ''"So that when a user without CSS, or a text-mode browser, or a screen reader visits, they'll be presented with a page that at least has a logical document flow."'' From what I can tell, by default, the page title is rendered as a '''h1''' element. So are single '''=''' headings. Double '''==''' are '''h2''' and so on. So from that point of view it does make sense to start at two equals. I think the real issue is that the software is slightly broken in not generating an '''h2''' for a single equals sign. So, what do you think? [[User:BrettGiles|BrettGiles]] 22:36, 26 February 2006 (UTC)<br />
<br />
::::I prefer starting with ==, like Wikipedia. &mdash;[[User:Ashley Y|Ashley Y]] 00:52, 2 March 2006 (UTC)<br />
<br />
::::MediaWiki is broken in not creating an '''h2''' element for a headline with single equal signs. So we should work around this brokeness by making our top-level headlines <tt>==</tt> headlines. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:22, 8 March 2006 (UTC)<br />
<br />
:::::I've added a respective section to [[HaskellWiki:Guidelines]]. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:40, 8 March 2006 (UTC)<br />
<br />
==Page nameing and renaming==<br />
Apparently the last rename I did so offended the author that they quit the wiki. I used to follow the guideline I just added on the main page, but had never got a response before. However, considering the size of the wiki and the number of new users (especially those that seem to not know there are guidelines :), perhaps it is time to re-institute the practice. --[[User:BrettGiles|BrettGiles]] 18:04, 24 July 2007 (UTC)<br />
<br />
==Official guidelines?==<br />
<br />
What makes a guidline an "official" guidline? What's the process? &mdash;[[User:Ashley Y|Ashley Y]] 00:53, 2 March 2006 (UTC)<br />
<br />
:I suspect that a large number of the users / contributors to this site have an academic background. My experience with academia is that some kind of consensus based decision is the most successful at getting the decision adopted. In a community like this wiki, I’m not sure how to get that. We could send out notices on the various mailing lists, we could post notes on the front page and probably other ways as well. Like any consensus decision making it would take a long time. On the other hand, those that support the site could just be autocratic, set the rule and if there is too much flak, change it. <br />
:As to what actually makes an "official" guidline, I think it is up to the '''sysops''' whether we even have such a thing. The word tends to mean that '''sysops''' are required to monitor and fix the site when these are not followed. So, we might not even want to go to official, stay at unofficial and just let the community grow the guidlines (and do the monitoring) as they see fit. Sometimes, a certain level of anarchy works fine.[[User:BrettGiles|BrettGiles]] 16:06, 2 March 2006 (UTC)<br />
::I think that the traditional consensus decision making process is too slow. In addition, not everyone is interested in every guideline question, so it is not sensible to bother everyone with each of these questions. Therefore, it seems to me that it is better to follow your “autocracy approach”. And I also doubt that it is sensible to let some sysops decide about what guidelines are official. At least, this would be a bit contrary to the wiki approach, in my opinion. So I propose that users are allowed to set up guidelines they think of as sensible—possibly after some short discussion—and to declare them as official. If others don't like the guidelines, the guidelines can still be discussed (again) and changed. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:22, 8 March 2006 (UTC)<br />
<br />
OK. Everything on the project page should have a consensus. Everything else should be discussed here. I have adjusted the project page accordingly. &mdash;[[User:Ashley Y|Ashley Y]] 06:34, 3 March 2006 (UTC)<br />
:What is the “project page”? -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:22, 8 March 2006 (UTC)<br />
::Ah, I understand. It's [[HaskellWiki:Guidelines]]. -- [[User:Wolfgang Jeltsch|Wolfgang Jeltsch]] 21:40, 8 March 2006 (UTC)<br />
<br />
== Headlines ==<br />
<br />
Each page has a title which is automatically shown near the top of the page (currently centered and in red). So it doesn't make sense to put a single level 1 headline at the top of the page. (At the moment, you will see this on a few pages of this wiki. I suppose this is a legacy which comes from copying content from the old website to this wiki.)<br />
<br />
For top-level structuring, level 1 headlines (the ones denoted by single equals signs in the source) should be used. It is semantically incorrect to use level 2 headlines for this purpose. I note that level 1 headlines are currently rather large and in a font that might not be preferable. However, the solution to this is not to refrain from using level 1 headlines but to change the stylesheet appropriately. ''&mdash;says [[User:Wolfgang Jeltsch]]''<br />
<br />
:Note that is is currently being discussed. Level 1 (single equals) create an <tt>&lt;h1&gt;</tt> heading, which is the same level as the page title. We may (or may not:) switch to the standard of starting at level 2 (double equals) ''&mdash;says [[User:BrettGiles]]''<br />
<br />
:I prefer starting with level 2 as per Wikipedia. I consider the page title to be implicitly level 1 (whether it gets rendered as h1 or not). &mdash;[[User:Ashley Y|Ashley Y]] 06:34, 3 March 2006 (UTC)<br />
<br />
<br />
== Wiki Bug ==<br />
Try giving a Haskell example with the greater than or the less than sign in it. It clashes with the HTML rendering! -- says [[User:Uchchwhash|Pirated Dreams]] 04:22, 17 March 2006 (UTC)<br />
<br />
==What's Wrong With This Guidelines==<br />
I don't know how these guidelines became the official policy of this<br />
wiki, and I cannot find out how some of us decided to become the<br />
housekeepers, or the [http://c2.com/cgi/wiki?WikiGnome WikiGnome]s, of<br />
it.<br />
<br />
Still I've been involved in wiki development and I have some ideas on<br />
how a wiki should work. I tried to discuss it in the<br />
[http://www.haskell.org/pipermail/haskell-cafe/2007-July/029483.html haskell-café mailing list].<br />
<br />
Probably I should do that here too.<br />
<br />
[http://c2.com/cgi/wiki?HowToReactToGoodStyleTransgressions How to react to good style transgression?]<br />
Are these guidelines to be enforced? Aren't enforced guidelines a<br />
contradiction in terms? Sure they are...<br />
<br />
I don't think that cosmetic edits done just to enforce these<br />
guidelines are going to help this wiki on the long run. They piss<br />
newcomers off. And you are pissing off right the newcomers you would<br />
like to keep, the ones who are willing to contribute.<br />
<br />
You could start by reading the new material, and doing some edit to<br />
the content: if you collaborate the wiki way, probably your<br />
suggestions on style issues would be followed.<br />
<br />
Anyway, these are guidelines, or they pretend they are, and so you<br />
must always think about the fact the new paths can be taken.<br />
<br />
Instead what you have been doing, in the last year, is setting some<br />
rules here and reshaping the wiki in the name of those rules.<br />
<br />
And that leads me to the second real big issue: page renaming.<br />
<br />
==Why Page Renaming Is BAD==<br />
<br />
Wiki page titles are [http://en.wikipedia.org/wiki/URI URI], and URI<br />
should be persistent over time.<br />
<br />
When you rename a page you are breaking URI persistence. A redirect is<br />
only a work around. A bad work around.<br />
<br />
You did a lot of page renaming lately. By doing so you broke a lot of<br />
URIs.<br />
<br />
You are also breaking the structure of this wiki, by creating a false<br />
hierarchy of pages. In a wiki all pages are top level. Hierarchy and<br />
wikis belong to different worlds.<br />
<br />
I do believe that this guidelines are used in a vary bad way. I would<br />
like you to stop. Or at least to seriously reconsider your way of<br />
enforcing them.<br />
<br />
AndreaRossato -- Wed Jul 25 2007 19:57 CEST</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=User:AndreaRossato&diff=14608User:AndreaRossato2007-07-23T07:46:58Z<p>AndreaRossato: clean up</p>
<hr />
<div></div>AndreaRossatohttps://wiki.haskell.org/index.php?title=X_window_programming_in_Haskell&diff=14607X window programming in Haskell2007-07-23T07:45:45Z<p>AndreaRossato: last sections, removed my signature and any reference to xmobar. I'm not going to edit this stuff anymore.</p>
<hr />
<div>==Writing an X application with Haskell Xlib bindings==<br />
<br />
This tutorial will show you how to write a simple X application using<br />
the low level Xlib library. The goal is to write a simple text base<br />
clock, that will display the system time, to be run on top of every<br />
other applications, like a status bar.<br />
<br />
While the application is fairly simple, still it will require us to<br />
get to know quite a lot of the details that must be taken into account<br />
when writing a properly working X application.<br />
<br />
Obviously some understanding of X and Xlib is required.<br />
<br />
These are some links that can be used as reference:<br />
<br />
* [http://www.tronche.com/gui/x/xlib/ The Xlib Manual]: this is the reference manual, and you should look up here every function that we are going to use in this tutorial.<br />
* [http://en.wikipedia.org/wiki/Xlib Xlib (Wikipedia)]<br />
* [http://en.wikipedia.org/wiki/X_Window_System_core_protocol X Window System core protocol (Wikipedia)]<br />
<br />
This tutorial is dedicated to the intermediate Haskell coder. While I<br />
will try to write the simplest code I can (probably it will even look<br />
the dumbest, but that's me), I'm not going into much details about the<br />
Haskell part.<br />
<br />
What are we going to learn:<br />
* how to create a window and set, or change, its attributes;<br />
* how to draw in that window, specifically some text, with some properties, like fonts or colors;<br />
* how to properly update the window;<br />
* how to handle events, like a mouse button press.<br />
<br />
In order to compile the following code examples you need at least:<br />
<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11 X11], the Haskell binding to the X11 graphics library.<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras]: will be required in some examples. This library provides missing bindings to the X11 graphics library and is actively developed by Spencer Janssen at the time of this writing. Some functions needed in this tutorial can be found only in the darcs repository of X11-extras: [http://darcs.haskell.org/~sjanssen/X11-extras http://darcs.haskell.org/~sjanssen/X11-extras]. Read carefully the README before installing.<br />
<br />
<br />
<br />
==Hello world==<br />
<br />
Let's start from the usual simple "Hello World"<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
border = blackPixel dpy dflt<br />
background = whitePixel dpy dflt<br />
rootw <- rootWindow dpy dflt<br />
win <- createSimpleWindow dpy rootw 0 0 100 100 1 border background<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
</haskell><br />
<br />
The first function, <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AopenDisplay openDisplay],<br />
is the interface to the Xlib function <br />
[http://www.tronche.com/gui/x/xlib/display/opening.html XOpenDisplay()], <br />
and opens a connection to the X sever that controls a display. The connection is returned and bound to dpy. By applying <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AdefaultScreen defaultScreen], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#DefaultScreen XDefaultScreen], <br />
we get the default screen, that is required in many of the following functions.<br />
With<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3ArootWindow rootWindow], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#RootWindow XRootWindow()], <br />
we get the root window. We need it in order to set the parent window in the most important function of the above code: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AcreateSimpleWindow createSimpleWindow], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html XCreateSimpleWindow].<br />
<br />
This function takes, as arguments: the display, the parent window of<br />
the window to be created, the x position, the y position, the width,<br />
the height, the border width, the border pixel and the background<br />
pixel.<br />
<br />
The x and y positions are relative to the upper left corner of the<br />
parent window's inside borders.<br />
<br />
In order to retrieve the values of the black and white pixels for that<br />
specific screen, we use two specific functions:<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AblackPixel blackPixel], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#BlackPixel BlackPixel], <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AwhitePixel whitePixel], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#WhitePixel WhitePixel]<br />
<br />
The function createSimpleWindow will return the window ID and, with<br />
this ID, we can start manipulating our newly created window, as we do,<br />
in the above code, with the function<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AsetTextProperty setTextProperty], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/ICC/client-to-window-manager/XSetTextProperty.html XSetTextProperty()].<br />
<br />
This function is needed, in our code, to set the window's name, that<br />
your window manager will display on some decoration attached to the<br />
window (other window managers will not display anything, for instance<br />
a tiling WM like [[Xmonad]])<br />
<br />
To set the window's name we need to manipulate the <br />
[http://www.tronche.com/gui/x/xlib/ICC/client-to-window-manager/converting-string-lists.html XTextProperty structure]. <br />
<br />
Properties, such as the XTextProperty, have a string name and a<br />
numerical identifier called an atom. An atom is an ID that uniquely<br />
identifies a particular property. Property name strings are typically<br />
all upper case - with the first letter in low case when translated<br />
into Haskell - with words separated by underscores. In our example we<br />
had to set the WM_NAME property to "Hello World".<br />
<br />
Creating and manipulating a window is just the first step to have a<br />
new window displayed. In order for the window to become visible we<br />
must map it with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AmapWindow mapWindow], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/window/XMapWindow.html XMapWindow()]. <br />
This will make the window visible.<br />
<br />
Xlib will not send requests and calls to the Xserver immediately, but<br />
will buffer them and send the full buffer when some given conditions<br />
are met.<br />
<br />
One way to force the flushing of the output buffer is to call <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Event.html#v%3Async sync], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/event-handling/XSync.html XSync()], <br />
which takes 2 arguments: the connection (dpy) and a Boolean value that<br />
indicates whether XSync() must discard all events on the event queue.<br />
<br />
After that the Xserver will eventually display our window. <br />
<br />
The rest of the above example does nothing else but blocking the<br />
program execution for 10 seconds (to let you stare at your newly<br />
created window) and then will exit.<br />
<br />
==Window's attributes==<br />
<br />
Even though in our "Hello World" example we set the window's<br />
dimension, we have no assurance that the Window Manager will respect<br />
our decision.<br />
<br />
[[Xmonad]], for instance, will just create a window with the<br />
dimensions needed to fill its tiled screen, no matter what you set in<br />
createSimpleWindow.<br />
<br />
But we decided to write a small clock that will behave as a status<br />
bar, that is to say, we want to create a window that will not be<br />
managed by a Window Manager.<br />
<br />
In order to achieve this result we need to start dealing with window's<br />
attributes.<br />
<br />
There are two ways of dealing with window's attributes: the first is<br />
to set them at window's creation time, but in that case<br />
createSimpleWindow is not powerful enough.<br />
<br />
The second way is to change window's attributes after the window's has<br />
been created. This second approach is not implemented<br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11 X11] but<br />
has been implemented in the darcs version of <br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras].<br />
<br />
===Setting window's attribute at creation time===<br />
<br />
In order to set window's attributes at creation time, the window must be created with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AcreateWindow createWindow], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html XCreateWindow()].<br />
<br />
The type signature of this function is quite long:<br />
<br />
<haskell><br />
<br />
createWindow :: Display -> Window <br />
-> Position -> Position <br />
-> Dimension -> Dimension <br />
-> CInt <br />
-> CInt <br />
-> WindowClass <br />
-> Visual <br />
-> AttributeMask <br />
-> Ptr SetWindowAttributes <br />
-> IO Window<br />
<br />
</haskell><br />
<br />
That is to say:<br />
* the connection and the parent window<br />
* the x and y position (origins in the upper left corner of the inside border of the parent window)<br />
* width and height<br />
* border width<br />
* depth of screen<br />
* the window's class<br />
* the visual<br />
* the attribute mask<br />
* and the pointer to the [http://www.tronche.com/gui/x/xlib/window/attributes/#XSetWindowAttributes XSetWindowAttributes] foreign C structure.<br />
<br />
This last one gives you an idea of the type of operation we must do in<br />
order to create a window (createSimpleWindow is just a wrapper around<br />
this more complicated createWindow, with some default arguments): we<br />
need a function to allocate some memory for the creation of the<br />
foreign C structure, and then manipulate this foreign structure from<br />
within this function.<br />
<br />
The needed function is <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AallocaSetWindowAttributes allocaSetWindowAttributes], <br />
whose type indeed is:<br />
<br />
<haskell><br />
<br />
allocaSetWindowAttributes :: (Ptr SetWindowAttributes -> IO a) -> IO a<br />
<br />
</haskell><br />
<br />
allocaSetWindowAttributes will take a function which takes the pointer<br />
to the foreign structure as its argument. This function will perform<br />
an IO action that is the action returned by allocaSetWindowAttributes.<br />
<br />
In our case allocaSetWindowAttributes will take a function that will<br />
use createWindow to return the new window.<br />
<br />
So, we will need to use createWindow inside allocaSetWindowAttributes.<br />
We will soon see how. But first let's analyze the other arguments of<br />
createWindow.<br />
<br />
The display, the parent window, the coordinates and dimensions are the<br />
same as with createSimpleWindow. But now we must specify the depth of<br />
the screen, the window's class, the visual and the attribute mask. We<br />
also need to manipulate the XSetWindowAttribute after its creation by<br />
allocaSetWindowAttributes, before calling createWindow.<br />
<br />
The depth is the number of bits available for each pixel to represent<br />
colors while the visual is way pixel values are translated to produce<br />
colors on the monitor.<br />
<br />
We are going to use <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Screen.html#v%3AdefaultDepthOfScreen defaultDepthOfScreen], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/image-format-macros.html#DefaultDepthOfScreen XDefaultDepthOfScreen()], <br />
in order to retrieve the default screen depth.<br />
<br />
For the visual we are going to use<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Screen.html#v%3AdefaultVisualOfScreen defaultVisualOfScreen], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/image-format-macros.html#DefaultVisualOfScreen DefaultVisualOfScreen].<br />
<br />
The <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3AWindowClass WindowClass] <br />
can either be copyFromParent, inputOutput, or inputOnly. In the first case the<br />
class is copied from the class of the parent window. An inputOnly<br />
window can only be used for receiving input events. In our code we are<br />
going to use inputOutput windows, windows that can receive input events<br />
and that can also be used to display some output.<br />
<br />
The attributeMask «specifies which window attributes are defined in<br />
the attributes argument. This mask is the bitwise inclusive OR of the<br />
valid attribute mask bits. If value mask is zero, the attributes are<br />
ignored and are not referenced.»<br />
(see [http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html]).<br />
<br />
In other words, in order to set more then one attribute, you need to pass a value mask such as:<br />
<br />
<haskell><br />
<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel .|. etc ...<br />
<br />
</haskell><br />
<br />
and set each of this attributes within allocaSetWindowAttributes with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#12 specific attributes setting functions].<br />
<br />
Among these functions the one we need: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_override_redirect set_override_redirect], <br />
whose type is:<br />
<br />
<haskell><br />
<br />
set_override_redirect :: Ptr SetWindowAttributes -> Bool -> IO ()<br />
<br />
</haskell><br />
<br />
This function takes the pointer to the XSetWindowAttributes structure and the flag to be set (True or False).<br />
<br />
For the list of avaliable attributes see<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3AAttributeMask the AttributeMask type defnition].<br />
<br />
For their meaning see <br />
[http://www.tronche.com/gui/x/xlib/window/attributes/#XSetWindowAttributes the XSetWindowAttributes structure reference].<br />
<br />
Now, our goal was to create a window that the Window Manager is going<br />
to ignore, and in order to do that all we need to set the attribute<br />
[http://www.tronche.com/gui/x/xlib/window/attributes/override-redirect.html CWOverrideRedirect] <br />
to True. And now we know how to do it.<br />
<br />
Ok, it's time to introduce our function to create new windows with the CWOverrideRedirect set to True<br />
<br />
<haskell><br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr) <br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
</haskell><br />
<br />
Like simpleCreateWindow, our function is a wrapper around<br />
createWindow, but this time we are manually setting the<br />
CWOverrideRedirect flag.<br />
<br />
As you see our function, unlike createSimpleWindow, does not have,<br />
among its arguments, the background and the border pixels. This colors<br />
can be set, for windows created with createWindow, using the attribute<br />
mask, and setting CWBackPixel and CWBorderPixel with the needed<br />
functions: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_background_pixel set_background_pixel] <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_border_pixel set_border_pixel].<br />
<br />
By the way, setting the border color with this version of<br />
mkUnmanagedWindow is actually useless since the border width is set to<br />
zero. In the next example we will set it to 1.<br />
<br />
Our function needs also the screen now, since we have to retrieve the<br />
default depth and visual.<br />
<br />
We can now rewrite our initial code using the new function now.<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
set_background_pixel attributes $ whitePixel dpy (defaultScreen dpy)<br />
set_border_pixel attributes $ blackPixel dpy (defaultScreen dpy)<br />
createWindow dpy rw x y w h 1 (defaultDepthOfScreen scr)<br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
</haskell><br />
<br />
Ok, let's give it a try. Did you see? Now the window will be placed in<br />
the specified x and y position, with the given dimensions. No Window<br />
Manager decorations, and so, no name displayed.<br />
<br />
===Changing an existing window's attributes===<br />
<br />
This task requires <br />
[http://www.tronche.com/gui/x/xlib/window/XChangeWindowAttributes.html XChangeWindowAttrbutes()], <br />
implemented only in the darcs version of <br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras].<br />
<br />
In order to change a window's attributes we just need the window ID in<br />
that specific X server, after that we need to unmap the window first,<br />
and then change its attributes with changeWindowAttributes, the<br />
interface to XChangeWindowAttrbutes() implemented by<br />
[http://darcs.haskell.org/~sjanssen/X11-extras the darcs version of X11-extras].<br />
<br />
Here's the code:<br />
<br />
<haskell><br />
<br />
module Main where<br />
<br />
import Graphics.X11.Xlib<br />
import Graphics.X11.Xlib.Extras<br />
import System.Environment<br />
<br />
usage :: String -> String<br />
usage n = "Usage: " ++ n ++ " manage/unmanage windowID"<br />
<br />
main :: IO ()<br />
main = do<br />
args <- getArgs<br />
pn <- getProgName<br />
let (win,ac) = case args of<br />
[] -> error $ usage pn<br />
w -> case (w !!0) of <br />
"manage" -> (window, False)<br />
"unmanage" -> (window, True)<br />
_ -> error $ usage pn<br />
where window = case (w !! 1) of <br />
[] -> error $ usage pn<br />
w -> read w :: Window<br />
dpy <- openDisplay ""<br />
unmapWindow dpy win<br />
sync dpy False<br />
allocaSetWindowAttributes $<br />
\attributes -> do<br />
set_override_redirect attributes ac<br />
changeWindowAttributes dpy win cWOverrideRedirect attributes<br />
mapWindow dpy win<br />
sync dpy False<br />
<br />
</haskell><br />
<br />
Save it as Unmanage.hs and compile with:<br />
ghc --make Unmanage.hs -o unmanage<br />
<br />
To use it you need to retrieve the window ID with the stand alone utility<br />
xwininfo<br />
<br />
Then you run the above code with:<br />
unmanage unmanage/manage windowID<br />
<br />
to set override_redirect to True or False.<br />
<br />
Obviously the important part of the code is this:<br />
<haskell><br />
dpy <- openDisplay ""<br />
unmapWindow dpy win<br />
sync dpy False<br />
allocaSetWindowAttributes $<br />
\attributes -> do<br />
set_override_redirect attributes ac<br />
changeWindowAttributes dpy win cWOverrideRedirect attributes<br />
mapWindow dpy win<br />
sync dpy False<br />
</haskell><br />
<br />
where we:<br />
# connect to the X server<br />
# unmap the window<br />
# flush the output buffer to have the X server actually unmap the window<br />
# change the attributes with the same procedure we used to set them when creating the window<br />
# map the window again<br />
# flush the output buffer to see the change take effect.<br />
<br />
You can modify this program to change other window's attributes.<br />
<br />
==Colors and color Ddepth==<br />
<br />
So far we have set the window background color as a window attribute.<br />
This is not the most convenient way to set the window background<br />
color: if we need to change it, we must change the window's attribute,<br />
and we have seen that this task requires unmapping the window,<br />
flushing the output with changeWindowAttributes within<br />
changeWindowAttributes, remapping the window and reflushing the output<br />
buffer. Moreover we can do that only we the darcs version of<br />
X11-extras...<br />
<br />
In the following sections we are going to adopt a more efficient way<br />
of setting the window's background color: we will start drawing into<br />
the window. But first we must familiarize with colors and the way the<br />
X server deals with them.<br />
<br />
So far we have set the colors by using some functions to retrieve<br />
their pixel values: blackPixel and whitePixel. These functions take<br />
the display and the default screen and return respectively the pixel<br />
values for the black and the white colors in that screen.<br />
<br />
A color is represented by a 32-bit unsigned integer, called a pixel<br />
value. The elements affecting the pixel representation of a color are:<br />
1. the color depth; 2. the colormap, which is a table containing red,<br />
green, and blue intensity values; 3. the visual type.<br />
<br />
All these elements are specific to a given piece of hardware, and so<br />
our X application must detect them in order to set colors<br />
appropriately for that given hardware.<br />
<br />
The approach we are going to use to accomplish this task is this: we<br />
are going to use named colors, or colors represented by<br />
[http://en.wikipedia.org/wiki/RGB RGB triple], such as "red",<br />
"yellow", or "#FFFFFF", etc; and we are going to translate these<br />
colors into the pixel values appropriate for the screen we are<br />
operating on.<br />
<br />
In order to achieve our goal we are going to use the function <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Color.html#v%3AallocNamedColor allocNamedColor] <br />
which is the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/color/XAllocNamedColor.html XAllocNamedColor()].<br />
<br />
The type signature of allocNamedColor is:<br />
<br />
<haskell><br />
<br />
allocNamedColor :: Display -> Colormap -> String -> IO (Color, Color)<br />
<br />
</haskell><br />
<br />
That is to say, given a display connection, a color map and a string -<br />
our color representation -, this function will return a tuple with the<br />
closest RGB values provided by the hardware and the exact RGB values,<br />
both encoded in a <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib.html#v%3AColor Haskell Color data type]. <br />
We will use the first approximated value.<br />
<br />
The Color data type has a field name we will use to retrieve the<br />
needed pixel value: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib.html#v%3Acolor_pixel color_pixel].<br />
<br />
We can now write this helper function:<br />
<br />
<haskell><br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(approx,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel approx <br />
<br />
</haskell><br />
<br />
To retrieve the colormap of the screen we used <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AdefaultColormap defaultColormap], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#DefaultColormap XDefaultColormap()], <br />
which requires the display and the screen, and returns the colormap of that screen.<br />
<br />
We can now rewrite our example using this new approach.<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel<br />
background_color <- initColor dpy "red"<br />
border_color <- initColor dpy "black" <br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
set_background_pixel attributes background_color<br />
set_border_pixel attributes border_color<br />
createWindow dpy rw x y w h 1 (defaultDepthOfScreen scr)<br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(apros,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel apros<br />
<br />
</haskell><br />
<br />
Just give it a try. Now you can also experiment with different colors.<br />
This approach will assure that our application will work no matter the<br />
color depth of the screen we are working on.<br />
<br />
==Drawing in windows==<br />
<br />
The X server provides two objects that can be used to draw something<br />
to: windows and pixmaps.<br />
<br />
In this section we will start drawing into windows.<br />
<br />
We have seen that changing the background color of a window is a<br />
troublesome operation, since the window must be unamapped and<br />
remapped, memory for a foreign structure allocated, and so on.<br />
<br />
Instead, we can use some graphic operations to draw a rectangle over<br />
the window. We will latter manipulate the foreground, visible, color of<br />
this rectangle, that will become the new background of our window.<br />
<br />
We can also use multiple rectangles with different dimension to<br />
decorate our window with a border, for instance.<br />
<br />
Later on we will print some text over these rectangles.<br />
<br />
===Drawing rectangles in a window===<br />
<br />
Citing from<br />
[http://en.wikipedia.org/wiki/X_Window_System_core_protocol#Graphic_contexts_and_fonts Wikipedia]:<br />
<br />
<blockquote><br />
The client can request a number of graphic operations, such clearing<br />
an area, copying an area into another, drawing points, lines,<br />
rectangles, and text. Beside clearing, all operations are possible on<br />
all drawables, both windows and pixmaps.<br />
<br />
Most requests for graphic operations include a graphic context, which<br />
is a structure that contains the parameters of the graphic operations.<br />
A graphic context includes the foreground color, the background color,<br />
the font of text, and other graphic parameters. When requesting a<br />
graphic operation, the client includes a graphic context.<br />
</blockquote> <br />
<br />
In other words, as for setting window's attribute, we must use a<br />
foreign C structure to set parameters for graphic operations, and then<br />
we will feed this structure to the <br />
[http://www.tronche.com/gui/x/xlib/graphics/ functions] <br />
that will perform these graphic operations.<br />
<br />
We one difference: instead of operating within a function that<br />
allocates memory and creates a pointer to the foreign structure, now<br />
we have to explicitally create the Graphic Context, and free it after<br />
having used it, with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AcreateGC createGC], <br />
the interface to <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AcreateGC XCreateGC], <br />
and<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AfreeGC freeGC], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/GC/XFreeGC.html XFreeGC].<br />
<br />
Be careful: if you create a graphic context without freeing it after<br />
use, you are going to end up with a noticeable memory leak!<br />
<br />
The specific graphic functions we are going to need for drawing a<br />
rectangle into our window are:<br />
<br />
# [http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AsetForeground setForegrond] the interface to [http://www.tronche.com/gui/x/xlib/GC/convenience-functions/XSetForeground.html XSetForegroung]<br />
# [http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AsetBackground setBackground] the interface to [http://www.tronche.com/gui/x/xlib/GC/convenience-functions/XSetBackground.html XSetBackground]<br />
# [http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AfillRectangle fillRectangle] the interface to [http://www.tronche.com/gui/x/xlib/graphics/filling-areas/XFillRectangle.html XFillRectangle]<br />
<br />
The first two functions are needed to set the parameters in the<br />
[http://www.tronche.com/gui/x/xlib/GC/manipulating.html Graphic Context]. <br />
The third one will use this GC for filling a rectangle on the<br />
specified window. Just have a look to their type signatures:<br />
<br />
<haskell><br />
<br />
setForeground :: Display -> GC -> Pixel -> IO ()<br />
setBackground :: Display -> GC -> Pixel -> IO ()<br />
fillRectangle :: Display -> Drawable -> GC -> Position -> Position -> Dimension -> Dimension -> IO ()<br />
<br />
</haskell><br />
<br />
Ok, this is the function that we will be using for drawing into a<br />
window:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> IO ()<br />
drawInWin dpy win = do<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 0 0 100 100<br />
freeGC dpy gc<br />
<br />
</haskell><br />
<br />
This will just fill our window with a rectangle at (0, 0) (x, y)<br />
coordinates (relatives to the window's internal border), with the same<br />
dimensions of our window.<br />
<br />
Obviously we can play a bit with rectangles. This version, for<br />
instance, will draw 2 rectangles to simulate a blu rectangle with a<br />
green border, two pixels width:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> IO ()<br />
drawInWin dpy win = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 100 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 96 96<br />
freeGC dpy gc<br />
<br />
</haskell><br />
<br />
You can use this function on a mapped window. This is our original<br />
example rewritten with this new approach:<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
drawInWin dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
drawInWin :: Display -> Window -> IO ()<br />
drawInWin dpy win = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 100 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 96 96<br />
freeGC dpy gc<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr)<br />
inputOutput visual cWOverrideRedirect attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
<br />
</haskell><br />
<br />
As you see, now mkUnmanagedWindow sets a null border width and does<br />
not set any background color. Everything is easily done with<br />
rectangles.<br />
<br />
===Printing a string===<br />
<br />
Printing a string to a window requires operating with another foreign<br />
C structure, the <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/ XFontStruct], <br />
which contains all of the information regarding the metrics of font<br />
that the X server will use to display our string.<br />
<br />
This structure will be used to perform some computations that are<br />
required for the correct placement of the text in the window.<br />
<br />
As we have seen with the window's attributes and the Graphic Context,<br />
we need a function that returns a pointer to this foreign structure,<br />
pointer that must be freed after using it.<br />
<br />
A pointer to the XFontStruct is returned by <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AloadQueryFont loadQueryFont], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XLoadQueryFont.html XLoadQueryFont()].<br />
<br />
XLoadQueryFont, which requires the X connection and the font name,<br />
will perform two distinct operations: load the needed font and return<br />
its id<br />
(performed by [http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XLoadFont.html XLoadFont()] <br />
which doesn't have a Haskell interface) and query the font to retrieve the XFontStruct <br />
(performed by [http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XQueryFont.html XQueryFont] <br />
which does have a Haskell interface:<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AqueryFont queryFont]).<br />
<br />
The XFontStruct is needed by <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AtextExtents textExtents], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XTextExtents.html XTexteExtent()], <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AtextWidth textWidth], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XTextWidth.html XTextWidth()].<br />
<br />
These are their type signatures.<br />
<br />
<haskell><br />
<br />
textExtents :: FontStruct -> String -> (FontDirection, Int32, Int32, CharStruct)<br />
textWidth :: FontStruct -> String -> Int32<br />
<br />
</haskell><br />
<br />
Given the FontStruct and the string to be printed, these functions<br />
will provide some valuable information. The values returned by the<br />
first one are related to font direction and vertical placement, while<br />
the second one will return the total width of the string to be printed<br />
with that specific font.<br />
<br />
This information can be used with the graphic function that will<br />
actually draw the text on the window: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AdrawImageString drawImageString], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/drawing-text/XDrawImageString.html XDrawImageString].<br />
<br />
There are other version of this string, <br />
[http://www.tronche.com/gui/x/xlib/graphics/drawing-text/XDrawText.html XDrawText()] <br />
and <br />
[http://www.tronche.com/gui/x/xlib/graphics/drawing-text/XDrawText16.html XDrawText16()] <br />
for 16-bit characters.<br />
<br />
We are going to use the first one because it will also use the<br />
foreground pixel set in the Graphic Context.<br />
<br />
It's type signature is:<br />
<br />
<haskell><br />
<br />
drawImageString :: Display -> Drawable -> GC -> Position -> Position -> String -> IO ()<br />
<br />
</haskell><br />
<br />
Finally we must remember to free the FontStruct with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AfreeFont freeFont] <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XFreeFont.html XFreeFont].<br />
<br />
We can now write our function to print a string on a window, but first<br />
we need to make some small modifications to our darwInWin function,<br />
that will now take a string and will load and free the needed<br />
FontStruct to be passed to the new printString function:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> String -> IO ()<br />
drawInWin dpy win str = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
fontStruc <- loadQueryFont dpy "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 200 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 196 96<br />
printString dpy win gc fontStruc str<br />
freeGC dpy gc<br />
freeFont dpy fontStruc<br />
<br />
</haskell><br />
<br />
Here we are loading the "misc-fixed" font. You can select different<br />
fonts with the standalone utility:<br />
xfontsel<br />
<br />
As you see the FontStruct retrieved by loadQueryFont is used by<br />
printString and then freed.<br />
<br />
So, let's look printString:<br />
<br />
<br />
<haskell><br />
<br />
printString :: Display <br />
-> Drawable <br />
-> GC<br />
-> FontStruct<br />
-> String<br />
-> IO ()<br />
printString dpy d gc fontst str =<br />
do let strLen = textWidth fontst str<br />
(_,asc,_,_) = textExtents fontst str<br />
valign = (100 + fromIntegral asc) `div` 2<br />
remWidth = 200 - strLen<br />
offset = remWidth `div` 2<br />
fgcolor <- initColor dpy "white"<br />
bgcolor <- initColor dpy "blue"<br />
setForeground dpy gc fgcolor<br />
setBackground dpy gc bgcolor<br />
drawImageString dpy d gc offset valign str<br />
<br />
</haskell><br />
<br />
In the "let" part we use textWidth and textExtents to set vertical and<br />
horizontal alignment: this is done by calculating the x and y<br />
coordinates for drawImageString. In this example text will be<br />
vertically and horizontally centered (take into account that I have<br />
also enlarged the window, whose width now is 200 pixels).<br />
<br />
For a reference of the meaning of font ascent and descent, and the<br />
origins of the rectangle drawn by drawImageString read the par. 8.6<br />
([http://www.tronche.com/gui/x/xlib/graphics/drawing-text/ Drawing Text]) <br />
of the <br />
[http://www.tronche.com/gui/x/xlib/ The Xlib Manual].<br />
<br />
You may notice that printString takes a <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3ADrawable Drawable], <br />
which can be either a window or a pixmap (see below).<br />
<br />
We can now rewrite our example and finally see some text printed in<br />
our window:<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import System.Time<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 200 100<br />
setTextProperty dpy win "The Clock" wM_NAME <br />
mapWindow dpy win<br />
drawInWin dpy win =<< date<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
date :: IO String<br />
date = do <br />
t <- toCalendarTime =<< getClockTime<br />
return $ calendarTimeToString t<br />
<br />
drawInWin :: Display -> Window -> String -> IO ()<br />
drawInWin dpy win str = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
fontStruc <- loadQueryFont dpy "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 200 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 196 96<br />
printString dpy win gc fontStruc str<br />
freeGC dpy gc<br />
freeFont dpy fontStruc<br />
<br />
printString :: Display <br />
-> Drawable <br />
-> GC<br />
-> FontStruct<br />
-> String<br />
-> IO ()<br />
printString dpy d gc fontst str =<br />
do let strLen = textWidth fontst str<br />
(_,asc,_,_) = textExtents fontst str<br />
valign = (100 + fromIntegral asc) `div` 2<br />
remWidth = 200 - strLen<br />
offset = remWidth `div` 2<br />
fgcolor <- initColor dpy "white"<br />
bgcolor <- initColor dpy "blue"<br />
setForeground dpy gc fgcolor<br />
setBackground dpy gc bgcolor<br />
drawImageString dpy d gc offset valign str<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr)<br />
inputOutput visual cWOverrideRedirect attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(apros,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel apros<br />
<br />
</haskell><br />
<br />
Since we are going to display the system time, I already added a<br />
"date" function and increased to width of our window. <br />
<br />
Just give it a try. We are almost there. It's a clock, after all.<br />
<br />
==Updating a window==<br />
<br />
If you do not believe that now we have a system clock, just change the<br />
main function of the above example with the following two functions and<br />
try yourself:<br />
<br />
<haskell><br />
<br />
main :: IO ()<br />
main = do <br />
dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 200 100<br />
setTextProperty dpy win "The Clock" wM_NAME <br />
mapWindow dpy win<br />
updateWin dpy win<br />
<br />
updateWin :: Display -> Window -> IO ()<br />
updateWin dpy win = do<br />
drawInWin dpy win =<< date<br />
sync dpy False<br />
threadDelay (1 * 1000000)<br />
updateWin dpy win<br />
<br />
</haskell><br />
<br />
This piece of code just adds the eternal recursive loop and, within<br />
this loop, reduces the thread block to 1 second. That's it.<br />
<br />
Every second our window will be updated - redrawn.<br />
<br />
Now, if you let the clock run for a while, you will notice that<br />
sometimes, during an update, the window sort of flickers.<br />
<br />
This is due to the fact that we are drawing directly over the window.<br />
We need to adopt a better technique: we need to write to a pixmap<br />
first, and then copy the pixmap over the window.<br />
<br />
Citing from<br />
[http://en.wikipedia.org/wiki/X_Window_System_core_protocol#Pixmaps_and_drawables Wikipedia]:<br />
<br />
<blockquote> <br />
«A pixmap is a region of memory that can be used for<br />
drawing. Contrary to windows, the contents of pixmaps are not<br />
automatically shown on the screen. However, the content of a pixmap<br />
(or a part of it) can be transferred to a window and vice versa. This<br />
allows for techniques such as <br />
[http://en.wikipedia.org/wiki/Double_buffering double buffering]. <br />
Most of the graphical<br />
operations that can be done on windows can also be done on pixmaps.<br />
Windows and pixmaps are collectively named drawables, and their<br />
content data resides on the server.» <br />
</blockquote><br />
<br />
This is not very difficult, and requires a very small change of our<br />
drawInWin function.<br />
<br />
In order to create the pixmap we will use <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AcreatePixmap createPixmap], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/pixmap-and-cursor/XCreatePixmap.htm XCreatePixmap()], <br />
which takes the display connection, the drawable upon which the pixmap<br />
is created, the width, the height, and the depth of the screen.<br />
<br />
We will then use <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AcopyArea copyArea], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/XCopyArea.html XCopyArea], <br />
to copy the pixmap over the window.<br />
<br />
This is the copyArea type signature:<br />
<br />
<haskell><br />
<br />
copyArea :: Display <br />
-> Drawable -> Drawable <br />
-> GC <br />
-> Position -> Position <br />
-> Dimension -> Dimension <br />
-> Position -> Position <br />
-> IO ()<br />
<br />
</haskell><br />
<br />
that is to say:<br />
# the display connection<br />
# the origin and the destination drawables (our pixmap and our window respectively)<br />
# the x and y coordinates relative to the origin drawable upper left corner<br />
# the width and the height of the area the be copied<br />
# the x and y coordinates relative to the upper left corner of the destination drawable where the copied area must be placed<br />
<br />
And we will eventually free the pixmap with<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AfreePixmap freePixmap], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/pixmap-and-cursor/XFreePixmap.html XFreePixmap].<br />
<br />
Since our printString function may accept either a window or a pixmap,<br />
they both are drawables, all we need to do is to change drawInWin<br />
accordingly:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> String ->IO ()<br />
drawInWin dpy win str = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
fontStruc <- loadQueryFont dpy "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"<br />
p <- createPixmap dpy win 200 100 (defaultDepthOfScreen (defaultScreenOfDisplay dpy))<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy p gc 0 0 200 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy p gc 2 2 196 96<br />
printString dpy p gc fontStruc str<br />
copyArea dpy p win gc 0 0 200 100 0 0<br />
freeGC dpy gc<br />
freeFont dpy fontStruc<br />
freePixmap dpy p<br />
<br />
</haskell><br />
<br />
Now, all graphic functions take now "p" and not "win". After copyArea<br />
everything is freed.<br />
<br />
Our clock:<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import System.Time<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main = do <br />
dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 200 100<br />
setTextProperty dpy win "Hello World - The Clock" wM_NAME <br />
mapWindow dpy win<br />
updateWin dpy win<br />
<br />
updateWin :: Display -> Window -> IO ()<br />
updateWin dpy win = do<br />
drawInWin dpy win =<< date<br />
sync dpy False<br />
threadDelay (1 * 1000000)<br />
updateWin dpy win<br />
<br />
date :: IO String<br />
date = do <br />
t <- toCalendarTime =<< getClockTime<br />
return $ calendarTimeToString t<br />
<br />
drawInWin :: Display -> Window -> String ->IO ()<br />
drawInWin dpy win str = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
fontStruc <- loadQueryFont dpy "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"<br />
p <- createPixmap dpy win 200 100 (defaultDepthOfScreen (defaultScreenOfDisplay dpy))<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy p gc 0 0 200 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy p gc 2 2 196 96<br />
printString dpy p gc fontStruc str<br />
copyArea dpy p win gc 0 0 200 100 0 0<br />
freeGC dpy gc<br />
freeFont dpy fontStruc<br />
freePixmap dpy p<br />
<br />
<br />
printString :: Display <br />
-> Drawable <br />
-> GC<br />
-> FontStruct<br />
-> String<br />
-> IO ()<br />
printString dpy d gc fontst str =<br />
do let strLen = textWidth fontst str<br />
(_,asc,_,_) = textExtents fontst str<br />
valign = (100 + fromIntegral asc) `div` 2<br />
remWidth = 200 - strLen<br />
offset = remWidth `div` 2<br />
fgcolor <- initColor dpy "white"<br />
bgcolor <- initColor dpy "blue"<br />
setForeground dpy gc fgcolor<br />
setBackground dpy gc bgcolor<br />
drawImageString dpy d gc offset valign str<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr)<br />
inputOutput visual cWOverrideRedirect attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(apros,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel apros<br />
<br />
</haskell><br />
<br />
<br />
==Dealing with events==<br />
<br />
Now try this.<br />
<br />
In updateWin set threadDelay to something like:<br />
<br />
<haskell><br />
<br />
threadDelay (60 * 1000000)<br />
<br />
</haskell><br />
<br />
Run the clock, switch to a console (with Alt+Ctrl+F1) and come back to<br />
the X server where the clock is running.<br />
<br />
What happened? The window disappeared, and came back after being<br />
redrawn by drawInWin.<br />
<br />
The problem is that our application does not respond to the events the<br />
X server is sending to our window. If a window is covered or anyway no<br />
more visible on the screen, when the covered area becomes visible<br />
again the X server will send to that window an ''Expose'' event, so<br />
that the application using that window may redraw it. Since our clock<br />
doesn't listen for any event, the window will not be redrawn till a<br />
new call to drawInWin is done.<br />
<br />
Citing from <br />
[http://en.wikipedia.org/wiki/X_Window_System_core_protocol#Events Wikipedia]:<br />
<br />
<blockquote> <br />
Events are packets sent by the server to a client to<br />
communicate that something the client may be interested in has<br />
happened. For example, an event is sent when the user presses a key or<br />
clicks a mouse button. <br />
<br />
Events are not only used for input: for example, events are sent to<br />
indicate the creation of new subwindows of a given window. Every event<br />
is relative to a window. For example, if the user clicks when the<br />
pointer is in a window, the event will be relative to that window. The<br />
event packet contains the identifier of that window.<br />
</blockquote><br />
<br />
The list of events a window will be reacting too is set as the <br />
[http://www.tronche.com/gui/x/xlib/window/attributes/event-and-do-not-propagate.html event mask] <br />
attribute of that window, and so may be set at creation time, as<br />
we have seen for the background pixel, or with<br />
XChangeWindowAttributes, or by using<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Event.html#v%3AselectInput selectInput], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/event-handling/XSelectInput.html XSelectInput].<br />
<br />
In any case an event mask, <br />
[http://www.tronche.com/gui/x/xlib/window/attributes/event-and-do-not-propagate.html define as] <br />
«the bitwise inclusive OR of zero or more of the valid event mask<br />
bits», must be specified in a way very similar to the attribute mask<br />
specification.<br />
<br />
This is the type signature of selectInput:<br />
<br />
<haskell><br />
<br />
selectInput :: Display -> Window -> EventMask -> IO ()<br />
<br />
</haskell><br />
<br />
The possible events to be included in the event must, separated by a<br />
bitwise inclusive OR, are listed<br />
[http://www.tronche.com/gui/x/xlib/events/mask.html here] and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#3 here].<br />
<br />
For a list of events types refer to the <br />
[http://www.tronche.com/gui/x/xlib/events/types.html Xlib Manual].<br />
<br />
In order to capture ''Expose'' events, we will need to set the <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#v%3AexposureMask exposureMask] <br />
with something like this, right after the new window has been mapped,<br />
in our main function:<br />
<br />
<haskell><br />
<br />
selectInput dpy win exposureMask<br />
<br />
</haskell><br />
<br />
This is all we need to do in order to configure the window in such a<br />
way that it will receive ''Expose'' events.<br />
<br />
Our problem is far bigger than that, unfortunately. Our problem,<br />
indeed, is that we must update (redraw) our window either in the case<br />
of an ''Expose'' event is received '''and''' when a given amount of<br />
time is elapsed. This second requirement was met by blocking our<br />
program execution with threadDelay. But when our program is blocked it<br />
cannot receive any event.<br />
<br />
But if we start listening for events and no ''Expose'' event happens,<br />
after some time is elapsed we must update our window anyhow.<br />
<br />
How can we achieve such a result?<br />
<br />
Just to explain our porblem with other words, if we change, in the<br />
last example, main and updateWin to listen to events we end up with<br />
something like this:<br />
<br />
<haskell><br />
<br />
main :: IO ()<br />
main = do <br />
dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 200 100<br />
setTextProperty dpy win "The Clock" wM_NAME <br />
mapWindow dpy win<br />
selectInput dpy win (exposureMask .|. buttonPress)<br />
updateWin dpy win<br />
<br />
updateWin :: Display -> Window -> IO ()<br />
updateWin dpy win = do<br />
drawInWin dpy win =<< date<br />
sync dpy True<br />
allocaXEvent $ \e -> <br />
do nextEvent dpy e<br />
ev <- getEvent e<br />
putStrLn $ eventName ev<br />
updateWin dpy win<br />
<br />
</haskell><br />
<br />
<br />
In main we added the selectInput call. Note that the event mask<br />
includes both ''Expose'' events, and mouse button press events (you<br />
may specify different types of events).<br />
<br />
The second function, updateWin, required more modifications.<br />
<br />
Now the sync call takes a True, and not a False any more. This means<br />
that when flushing the output buffer all events in the event queue<br />
will be discarded. This is necessary otherwise we are going to<br />
intercept previous events. For instance, if you change the Boolean to<br />
False, you will see a<br />
''[http://www.tronche.com/gui/x/xlib/events/exposure/graphics-expose-and-no-expose.html NoExpose]'' <br />
event, that is the result of the application of XCopyArea in<br />
drawInWin.<br />
<br />
Please note the use of <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Event.html#v%3AallocaXEvent allocaXEvent], <br />
very similar to the use of allocaSetWindowAttributes, as shown by its<br />
type signature:<br />
<br />
<haskell><br />
<br />
allocaXEvent :: (XEventPtr -> IO a) -> IO a<br />
<br />
</haskell><br />
<br />
Within allocaXEvent we can use the pointer to the XEvent to:<br />
# wait for the next event with [http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Event.html#v%3AnextEvent nextEvent], the interface to [http://www.tronche.com/gui/x/xlib/event-handling/manipulating-event-queue/XNextEvent.html XNextEvent];<br />
# get the occurred event with getEvent (which requires X11-extras);<br />
# convert the event in a string with eventName (which requires X11-extras);<br />
# print the event name to the standard output: if we run our program from the command line, we can see the events received by our window.<br />
<br />
I've also removed the threadDelay call. Guess why?<br />
<br />
Just give it a run and you'll find out. If sync discards previous<br />
events, now nextEvent will block the program execution till an event<br />
occurs. If you don't press the mouse button over the window, or force<br />
an ''Expose'' event to occur, for instance by switching to a text<br />
console and back, the thread will be blocked in the safe foreign call<br />
to <br />
[http://www.tronche.com/gui/x/xlib/event-handling/manipulating-event-queue/XNextEvent.html XNextEvent], <br />
which, «if the event queue is empty, flushes the output buffer and<br />
blocks until an event is received».<br />
<br />
This is the implementation of nextEvent:<br />
<br />
<haskell><br />
<br />
-- | interface to the X11 library function @XNextEvent()@.<br />
foreign import ccall safe "HsXlib.h XNextEvent"<br />
nextEvent :: Display -> XEventPtr -> IO ()<br />
<br />
</haskell><br />
<br />
How can we unblock nextEvent after a given amount of time is elapsed?<br />
<br />
===Events and threads===<br />
<br />
One possible solution is to use a second thread to ask the X server to<br />
send an ''Expose'' event after some time.<br />
<br />
This is the code:<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import Graphics.X11.Xlib.Extras<br />
import System.Exit (exitWith, ExitCode(..))<br />
import System.Time<br />
import Control.Concurrent (threadDelay, forkIO)<br />
<br />
main :: IO ()<br />
main = do <br />
dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 200 100<br />
setTextProperty dpy win "The Clock" wM_NAME <br />
mapWindow dpy win<br />
selectInput dpy win (exposureMask .|. buttonPress)<br />
updateWin dpy win<br />
<br />
updateWin :: Display -> Window -> IO ()<br />
updateWin dpy win = do<br />
forkIO $ sendExposeEvent dpy win<br />
drawInWin dpy win =<< date<br />
sync dpy True<br />
allocaXEvent $ \e -> <br />
do nextEvent dpy e<br />
ev <- getEvent e<br />
putStrLn $ eventName ev<br />
updateWin dpy win<br />
<br />
sendExposeEvent :: Display -> Window -> IO ()<br />
sendExposeEvent dpy w = <br />
do threadDelay (1 * 1000000)<br />
allocaXEvent $ \e -> do<br />
setEventType e expose<br />
sendEvent dpy w False noEventMask e<br />
sync dpy False<br />
<br />
date :: IO String<br />
date = do <br />
t <- toCalendarTime =<< getClockTime<br />
return $ calendarTimeToString t<br />
<br />
drawInWin :: Display -> Window -> String ->IO ()<br />
drawInWin dpy win str = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
fontStruc <- loadQueryFont dpy "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"<br />
p <- createPixmap dpy win 200 100 (defaultDepthOfScreen (defaultScreenOfDisplay dpy))<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy p gc 0 0 200 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy p gc 2 2 196 96<br />
printString dpy p gc fontStruc str<br />
copyArea dpy p win gc 0 0 200 100 0 0<br />
freeGC dpy gc<br />
freeFont dpy fontStruc<br />
freePixmap dpy p<br />
<br />
<br />
printString :: Display <br />
-> Drawable <br />
-> GC<br />
-> FontStruct<br />
-> String<br />
-> IO ()<br />
printString dpy d gc fontst str =<br />
do let strLen = textWidth fontst str<br />
(_,asc,_,_) = textExtents fontst str<br />
valign = (100 + fromIntegral asc) `div` 2<br />
remWidth = 200 - strLen<br />
offset = remWidth `div` 2<br />
fgcolor <- initColor dpy "white"<br />
bgcolor <- initColor dpy "blue"<br />
setForeground dpy gc fgcolor<br />
setBackground dpy gc bgcolor<br />
drawImageString dpy d gc offset valign str<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr)<br />
inputOutput visual cWOverrideRedirect attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(apros,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel apros<br />
<br />
<br />
</haskell><br />
<br />
This is going to work only if compiled with the ghc flag ''-threaded''<br />
otherwise it will not work.<br />
<br />
A clear explanation of why can be found <br />
[http://haskell.org/ghc/docs/latest/html/libraries/base/Control-Concurrent.html#10 here].<br />
<br />
===A new nextEvent with asynchronous exceptions===<br />
<br />
This is a second solution and was proposed by Spencer Janssen.<br />
<br />
It uses a version of nextEvent that will not block in a foreign call.<br />
An <br />
[http://haskell.org/ghc/docs/latest/html/libraries/base/Control-Exception.html#14 asynchronous exception] <br />
will be used to interrupt threadWaitRead.<br />
<br />
This is the code.<br />
<br />
<haskell><br />
<br />
import Prelude hiding (catch)<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import Graphics.X11.Xlib.Extras<br />
import System.Exit (exitWith, ExitCode(..))<br />
import System.Time<br />
import Control.Concurrent<br />
import Control.Exception<br />
import System.Posix.Types (Fd(..))<br />
<br />
<br />
main :: IO ()<br />
main = do <br />
dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 200 100<br />
setTextProperty dpy win "The Clock" wM_NAME <br />
mapWindow dpy win<br />
selectInput dpy win (exposureMask .|. buttonPress)<br />
updateWin dpy win<br />
<br />
-- | A version of nextEvent that does not block in foreign calls.<br />
nextEvent' :: Display -> XEventPtr -> IO ()<br />
nextEvent' d p = do<br />
pend <- pending d<br />
if pend /= 0<br />
then nextEvent d p<br />
else do<br />
threadWaitRead (Fd fd)<br />
nextEvent' d p<br />
where<br />
fd = connectionNumber d<br />
<br />
-- | The event loop<br />
updateWin :: Display -> Window -> IO ()<br />
updateWin dpy win = do<br />
t <- forkIO (block go)<br />
timer t<br />
where<br />
-- interrupt the drawing thread every so often<br />
timer t = do<br />
threadDelay (1 * 1000000)<br />
throwTo t (ErrorCall "done")<br />
timer t<br />
-- Continuously wait for a timer interrupt or an expose event<br />
go = do<br />
drawInWin dpy win =<< date<br />
catch (unblock $ allocaXEvent $ nextEvent' dpy) (const $ return ())<br />
go<br />
<br />
sendExposeEvent :: Display -> Window -> IO ()<br />
sendExposeEvent dpy w = <br />
do threadDelay (1 * 1000000)<br />
allocaXEvent $ \e -> do<br />
setEventType e expose<br />
sendEvent dpy w False noEventMask e<br />
sync dpy False<br />
<br />
date :: IO String<br />
date = do <br />
t <- toCalendarTime =<< getClockTime<br />
return $ calendarTimeToString t<br />
<br />
drawInWin :: Display -> Window -> String ->IO ()<br />
drawInWin dpy win str = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
fontStruc <- loadQueryFont dpy "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"<br />
p <- createPixmap dpy win 200 100 (defaultDepthOfScreen (defaultScreenOfDisplay dpy))<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy p gc 0 0 200 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy p gc 2 2 196 96<br />
printString dpy p gc fontStruc str<br />
copyArea dpy p win gc 0 0 200 100 0 0<br />
freeGC dpy gc<br />
freeFont dpy fontStruc<br />
freePixmap dpy p<br />
<br />
<br />
printString :: Display <br />
-> Drawable <br />
-> GC<br />
-> FontStruct<br />
-> String<br />
-> IO ()<br />
printString dpy d gc fontst str =<br />
do let strLen = textWidth fontst str<br />
(_,asc,_,_) = textExtents fontst str<br />
valign = (100 + fromIntegral asc) `div` 2<br />
remWidth = 200 - strLen<br />
offset = remWidth `div` 2<br />
fgcolor <- initColor dpy "white"<br />
bgcolor <- initColor dpy "blue"<br />
setForeground dpy gc fgcolor<br />
setBackground dpy gc bgcolor<br />
drawImageString dpy d gc offset valign str<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr)<br />
inputOutput visual cWOverrideRedirect attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(apros,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel apros<br />
<br />
</haskell><br />
<br />
[[Category:Tutorials]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=User_talk:BrettGiles&diff=14597User talk:BrettGiles2007-07-22T21:02:45Z<p>AndreaRossato: </p>
<hr />
<div>Why did you rename the page I created, X Window Programming in Haskell, to X Window Programming?<br />
<br />
It is not about X Window programming, but about X Window Programming IN HASKELL!<br />
: Hi Andrea. If you feel strongly about it, please go ahead and rename it back to "X window programming in Haskell". I had two reasons. First, it did not follow the capitalization standards for pages here. Second, there have been various discussions that "In Haskell", "Haskell..." etc., page titles are redundant as the site is dedicated to Haskell. I'll post this on your talk page as well, in case you aren't monitoring this one. --[[User:BrettGiles|BrettGiles]] 20:02, 22 July 2007 (UTC)<br />
<br />
Hi, it's not a matter of feeling strongly about it, but the fact that pages are getting indexed by search engines outside this wiki too, and titles matter: someone looking for a page on X window programming may just be misguided by the page title of a tutorial that only discuss the way of programming the X server from the Haskell side but whose title suggests it is dedicated to discussing general X programming issues. <br />
As the guidelines say "it is important that a page title is a title".<br />
While I may understand your argument for pages discussing specific Haskell issues, for a page discussing bindings to a library in a foreign language it seems plainly wrong to me.<br />
Still I'm open to discussion (which means: we discuss and ''then'' a decision is taken). AndreaRossato 23:02 22 July 2007 (CEST)<br />
<br />
: I already renamed the page to the original title, but with sentence capitalization (I didn't see it in the guidelines, I must confess). --AndreaRossato - same time as before.</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=User_talk:BrettGiles&diff=14596User talk:BrettGiles2007-07-22T20:57:42Z<p>AndreaRossato: reply to Brett</p>
<hr />
<div>Why did you rename the page I created, X Window Programming in Haskell, to X Window Programming?<br />
<br />
It is not about X Window programming, but about X Window Programming IN HASKELL!<br />
: Hi Andrea. If you feel strongly about it, please go ahead and rename it back to "X window programming in Haskell". I had two reasons. First, it did not follow the capitalization standards for pages here. Second, there have been various discussions that "In Haskell", "Haskell..." etc., page titles are redundant as the site is dedicated to Haskell. I'll post this on your talk page as well, in case you aren't monitoring this one. --[[User:BrettGiles|BrettGiles]] 20:02, 22 July 2007 (UTC)<br />
<br />
Hi, it's not a matter of feeling strongly about it, but the fact that pages are getting indexed by search engines outside this wiki too, and titles matter: someone looking for a page on X window programming may just be misguided by the page title of a tutorial that only discuss the way of programming the X server from the Haskell side but whose title suggests it is dedicated to discussing general X programming issues. <br />
As the guidelines say "it is important that a page title is a title".<br />
While I may understand your argument for pages discussing specific Haskell issues, for a page discussing bindings to a library in a foreign language it seems plainly wrong to me.<br />
Still I'm open to discussion (which means: we discuss and ''then'' a decision is taken). AndreaRossato 23:02 22 July 2007 (CEST)</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=X_window_programming_in_Haskell&diff=14592X window programming in Haskell2007-07-22T18:47:39Z<p>AndreaRossato: X window programming moved to X window programming in Haskell</p>
<hr />
<div>==Writing an X application with Haskell Xlib bindings==<br />
<br />
This tutorial is a side product of the research and the learning<br />
experience of writing a<br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmobar status bar] <br />
for the [http://xmonad.org XMonad Window Manager], the<br />
first WM written in Haskell.<br />
<br />
It will show you how to write a simple X application using the low<br />
level Xlib library. The goal is to write a simple text base clock,<br />
that will display the system time, to be run on top of every other<br />
applications, like a status bar.<br />
<br />
While the application is fairly simple, still it will require us to<br />
get to know quite a lot of the details that must be taken into account<br />
when writing a properly working X application.<br />
<br />
Obviously some understanding of X and Xlib is required.<br />
<br />
These are some links that can be used as reference:<br />
<br />
* [http://www.tronche.com/gui/x/xlib/ The Xlib Manual]: this is the reference manual, and you should look up here every function that we are going to use in this tutorial.<br />
* [http://en.wikipedia.org/wiki/Xlib Xlib (Wikipedia)]<br />
* [http://en.wikipedia.org/wiki/X_Window_System_core_protocol X Window System core protocol (Wikipedia)]<br />
* [http://www.sbin.org/doc/Xlib/ Xlib Programming Manual]: specifically the [http://www.sbin.org/doc/Xlib/chapt_02.html Chapter 2 X Concepts]<br />
<br />
This tutorial is dedicated to the intermediate Haskell coder. While I<br />
will try to write the simplest code I can (probably it will even look<br />
the dumbest, but that's me), I'm not going into much details about the<br />
Haskell part.<br />
<br />
What are we going to learn:<br />
* how to create a window and set, or change, its attributes;<br />
* how to draw in that window, specifically some text, with some properties, like fonts or colors;<br />
* how to properly update the window;<br />
* how to handle events, like a mouse button press.<br />
<br />
In order to compile the following code examples you need at least:<br />
<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11 X11], the Haskell binding to the X11 graphics library.<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras]: will be required in some examples. This library provides missing bindings to the X11 graphics library and is actively developed by Spencer Janssen at the time of this writing. Some functions needed in this tutorial can be found only in the darcs repository of X11-extras: [http://darcs.haskell.org/~sjanssen/X11-extras http://darcs.haskell.org/~sjanssen/X11-extras]. Read carefully the README before installing.<br />
<br />
<br />
<br />
==Hello World==<br />
<br />
Let's start from the usual simple "Hello World"<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
border = blackPixel dpy dflt<br />
background = whitePixel dpy dflt<br />
rootw <- rootWindow dpy dflt<br />
win <- createSimpleWindow dpy rootw 0 0 100 100 1 border background<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
</haskell><br />
<br />
The first function, <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AopenDisplay openDisplay],<br />
is the interface to the Xlib function <br />
[http://www.tronche.com/gui/x/xlib/display/opening.html XOpenDisplay()], <br />
and opens a connection to the X sever that controls a display. The connection is returned and bound to dpy. By applying <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AdefaultScreen defaultScreen], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#DefaultScreen XDefaultScreen], <br />
we get the default screen, that is required in many of the following functions.<br />
With<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3ArootWindow rootWindow], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#RootWindow XRootWindow()], <br />
we get the root window. We need it in order to set the parent window in the most important function of the above code: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AcreateSimpleWindow createSimpleWindow], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html XCreateSimpleWindow].<br />
<br />
This function takes, as arguments: the display, the parent window of<br />
the window to be created, the x position, the y position, the width,<br />
the height, the border width, the border pixel and the background<br />
pixel.<br />
<br />
The x and y positions are relative to the upper left corner of the<br />
parent window's inside borders.<br />
<br />
In order to retrieve the values of the black and white pixels for that<br />
specific screen, we use two specific functions:<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AblackPixel blackPixel], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#BlackPixel BlackPixel], <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AwhitePixel whitePixel], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#WhitePixel WhitePixel]<br />
<br />
The function createSimpleWindow will return the window ID and, with<br />
this ID, we can start manipulating our newly created window, as we do,<br />
in the above code, with the function<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AsetTextProperty setTextProperty], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/ICC/client-to-window-manager/XSetTextProperty.html XSetTextProperty()].<br />
<br />
This function is needed, in our code, to set the window's name, that<br />
your window manager will display on some decoration attached to the<br />
window (other window managers will not display anything, for instance<br />
a tiling WM like [http://xmonad.org XMonad])<br />
<br />
To set the window's name we need to manipulate the <br />
[http://www.tronche.com/gui/x/xlib/ICC/client-to-window-manager/converting-string-lists.html XTextProperty structure]. <br />
<br />
Properties, such as the XTextProperty, have a string name and a<br />
numerical identifier called an atom. An atom is an ID that uniquely<br />
identifies a particular property. Property name strings are typically<br />
all upper case - with the first letter in low case when translated<br />
into Haskell - with words separated by underscores. In our example we<br />
had to set the WM_NAME property to "Hello World".<br />
<br />
Creating and manipulating a window is just the first step to have a<br />
new window displayed. In order for the window to become visible we<br />
must map it with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AmapWindow mapWindow], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/window/XMapWindow.html XMapWindow()]. <br />
This will make the window visible.<br />
<br />
Xlib will not send requests and calls to the Xserver immediately, but<br />
will buffer them and send the full buffer when some given conditions<br />
are met.<br />
<br />
One way to force the flushing of the output buffer is to call <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Event.html#v%3Async sync], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/event-handling/XSync.html XSync()], <br />
which takes 2 arguments: the connection (dpy) and a Boolean value that<br />
indicates whether XSync() must discard all events on the event queue.<br />
<br />
After that the Xserver will eventually display our window. <br />
<br />
The rest of the above example does nothing else but blocking the<br />
program execution for 10 seconds (to let you stare at your newly<br />
created window) and then will exit.<br />
<br />
==Window's attributes==<br />
<br />
Even though in our "Hello World" example we set the window's<br />
dimension, we have no assurance that the Window Manager will respect<br />
our decision.<br />
<br />
[[Xmonad]], for instance, will just create a window<br />
with the dimensions needed to fill its tiled screen, no matter what<br />
you set in createSimpleWindow.<br />
<br />
But we decided to write a small clock that will behave as a status<br />
bar, that is to say, we want to create a window that will not be<br />
managed by a Window Manager.<br />
<br />
In order to achieve this result we need to start dealing with window's<br />
attributes.<br />
<br />
There are two ways of dealing with window's attributes: the first is<br />
to set them at window's creation time, but in that case<br />
createSimpleWindow is not powerful enough.<br />
<br />
The second way is to change window's attributes after the window's has<br />
been created. This second approach is not implemented<br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11 X11] but<br />
has been implemented in the darcs version of <br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras].<br />
<br />
===Setting the window attributes at creation time===<br />
<br />
In order to set window's attributes at creation time, the window must be created with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AcreateWindow createWindow], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html XCreateWindow()].<br />
<br />
The type signature of this function is quite long:<br />
<br />
<haskell><br />
<br />
createWindow :: Display -> Window <br />
-> Position -> Position <br />
-> Dimension -> Dimension <br />
-> CInt <br />
-> CInt <br />
-> WindowClass <br />
-> Visual <br />
-> AttributeMask <br />
-> Ptr SetWindowAttributes <br />
-> IO Window<br />
<br />
</haskell><br />
<br />
That is to say:<br />
* the connection and the parent window<br />
* the x and y position (origins in the upper left corner of the inside border of the parent window)<br />
* width and height<br />
* border width<br />
* depth of screen<br />
* the window's class<br />
* the visual<br />
* the attribute mask<br />
* and the pointer to the [http://www.tronche.com/gui/x/xlib/window/attributes/#XSetWindowAttributes XSetWindowAttributes] foreign C structure.<br />
<br />
This last one gives you an idea of the type of operation we must do in<br />
order to create a window (createSimpleWindow is just a wrapper around<br />
this more complicated createWindow, with some default arguments): we<br />
need a function to allocate some memory for the creation of the<br />
foreign C structure, and then manipulate this foreign structure from<br />
within this function.<br />
<br />
The needed function is <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AallocaSetWindowAttributes allocaSetWindowAttributes], <br />
whose type indeed is:<br />
<br />
<haskell><br />
<br />
allocaSetWindowAttributes :: (Ptr SetWindowAttributes -> IO a) -> IO a<br />
<br />
</haskell><br />
<br />
allocaSetWindowAttributes will take a function which takes the pointer<br />
to the foreign structure as its argument. This function will perform<br />
an IO action that is the action returned by allocaSetWindowAttributes.<br />
<br />
In our case allocaSetWindowAttributes will take a function that will<br />
use createWindow to return the new window.<br />
<br />
So, we will need to use createWindow inside allocaSetWindowAttributes.<br />
We will soon see how. But first let's analyze the other arguments of<br />
createWindow.<br />
<br />
The display, the parent window, the coordinates and dimensions are the<br />
same as with createSimpleWindow. But now we must specify the depth of<br />
the screen, the window's class, the visual and the attribute mask. We<br />
also need to manipulate the XSetWindowAttribute after its creation by<br />
allocaSetWindowAttributes, before calling createWindow.<br />
<br />
«The depth is the number of bits available for each pixel to represent color (or gray scales). The visual represents the way pixel values are translated to produce color or monochrome output on the monitor.»( see [http://www.sbin.org/doc/Xlib/chapt_02.html http://www.sbin.org/doc/Xlib/chapt_02.html])<br />
<br />
For the depth we are going to use <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Screen.html#v%3AdefaultDepthOfScreen defaultDepthOfScreen], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/image-format-macros.html#DefaultDepthOfScreen XDefaultDepthOfScreen()], <br />
to retrieve the default screen depth.<br />
<br />
For the visual we are going to use<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Screen.html#v%3AdefaultVisualOfScreen defaultVisualOfScreen], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/image-format-macros.html#DefaultVisualOfScreen DefaultVisualOfScreen].<br />
<br />
The <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3AWindowClass WindowClass] <br />
can either be copyFromParent, inputOutput, or inputOnly. In the first case the<br />
class is copied from the class of the parent window. An inputOnly<br />
window can only be used for receiving input events. In our code we are<br />
going to use inputOutput windows, windows that can receive input events<br />
and that can also be used to display some output.<br />
<br />
The attributeMask «specifies which window attributes are defined in<br />
the attributes argument. This mask is the bitwise inclusive OR of the<br />
valid attribute mask bits. If value mask is zero, the attributes are<br />
ignored and are not referenced.»<br />
(see [http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html]).<br />
<br />
In other words, in order to set more then one attribute, you need to pass a value mask such as:<br />
<br />
<haskell><br />
<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel .|. etc ...<br />
<br />
</haskell><br />
<br />
and set each of this attributes within allocaSetWindowAttributes with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#12 specific attributes setting functions].<br />
<br />
Among these functions the one we need: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_override_redirect set_override_redirect], <br />
whose type is:<br />
<br />
<haskell><br />
<br />
set_override_redirect :: Ptr SetWindowAttributes -> Bool -> IO ()<br />
<br />
</haskell><br />
<br />
This function takes the pointer to the XSetWindowAttributes structure and the flag to be set (True or False).<br />
<br />
For the list of avaliable attributes see<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3AAttributeMask the AttributeMask type defnition].<br />
<br />
For their meaning see <br />
[http://www.tronche.com/gui/x/xlib/window/attributes/#XSetWindowAttributes the XSetWindowAttributes structure reference].<br />
<br />
Now, our goal was to create a window that the Window Manager is going<br />
to ignore, and in order to do that all we need to set the attribute<br />
[http://www.tronche.com/gui/x/xlib/window/attributes/override-redirect.html CWOverrideRedirect] <br />
to True. And now we know how to do it.<br />
<br />
Ok, it's time to introduce our function to create new windows with the CWOverrideRedirect set to True<br />
<br />
<haskell><br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr) <br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
</haskell><br />
<br />
Like simpleCreateWindow, our function is a wrapper around<br />
createWindow, but this time we are manually setting the<br />
CWOverrideRedirect flag.<br />
<br />
As you see our function, unlike createSimpleWindow, does not have,<br />
among its arguments, the background and the border pixels. This colors<br />
can be set, for windows created with createWindow, using the attribute<br />
mask, and setting CWBackPixel and CWBorderPixel with the needed<br />
functions: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_background_pixel set_background_pixel] <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_border_pixel set_border_pixel].<br />
<br />
By the way, setting the border color with this version of<br />
mkUnmanagedWindow is actually useless since the border width is set to<br />
zero. In the next example we will set it to 1.<br />
<br />
Our function needs also the screen now, since we have to retrieve the<br />
default depth and visual.<br />
<br />
We can now rewrite our initial code using the new function now.<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
set_background_pixel attributes $ whitePixel dpy (defaultScreen dpy)<br />
set_border_pixel attributes $ blackPixel dpy (defaultScreen dpy)<br />
createWindow dpy rw x y w h 1 (defaultDepthOfScreen scr)<br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
</haskell><br />
<br />
Ok, let's give it a try. Did you see? Now the window will be placed in<br />
the specified x and y position, with the given dimensions. No Window<br />
Manager decorations, and so, no name displayed.<br />
<br />
===Changing the attributes of an existing window===<br />
<br />
This task requires <br />
[http://www.tronche.com/gui/x/xlib/window/XChangeWindowAttributes.html XChangeWindowAttrbutes()], <br />
implemented only in the darcs version of <br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras].<br />
<br />
In order to change a window's attributes we just need the window ID in<br />
that specific X server, after that we need to unmap the window first,<br />
and then change its attributes with changeWindowAttributes, the<br />
interface to XChangeWindowAttrbutes() implemented by<br />
[http://darcs.haskell.org/~sjanssen/X11-extras the darcs version of X11-extras].<br />
<br />
Here's the code:<br />
<br />
<haskell><br />
<br />
module Main where<br />
<br />
import Graphics.X11.Xlib<br />
import Graphics.X11.Xlib.Extras<br />
import System.Environment<br />
<br />
usage :: String -> String<br />
usage n = "Usage: " ++ n ++ " manage/unmanage windowID"<br />
<br />
main :: IO ()<br />
main = do<br />
args <- getArgs<br />
pn <- getProgName<br />
let (win,ac) = case args of<br />
[] -> error $ usage pn<br />
w -> case (w !!0) of <br />
"manage" -> (window, False)<br />
"unmanage" -> (window, True)<br />
_ -> error $ usage pn<br />
where window = case (w !! 1) of <br />
[] -> error $ usage pn<br />
w -> read w :: Window<br />
dpy <- openDisplay ""<br />
unmapWindow dpy win<br />
sync dpy False<br />
allocaSetWindowAttributes $<br />
\attributes -> do<br />
set_override_redirect attributes ac<br />
changeWindowAttributes dpy win cWOverrideRedirect attributes<br />
mapWindow dpy win<br />
sync dpy False<br />
<br />
</haskell><br />
<br />
Save it as Unmanage.hs and compile with:<br />
ghc --make Unmanage.hs -o unmanage<br />
<br />
To use it you need to retrieve the window ID with the stand alone utility<br />
xwininfo<br />
<br />
Then you run the above code with:<br />
unmanage unmanage/manage windowID<br />
<br />
to set override_redirect to True or False.<br />
<br />
Obviously the important part of the code is this:<br />
<haskell><br />
dpy <- openDisplay ""<br />
unmapWindow dpy win<br />
sync dpy False<br />
allocaSetWindowAttributes $<br />
\attributes -> do<br />
set_override_redirect attributes ac<br />
changeWindowAttributes dpy win cWOverrideRedirect attributes<br />
mapWindow dpy win<br />
sync dpy False<br />
</haskell><br />
<br />
where we:<br />
# connect to the X server<br />
# unmap the window<br />
# flush the output buffer to have the X server actually unmap the window<br />
# change the attributes with the same procedure we used to set them when creating the window<br />
# map the window again<br />
# flush the output buffer to see the change take effect.<br />
<br />
You can modify this program to change other window's attributes.<br />
<br />
==Colors and color depth==<br />
<br />
So far we have set the window background color as a window attribute.<br />
This is not the most convenient way to set the window background<br />
color: if we need to change it, we must change the window's attribute,<br />
and we have seen that this task requires unmapping the window,<br />
flushing the output with changeWindowAttributes within<br />
changeWindowAttributes, remapping the window and reflushing the output<br />
buffer. Moreover we can do that only we the darcs version of<br />
X11-extras...<br />
<br />
In the following sections we are going to adopt a more efficient way<br />
of setting the window's background color: we will start drawing into<br />
the window. But first we must familiarize with colors and the way the<br />
X server deals with them.<br />
<br />
So far we have set the colors by using some functions to retrieve<br />
their pixel values: blackPixel and whitePixel. These functions take<br />
the display and the default screen and return respectively the pixel<br />
values for the black and the white colors in that screen.<br />
<br />
A color is represented by a 32-bit unsigned integer, called a pixel<br />
value. The elements affecting the pixel representation of a color are:<br />
1. the color depth; 2. the colormap, which is a table containing red,<br />
green, and blue intensity values; 3. the visual type.<br />
<br />
All these elements are specific to a given piece of hardware, and so<br />
our X application must detect them in order to set colors<br />
appropriately for that given hardware.<br />
<br />
The approach we are going to use to accomplish this task is this: we<br />
are going to use named colors, or colors represented by<br />
[http://en.wikipedia.org/wiki/RGB RGB triple], such as "red",<br />
"yellow", or "#FFFFFF", etc; and we are going to translate these<br />
colors into the pixel values appropriate for the screen we are<br />
operating on.<br />
<br />
In order to achieve our goal we are going to use the function <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Color.html#v%3AallocNamedColor allocNamedColor] <br />
which is the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/color/XAllocNamedColor.html XAllocNamedColor()].<br />
<br />
The type signature of allocNamedColor is:<br />
<br />
<haskell><br />
<br />
allocNamedColor :: Display -> Colormap -> String -> IO (Color, Color)<br />
<br />
</haskell><br />
<br />
That is to say, given a display connection, a color map and a string -<br />
our color representation -, this function will return a tuple with the<br />
closest RGB values provided by the hardware and the exact RGB values,<br />
both encoded in a <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib.html#v%3AColor Haskell Color data type]. <br />
We will use the first approximated value.<br />
<br />
The Color data type has a field name we will use to retrieve the<br />
needed pixel value: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib.html#v%3Acolor_pixel color_pixel].<br />
<br />
We can now write this helper function:<br />
<br />
<haskell><br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(approx,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel approx <br />
<br />
</haskell><br />
<br />
To retrieve the colormap of the screen we used <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AdefaultColormap defaultColormap], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#DefaultColormap XDefaultColormap()], <br />
which requires the display and the screen, and returns the colormap of that screen.<br />
<br />
We can now rewrite our example using this new approach.<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel<br />
background_color <- initColor dpy "red"<br />
border_color <- initColor dpy "black" <br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
set_background_pixel attributes background_color<br />
set_border_pixel attributes border_color<br />
createWindow dpy rw x y w h 1 (defaultDepthOfScreen scr)<br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(apros,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel apros<br />
<br />
</haskell><br />
<br />
Just give it a try. Now you can also experiment with different colors.<br />
This approach will assure that our application will work no matter the<br />
color depth of the screen we are working on.<br />
<br />
==Drawing in windows==<br />
<br />
The X server provides two objects that can be used to draw something<br />
to: windows and pixmaps.<br />
<br />
In this section we will start drawing into windows.<br />
<br />
We have seen that changing the background color of a window is a<br />
troublesome operation, since the window must be unamapped and<br />
remapped, memory for a foreign structure allocated, and so on.<br />
<br />
Instead, we can use some graphic operations to draw a rectangle over<br />
the window. We will latter manipulate the foreground, visible, color of<br />
this rectangle, that will become the new background of our window.<br />
<br />
We can also use multiple rectangles with different dimension to<br />
decorate our window with a border, for instance.<br />
<br />
Later on we will print some text over these rectangles.<br />
<br />
===Drawing rectangles in a window===<br />
<br />
Citing from<br />
[http://en.wikipedia.org/wiki/X_Window_System_core_protocol#Graphic_contexts_and_fonts Wikipedia]:<br />
<br />
<blockquote><br />
The client can request a number of graphic operations, such clearing<br />
an area, copying an area into another, drawing points, lines,<br />
rectangles, and text. Beside clearing, all operations are possible on<br />
all drawables, both windows and pixmaps.<br />
<br />
Most requests for graphic operations include a graphic context, which<br />
is a structure that contains the parameters of the graphic operations.<br />
A graphic context includes the foreground color, the background color,<br />
the font of text, and other graphic parameters. When requesting a<br />
graphic operation, the client includes a graphic context.<br />
</blockquote> <br />
<br />
In other words, as for setting window's attribute, we must use a<br />
foreign C structure to set parameters for graphic operations, and then<br />
we will feed this structure to the <br />
[http://www.tronche.com/gui/x/xlib/graphics/ functions] <br />
that will perform these graphic operations.<br />
<br />
We one difference: instead of operating within a function that<br />
allocates memory and creates a pointer to the foreign structure, now<br />
we have to explicitally create the Graphic Context, and free it after<br />
having used it, with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AcreateGC createGC], <br />
the interface to <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AcreateGC XCreateGC], <br />
and<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AfreeGC freeGC], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/GC/XFreeGC.html XFreeGC].<br />
<br />
Be careful: if you create a graphic con without freeing it after use,<br />
you are going to end up with a noticeable memory leak!<br />
<br />
The specific graphic functions we are going to need for drawing a<br />
rectangle into our window are:<br />
<br />
# [http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AsetForeground setForegrond] the interface to [http://www.tronche.com/gui/x/xlib/GC/convenience-functions/XSetForeground.html XSetForegroung]<br />
# [http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AsetBackground setBackground] the interface to [http://www.tronche.com/gui/x/xlib/GC/convenience-functions/XSetBackground.html XSetBackground]<br />
# [http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AfillRectangle fillRectangle] the interface to [http://www.tronche.com/gui/x/xlib/graphics/filling-areas/XFillRectangle.html XFillRectangle]<br />
<br />
The first two functions are needed to set the parameters in the<br />
[http://www.tronche.com/gui/x/xlib/GC/manipulating.html Graphic Context]. <br />
The third one will use this GC for filling a rectangle on the<br />
specified window. Just have a look to their type signatures:<br />
<br />
<haskell><br />
<br />
setForeground :: Display -> GC -> Pixel -> IO ()<br />
setBackground :: Display -> GC -> Pixel -> IO ()<br />
fillRectangle :: Display -> Drawable -> GC -> Position -> Position -> Dimension -> Dimension -> IO ()<br />
<br />
</haskell><br />
<br />
Ok, this is the function that we will be using for drawing into a<br />
window:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> IO ()<br />
drawInWin dpy win = do<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 0 0 100 100<br />
freeGC dpy gc<br />
<br />
</haskell><br />
<br />
This will just fill our window with a rectangle at (0, 0) (x, y)<br />
coordinates (relatives to the window's internal border), with the same<br />
dimensions of our window.<br />
<br />
Obviously we can play a bit with rectangles. This version, for<br />
instance, will draw 2 rectangles to simulate a blu rectangle with a<br />
green border, two pixels width:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> IO ()<br />
drawInWin dpy win = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 100 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 96 96<br />
freeGC dpy gc<br />
<br />
</haskell><br />
<br />
You can use this function on a mapped window. This is our original<br />
example rewritten with this new approach:<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
drawInWin dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
drawInWin :: Display -> Window -> IO ()<br />
drawInWin dpy win = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 100 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 96 96<br />
freeGC dpy gc<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr)<br />
inputOutput visual cWOverrideRedirect attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
<br />
</haskell><br />
<br />
As you see, now mkUnmanagedWindow sets a null border width and does<br />
not set any background color. Everything is easily done with<br />
rectangles.<br />
<br />
===Printing a string===<br />
<br />
Printing a string to a window requires operating with another foreign<br />
C structure, the <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/ XFontStruct], <br />
which contains all of the information regarding the metrics of font<br />
that the X server will use to display our string.<br />
<br />
This structure will be used to perform some computations that are<br />
required for the correct placement of the text in the window.<br />
<br />
As we have seen with the window's attributes and the Graphic Context,<br />
we need a function that returns a pointer to this foreign structure,<br />
pointer that must be freed after using it.<br />
<br />
A pointer to the XFontStruct is returned by <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AloadQueryFont loadQueryFont], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XLoadQueryFont.html XLoadQueryFont()].<br />
<br />
XLoadQueryFont, which requires the X connection and the font name,<br />
will perform two distinct operations: load the needed font and return<br />
its id<br />
(performed by [http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XLoadFont.html XLoadFont()] <br />
which doesn't have a Haskell interface) and query the font to retrieve the XFontStruct <br />
(performed by [http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XQueryFont.html XQueryFont] <br />
which does have a Haskell interface:<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AqueryFont queryFont]).<br />
<br />
The XFontStruct is needed by <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AtextExtents textExtents], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XTextExtents.html XTexteExtent()], <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AtextWidth textWidth], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XTextWidth.html XTextWidth()].<br />
<br />
These are their type signatures.<br />
<br />
<haskell><br />
<br />
textExtents :: FontStruct -> String -> (FontDirection, Int32, Int32, CharStruct)<br />
textWidth :: FontStruct -> String -> Int32<br />
<br />
</haskell><br />
<br />
Given the FontStruct and the string to be printed, these functions<br />
will provide some valuable information. The values returned by the<br />
first one are related to font direction and vertical placement, while<br />
the second one will return the total width of the string to be printed<br />
with that specific font.<br />
<br />
This information can be used with the graphic function that will<br />
actually draw the text on the window: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AdrawImageString drawImageString], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/drawing-text/XDrawImageString.html XDrawImageString].<br />
<br />
There are other version of this string, <br />
[http://www.tronche.com/gui/x/xlib/graphics/drawing-text/XDrawText.html XDrawText()] <br />
and <br />
[http://www.tronche.com/gui/x/xlib/graphics/drawing-text/XDrawText16.html XDrawText16()] <br />
for 16-bit characters.<br />
<br />
We are going to use the first one because it will also use the<br />
foreground pixel set in the Graphic Context.<br />
<br />
It's type signature is:<br />
<br />
<haskell><br />
<br />
drawImageString :: Display -> Drawable -> GC -> Position -> Position -> String -> IO ()<br />
<br />
</haskell><br />
<br />
Finally we must remember to free the FontStruct with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AfreeFont freeFont] <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XFreeFont.html XFreeFont].<br />
<br />
We can now write our function to print a string on a window, but first<br />
we need to make some small modifications to our darwInWin function,<br />
that will now take a string and will load and free the needed<br />
FontStruct to be passed to the new printString function:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> String -> IO ()<br />
drawInWin dpy win str = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
fontStruc <- loadQueryFont dpy "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 200 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 196 96<br />
printString dpy win gc fontStruc str<br />
freeGC dpy gc<br />
freeFont dpy fontStruc<br />
<br />
</haskell><br />
<br />
Here we are loading the "misc-fixed" font. You can select different<br />
fonts with the standalone utility:<br />
xfontsel<br />
<br />
As you see the FontStruct retrieved by loadQueryFont is used by<br />
printString and then freed.<br />
<br />
So, let's look printString:<br />
<br />
<br />
<haskell><br />
<br />
printString :: Display <br />
-> Drawable <br />
-> GC<br />
-> FontStruct<br />
-> String<br />
-> IO ()<br />
printString dpy d gc fontst str =<br />
do let strLen = textWidth fontst str<br />
(_,asc,_,_) = textExtents fontst str<br />
valign = (100 + fromIntegral asc) `div` 2<br />
remWidth = 200 - strLen<br />
offset = remWidth `div` 2<br />
fgcolor <- initColor dpy "white"<br />
bgcolor <- initColor dpy "blue"<br />
setForeground dpy gc fgcolor<br />
setBackground dpy gc bgcolor<br />
drawImageString dpy d gc offset valign str<br />
<br />
</haskell><br />
<br />
In the "let" part we use textWidth and textExtents to set vertical and<br />
horizontal alignment: this is done by calculating the x and y<br />
coordinates for drawImageString. In this example text will be<br />
vertically and horizontally centered (take into account that I have<br />
also enlarged the window, whose width now is 200 pixels).<br />
<br />
For a reference of the meaning of font ascent and descent, and the<br />
origins of the rectangle drawn by drawImageString read the par. 8.6<br />
([http://www.tronche.com/gui/x/xlib/graphics/drawing-text/ Drawing Text]) <br />
of the <br />
[http://www.tronche.com/gui/x/xlib/ The Xlib Manual].<br />
<br />
You may notice that printString takes a <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3ADrawable Drawable], <br />
which can be either a window or a pixmap (see below).<br />
<br />
We can now rewrite our example and finally see some text printed in<br />
our window:<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import System.Time<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 200 100<br />
setTextProperty dpy win "The Clock" wM_NAME <br />
mapWindow dpy win<br />
drawInWin dpy win =<< date<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
date :: IO String<br />
date = do <br />
t <- getClockTime<br />
return $ calendarTimeToString . toUTCTime $ t <br />
<br />
drawInWin :: Display -> Window -> String -> IO ()<br />
drawInWin dpy win str = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
fontStruc <- loadQueryFont dpy "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 200 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 196 96<br />
printString dpy win gc fontStruc str<br />
freeGC dpy gc<br />
freeFont dpy fontStruc<br />
<br />
printString :: Display <br />
-> Drawable <br />
-> GC<br />
-> FontStruct<br />
-> String<br />
-> IO ()<br />
printString dpy d gc fontst str =<br />
do let strLen = textWidth fontst str<br />
(_,asc,_,_) = textExtents fontst str<br />
valign = (100 + fromIntegral asc) `div` 2<br />
remWidth = 200 - strLen<br />
offset = remWidth `div` 2<br />
fgcolor <- initColor dpy "white"<br />
bgcolor <- initColor dpy "blue"<br />
setForeground dpy gc fgcolor<br />
setBackground dpy gc bgcolor<br />
drawImageString dpy d gc offset valign str<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr)<br />
inputOutput visual cWOverrideRedirect attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(apros,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel apros<br />
<br />
</haskell><br />
<br />
Since we are going to display the system time, I already added a<br />
"date" function and increased to width of our window. <br />
<br />
Just give it a try. We are almost there. It's a clock, after all.<br />
<br />
==Updating a window==<br />
<br />
If you do not believe that now we have a system clock, just change the<br />
main function of the above example with the following two functions and<br />
try yourself:<br />
<br />
<haskell><br />
<br />
main :: IO ()<br />
main = do <br />
dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 200 100<br />
setTextProperty dpy win "The Clock" wM_NAME <br />
mapWindow dpy win<br />
updateWin dpy win<br />
<br />
updateWin :: Display -> Window -> IO ()<br />
updateWin dpy win = do<br />
drawInWin dpy win =<< date<br />
sync dpy False<br />
threadDelay (1 * 1000000)<br />
updateWin dpy win<br />
<br />
</haskell><br />
<br />
This piece of code just adds the eternal recursive loop and, within<br />
this loop, reduces the thread block to 1 second. That's it.<br />
<br />
Every second our window will be updated - redrawn.<br />
<br />
Now, if you let the clock run for a while, you will notice that<br />
sometimes, during an update, the window sort of flickers.<br />
<br />
This is due to the fact that we are drawing directly over the window.<br />
We need to adopt a better technique: we need to write to a pixmap<br />
first, and then copy the pixmap over the window.<br />
<br />
Citing from<br />
[http://en.wikipedia.org/wiki/X_Window_System_core_protocol#Pixmaps_and_drawables Wikipedia]:<br />
<br />
<blockquote> <br />
«A pixmap is a region of memory that can be used for<br />
drawing. Contrary to windows, the contents of pixmaps are not<br />
automatically shown on the screen. However, the content of a pixmap<br />
(or a part of it) can be transferred to a window and vice versa. This<br />
allows for techniques such as <br />
[http://en.wikipedia.org/wiki/Double_buffering double buffering]. <br />
Most of the graphical<br />
operations that can be done on windows can also be done on pixmaps.<br />
Windows and pixmaps are collectively named drawables, and their<br />
content data resides on the server.» <br />
</blockquote><br />
<br />
This is not very difficult, and requires a very small change of our<br />
drawInWin function.<br />
<br />
In order to create the pixmap we will use <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AcreatePixmap createPixmap], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/pixmap-and-cursor/XCreatePixmap.htm XCreatePixmap()], <br />
which takes the display connection, the drawable upon which the pixmap<br />
is created, the width, the height, and the depth of the screen.<br />
<br />
We will then use <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AcopyArea copyArea], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/XCopyArea.html XCopyArea], <br />
to copy the pixmap over the window.<br />
<br />
This is the copyArea type signature:<br />
<br />
<haskell><br />
<br />
copyArea :: Display <br />
-> Drawable -> Drawable <br />
-> GC <br />
-> Position -> Position <br />
-> Dimension -> Dimension <br />
-> Position -> Position <br />
-> IO ()<br />
<br />
</haskell><br />
<br />
that is to say:<br />
# the display connection<br />
# the origin and the destination drawables (our pixmap and our window respectively)<br />
# the x and y coordinates relative to the origin drawable upper left corner<br />
# the width and the height of the area the be copied<br />
# the x and y coordinates relative to the upper left corner of the destination drawable where the copied area must be placed<br />
<br />
And we will eventually free the pixmap with<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AfreePixmap freePixmap], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/pixmap-and-cursor/XFreePixmap.html XFreePixmap].<br />
<br />
Since our printString function may accept either a window or a pixmap,<br />
they both are drawables, all we need to do is to change drawInWin<br />
accordingly:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> String ->IO ()<br />
drawInWin dpy win str = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
fontStruc <- loadQueryFont dpy "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"<br />
p <- createPixmap dpy win 200 100 (defaultDepthOfScreen (defaultScreenOfDisplay dpy))<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy p gc 0 0 200 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy p gc 2 2 196 96<br />
printString dpy p gc fontStruc str<br />
copyArea dpy p win gc 0 0 200 100 0 0<br />
freeGC dpy gc<br />
freeFont dpy fontStruc<br />
freePixmap dpy p<br />
<br />
</haskell><br />
<br />
Now, all graphic functions take now "p" and not "win". After copyArea<br />
everything is freed.<br />
<br />
Our clock:<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import System.Time<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main = do <br />
dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 200 100<br />
setTextProperty dpy win "Hello World - The Clock" wM_NAME <br />
mapWindow dpy win<br />
updateWin dpy win<br />
<br />
updateWin :: Display -> Window -> IO ()<br />
updateWin dpy win = do<br />
drawInWin dpy win =<< date<br />
sync dpy False<br />
threadDelay (1 * 1000000)<br />
updateWin dpy win<br />
<br />
date :: IO String<br />
date = do <br />
t <- getClockTime<br />
return $ calendarTimeToString . toUTCTime $ t <br />
<br />
drawInWin :: Display -> Window -> String ->IO ()<br />
drawInWin dpy win str = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
fontStruc <- loadQueryFont dpy "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"<br />
p <- createPixmap dpy win 200 100 (defaultDepthOfScreen (defaultScreenOfDisplay dpy))<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy p gc 0 0 200 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy p gc 2 2 196 96<br />
printString dpy p gc fontStruc str<br />
copyArea dpy p win gc 0 0 200 100 0 0<br />
freeGC dpy gc<br />
freeFont dpy fontStruc<br />
freePixmap dpy p<br />
<br />
<br />
printString :: Display <br />
-> Drawable <br />
-> GC<br />
-> FontStruct<br />
-> String<br />
-> IO ()<br />
printString dpy d gc fontst str =<br />
do let strLen = textWidth fontst str<br />
(_,asc,_,_) = textExtents fontst str<br />
valign = (100 + fromIntegral asc) `div` 2<br />
remWidth = 200 - strLen<br />
offset = remWidth `div` 2<br />
fgcolor <- initColor dpy "white"<br />
bgcolor <- initColor dpy "blue"<br />
setForeground dpy gc fgcolor<br />
setBackground dpy gc bgcolor<br />
drawImageString dpy d gc offset valign str<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr)<br />
inputOutput visual cWOverrideRedirect attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(apros,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel apros<br />
<br />
</haskell><br />
<br />
<br />
==Dealing with XEvents==<br />
<br />
Now try this.<br />
<br />
In updateWin set threadDelay to something like:<br />
<br />
<haskell><br />
<br />
threadDelay (60 * 1000000)<br />
<br />
</haskell><br />
<br />
Run the clock, switch to a console (with Alt+Ctrl+F1) and come back to<br />
the X server where the clock is running.<br />
<br />
'''To be continued ...'''<br />
<br />
Two approaches<br />
<br />
- [[User:AndreaRossato|Andrea Rossato]]<br />
<br />
[[Category:Tutorials]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=X_window_programming&diff=14593X window programming2007-07-22T18:47:39Z<p>AndreaRossato: X window programming moved to X window programming in Haskell: I was writing a tutorial about X window programming in Haskell, and not about x window programming. If the haskewllwiki guidelines about page title forbids me from doing so I won't finish the </p>
<hr />
<div>#redirect [[X window programming in Haskell]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=User_talk:BrettGiles&diff=14591User talk:BrettGiles2007-07-22T18:19:59Z<p>AndreaRossato: just asking</p>
<hr />
<div>Why did you rename the page I created, X Window Programming in Haskell, to X Window Programming?<br />
<br />
It is not about X Window programming, but about X Window Programming IN HASKELL!</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=X_window_programming_in_Haskell&diff=14557X window programming in Haskell2007-07-20T13:51:43Z<p>AndreaRossato: updating windows with XCopyArea</p>
<hr />
<div>==Writing an X Application With Haskell Xlib Bindings==<br />
<br />
This tutorial is a side product of the research and the learning<br />
experience of writing a<br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmobar status bar] <br />
for the [http://xmonad.org XMonad Window Manager], the<br />
first WM written in Haskell.<br />
<br />
It will show you how to write a simple X application using the low<br />
level Xlib library. The goal is to write a simple text base clock,<br />
that will display the system time, to be run on top of every other<br />
applications, like a status bar.<br />
<br />
While the application is fairly simple, still it will require us to<br />
get to know quite a lot of the details that must be taken into account<br />
when writing a properly working X application.<br />
<br />
Obviously some understanding of X and Xlib is required.<br />
<br />
These are some links that can be used as reference:<br />
<br />
* [http://www.tronche.com/gui/x/xlib/ The Xlib Manual]: this is the reference manual, and you should look up here every function that we are going to use in this tutorial.<br />
* [http://en.wikipedia.org/wiki/Xlib Xlib (Wikipedia)]<br />
* [http://en.wikipedia.org/wiki/X_Window_System_core_protocol X Window System core protocol (Wikipedia)]<br />
* [http://www.sbin.org/doc/Xlib/ Xlib Programming Manual]: specifically the [http://www.sbin.org/doc/Xlib/chapt_02.html Chapter 2 X Concepts]<br />
<br />
This tutorial is dedicated to the intermediate Haskell coder. While I<br />
will try to write the simplest code I can (probably it will even look<br />
the dumbest, but that's me), I'm not going into much details about the<br />
Haskell part.<br />
<br />
What are we going to learn:<br />
* how to create a window and set, or change, its attributes;<br />
* how to draw in that window, specifically some text, with some properties, like fonts or colors;<br />
* how to properly update the window;<br />
* how to handle events, like a mouse button press.<br />
<br />
In order to compile the following code examples you need at least:<br />
<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11 X11], the Haskell binding to the X11 graphics library.<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras]: will be required in some examples. This library provides missing bindings to the X11 graphics library and is actively developed by Spencer Janssen at the time of this writing. Some functions needed in this tutorial can be found only in the darcs repository of X11-extras: [http://darcs.haskell.org/~sjanssen/X11-extras http://darcs.haskell.org/~sjanssen/X11-extras]. Read carefully the README before installing.<br />
<br />
<br />
<br />
==Hello World==<br />
<br />
Let's start from the usual simple "Hello World"<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
border = blackPixel dpy dflt<br />
background = whitePixel dpy dflt<br />
rootw <- rootWindow dpy dflt<br />
win <- createSimpleWindow dpy rootw 0 0 100 100 1 border background<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
</haskell><br />
<br />
The first function, <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AopenDisplay openDisplay],<br />
is the interface to the Xlib function <br />
[http://www.tronche.com/gui/x/xlib/display/opening.html XOpenDisplay()], <br />
and opens a connection to the X sever that controls a display. The connection is returned and bound to dpy. By applying <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AdefaultScreen defaultScreen], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#DefaultScreen XDefaultScreen], <br />
we get the default screen, that is required in many of the following functions.<br />
With<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3ArootWindow rootWindow], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#RootWindow XRootWindow()], <br />
we get the root window. We need it in order to set the parent window in the most important function of the above code: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AcreateSimpleWindow createSimpleWindow], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html XCreateSimpleWindow].<br />
<br />
This function takes, as arguments: the display, the parent window of<br />
the window to be created, the x position, the y position, the width,<br />
the height, the border width, the border pixel and the background<br />
pixel.<br />
<br />
The x and y positions are relative to the upper left corner of the<br />
parent window's inside borders.<br />
<br />
In order to retrieve the values of the black and white pixels for that<br />
specific screen, we use two specific functions:<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AblackPixel blackPixel], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#BlackPixel BlackPixel], <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AwhitePixel whitePixel], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#WhitePixel WhitePixel]<br />
<br />
The function createSimpleWindow will return the window ID and, with<br />
this ID, we can start manipulating our newly created window, as we do,<br />
in the above code, with the function<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AsetTextProperty setTextProperty], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/ICC/client-to-window-manager/XSetTextProperty.html XSetTextProperty()].<br />
<br />
This function is needed, in our code, to set the window's name, that<br />
your window manager will display on some decoration attached to the<br />
window (other window managers will not display anything, for instance<br />
a tiling WM like [http://xmonad.org XMonad])<br />
<br />
To set the window's name we need to manipulate the <br />
[http://www.tronche.com/gui/x/xlib/ICC/client-to-window-manager/converting-string-lists.html XTextProperty structure]. <br />
<br />
Properties, such as the XTextProperty, have a string name and a<br />
numerical identifier called an atom. An atom is an ID that uniquely<br />
identifies a particular property. Property name strings are typically<br />
all upper case - with the first letter in low case when translated<br />
into Haskell - with words separated by underscores. In our example we<br />
had to set the WM_NAME property to "Hello World".<br />
<br />
Creating and manipulating a window is just the first step to have a<br />
new window displayed. In order for the window to become visible we<br />
must map it with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AmapWindow mapWindow], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/window/XMapWindow.html XMapWindow()]. <br />
This will make the window visible.<br />
<br />
Xlib will not send requests and calls to the Xserver immediately, but<br />
will buffer them and send the full buffer when some given conditions<br />
are met.<br />
<br />
One way to force the flushing of the output buffer is to call <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Event.html#v%3Async sync], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/event-handling/XSync.html XSync()], <br />
which takes 2 arguments: the connection (dpy) and a Boolean value that<br />
indicates whether XSync() must discard all events on the event queue.<br />
<br />
After that the Xserver will eventually display our window. <br />
<br />
The rest of the above example does nothing else but blocking the<br />
program execution for 10 seconds (to let you stare at your newly<br />
created window) and then will exit.<br />
<br />
==Window's Attributes==<br />
<br />
Even though in our "Hello World" example we set the window's<br />
dimension, we have no assurance that the Window Manager will respect<br />
our decision.<br />
<br />
[http://xmonad.org XMonad], for instance, will just create a window<br />
with the dimensions needed to fill its tiled screen, no matter what<br />
you set in createSimpleWindow.<br />
<br />
But we decided to write a small clock that will behave as a status<br />
bar, that is to say, we want to create a window that will not be<br />
managed by a Window Manager.<br />
<br />
In order to achieve this result we need to start dealing with window's<br />
attributes.<br />
<br />
There are two ways of dealing with window's attributes: the first is<br />
to set them at window's creation time, but in that case<br />
createSimpleWindow is not powerful enough.<br />
<br />
The second way is to change window's attributes after the window's has<br />
been created. This second approach is not implemented<br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11 X11] but<br />
has been implemented in the darcs version of <br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras].<br />
<br />
===Setting Window's Attribute at Creation Time===<br />
<br />
In order to set window's attributes at creation time, the window must be created with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AcreateWindow createWindow], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html XCreateWindow()].<br />
<br />
The type signature of this function is quite long:<br />
<br />
<haskell><br />
<br />
createWindow :: Display -> Window <br />
-> Position -> Position <br />
-> Dimension -> Dimension <br />
-> CInt <br />
-> CInt <br />
-> WindowClass <br />
-> Visual <br />
-> AttributeMask <br />
-> Ptr SetWindowAttributes <br />
-> IO Window<br />
<br />
</haskell><br />
<br />
That is to say:<br />
* the connection and the parent window<br />
* the x and y position (origins in the upper left corner of the inside border of the parent window)<br />
* width and height<br />
* border width<br />
* depth of screen<br />
* the window's class<br />
* the visual<br />
* the attribute mask<br />
* and the pointer to the [http://www.tronche.com/gui/x/xlib/window/attributes/#XSetWindowAttributes XSetWindowAttributes] foreign C structure.<br />
<br />
This last one gives you an idea of the type of operation we must do in<br />
order to create a window (createSimpleWindow is just a wrapper around<br />
this more complicated createWindow, with some default arguments): we<br />
need a function to allocate some memory for the creation of the<br />
foreign C structure, and then manipulate this foreign structure from<br />
within this function.<br />
<br />
The needed function is <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AallocaSetWindowAttributes allocaSetWindowAttributes], <br />
whose type indeed is:<br />
<br />
<haskell><br />
<br />
allocaSetWindowAttributes :: (Ptr SetWindowAttributes -> IO a) -> IO a<br />
<br />
</haskell><br />
<br />
allocaSetWindowAttributes will take a function which takes the pointer<br />
to the foreign structure as its argument. This function will perform<br />
an IO action that is the action returned by allocaSetWindowAttributes.<br />
<br />
In our case allocaSetWindowAttributes will take a function that will<br />
use createWindow to return the new window.<br />
<br />
So, we will need to use createWindow inside allocaSetWindowAttributes.<br />
We will soon see how. But first let's analyze the other arguments of<br />
createWindow.<br />
<br />
The display, the parent window, the coordinates and dimensions are the<br />
same as with createSimpleWindow. But now we must specify the depth of<br />
the screen, the window's class, the visual and the attribute mask. We<br />
also need to manipulate the XSetWindowAttribute after its creation by<br />
allocaSetWindowAttributes, before calling createWindow.<br />
<br />
«The depth is the number of bits available for each pixel to represent color (or gray scales). The visual represents the way pixel values are translated to produce color or monochrome output on the monitor.»( see [http://www.sbin.org/doc/Xlib/chapt_02.html http://www.sbin.org/doc/Xlib/chapt_02.html])<br />
<br />
For the depth we are going to use <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Screen.html#v%3AdefaultDepthOfScreen defaultDepthOfScreen], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/image-format-macros.html#DefaultDepthOfScreen XDefaultDepthOfScreen()], <br />
to retrieve the default screen depth.<br />
<br />
For the visual we are going to use<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Screen.html#v%3AdefaultVisualOfScreen defaultVisualOfScreen], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/image-format-macros.html#DefaultVisualOfScreen DefaultVisualOfScreen].<br />
<br />
The <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3AWindowClass WindowClass] <br />
can either be copyFromParent, inputOutput, or inputOnly. In the first case the<br />
class is copied from the class of the parent window. An inputOnly<br />
window can only be used for receiving input events. In our code we are<br />
going to use inputOutput windows, windows that can receive input events<br />
and that can also be used to display some output.<br />
<br />
The attributeMask «specifies which window attributes are defined in<br />
the attributes argument. This mask is the bitwise inclusive OR of the<br />
valid attribute mask bits. If value mask is zero, the attributes are<br />
ignored and are not referenced.»<br />
(see [http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html]).<br />
<br />
In other words, in order to set more then one attribute, you need to pass a value mask such as:<br />
<br />
<haskell><br />
<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel .|. etc ...<br />
<br />
</haskell><br />
<br />
and set each of this attributes within allocaSetWindowAttributes with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#12 specific attributes setting functions].<br />
<br />
Among these functions the one we need: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_override_redirect set_override_redirect], <br />
whose type is:<br />
<br />
<haskell><br />
<br />
set_override_redirect :: Ptr SetWindowAttributes -> Bool -> IO ()<br />
<br />
</haskell><br />
<br />
This function takes the pointer to the XSetWindowAttributes structure and the flag to be set (True or False).<br />
<br />
For the list of avaliable attributes see<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3AAttributeMask the AttributeMask type defnition].<br />
<br />
For their meaning see <br />
[http://www.tronche.com/gui/x/xlib/window/attributes/#XSetWindowAttributes the XSetWindowAttributes structure reference].<br />
<br />
Now, our goal was to create a window that the Window Manager is going<br />
to ignore, and in order to do that all we need to set the attribute<br />
[http://www.tronche.com/gui/x/xlib/window/attributes/override-redirect.html CWOverrideRedirect] <br />
to True. And now we know how to do it.<br />
<br />
Ok, it's time to introduce our function to create new windows with the CWOverrideRedirect set to True<br />
<br />
<haskell><br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr) <br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
</haskell><br />
<br />
Like simpleCreateWindow, our function is a wrapper around<br />
createWindow, but this time we are manually setting the<br />
CWOverrideRedirect flag.<br />
<br />
As you see our function, unlike createSimpleWindow, does not have,<br />
among its arguments, the background and the border pixels. This colors<br />
can be set, for windows created with createWindow, using the attribute<br />
mask, and setting CWBackPixel and CWBorderPixel with the needed<br />
functions: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_background_pixel set_background_pixel] <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_border_pixel set_border_pixel].<br />
<br />
By the way, setting the border color with this version of<br />
mkUnmanagedWindow is actually useless since the border width is set to<br />
zero. In the next example we will set it to 1.<br />
<br />
Our function needs also the screen now, since we have to retrieve the<br />
default depth and visual.<br />
<br />
We can now rewrite our initial code using the new function now.<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
set_background_pixel attributes $ whitePixel dpy (defaultScreen dpy)<br />
set_border_pixel attributes $ blackPixel dpy (defaultScreen dpy)<br />
createWindow dpy rw x y w h 1 (defaultDepthOfScreen scr)<br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
</haskell><br />
<br />
Ok, let's give it a try. Did you see? Now the window will be placed in<br />
the specified x and y position, with the given dimensions. No Window<br />
Manager decorations, and so, no name displayed.<br />
<br />
===Changing an Existing Window's Attributes===<br />
<br />
This task requires <br />
[http://www.tronche.com/gui/x/xlib/window/XChangeWindowAttributes.html XChangeWindowAttrbutes()], <br />
implemented only in the darcs version of <br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras].<br />
<br />
In order to change a window's attributes we just need the window ID in<br />
that specific X server, after that we need to unmap the window first,<br />
and then change its attributes with changeWindowAttributes, the<br />
interface to XChangeWindowAttrbutes() implemented by<br />
[http://darcs.haskell.org/~sjanssen/X11-extras the darcs version of X11-extras].<br />
<br />
Here's the code:<br />
<br />
<haskell><br />
<br />
module Main where<br />
<br />
import Graphics.X11.Xlib<br />
import Graphics.X11.Xlib.Extras<br />
import System.Environment<br />
<br />
usage :: String -> String<br />
usage n = "Usage: " ++ n ++ " manage/unmanage windowID"<br />
<br />
main :: IO ()<br />
main = do<br />
args <- getArgs<br />
pn <- getProgName<br />
let (win,ac) = case args of<br />
[] -> error $ usage pn<br />
w -> case (w !!0) of <br />
"manage" -> (window, False)<br />
"unmanage" -> (window, True)<br />
_ -> error $ usage pn<br />
where window = case (w !! 1) of <br />
[] -> error $ usage pn<br />
w -> read w :: Window<br />
dpy <- openDisplay ""<br />
unmapWindow dpy win<br />
sync dpy False<br />
allocaSetWindowAttributes $<br />
\attributes -> do<br />
set_override_redirect attributes ac<br />
changeWindowAttributes dpy win cWOverrideRedirect attributes<br />
mapWindow dpy win<br />
sync dpy False<br />
<br />
</haskell><br />
<br />
Save it as Unmanage.hs and compile with:<br />
ghc --make Unmanage.hs -o unmanage<br />
<br />
To use it you need to retrieve the window ID with the stand alone utility<br />
xwininfo<br />
<br />
Then you run the above code with:<br />
unmanage unmanage/manage windowID<br />
<br />
to set override_redirect to True or False.<br />
<br />
Obviously the important part of the code is this:<br />
<haskell><br />
dpy <- openDisplay ""<br />
unmapWindow dpy win<br />
sync dpy False<br />
allocaSetWindowAttributes $<br />
\attributes -> do<br />
set_override_redirect attributes ac<br />
changeWindowAttributes dpy win cWOverrideRedirect attributes<br />
mapWindow dpy win<br />
sync dpy False<br />
</haskell><br />
<br />
where we:<br />
# connect to the X server<br />
# unmap the window<br />
# flush the output buffer to have the X server actually unmap the window<br />
# change the attributes with the same procedure we used to set them when creating the window<br />
# map the window again<br />
# flush the output buffer to see the change take effect.<br />
<br />
You can modify this program to change other window's attributes.<br />
<br />
==Colors and Color Depth==<br />
<br />
So far we have set the window background color as a window attribute.<br />
This is not the most convenient way to set the window background<br />
color: if we need to change it, we must change the window's attribute,<br />
and we have seen that this task requires unmapping the window,<br />
flushing the output with changeWindowAttributes within<br />
changeWindowAttributes, remapping the window and reflushing the output<br />
buffer. Moreover we can do that only we the darcs version of<br />
X11-extras...<br />
<br />
In the following sections we are going to adopt a more efficient way<br />
of setting the window's background color: we will start drawing into<br />
the window. But first we must familiarize with colors and the way the<br />
X server deals with them.<br />
<br />
So far we have set the colors by using some functions to retrieve<br />
their pixel values: blackPixel and whitePixel. These functions take<br />
the display and the default screen and return respectively the pixel<br />
values for the black and the white colors in that screen.<br />
<br />
A color is represented by a 32-bit unsigned integer, called a pixel<br />
value. The elements affecting the pixel representation of a color are:<br />
1. the color depth; 2. the colormap, which is a table containing red,<br />
green, and blue intensity values; 3. the visual type.<br />
<br />
All these elements are specific to a given piece of hardware, and so<br />
our X application must detect them in order to set colors<br />
appropriately for that given hardware.<br />
<br />
The approach we are going to use to accomplish this task is this: we<br />
are going to use named colors, or colors represented by<br />
[http://en.wikipedia.org/wiki/RGB RGB triple], such as "red",<br />
"yellow", or "#FFFFFF", etc; and we are going to translate these<br />
colors into the pixel values appropriate for the screen we are<br />
operating on.<br />
<br />
In order to achieve our goal we are going to use the function <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Color.html#v%3AallocNamedColor allocNamedColor] <br />
which is the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/color/XAllocNamedColor.html XAllocNamedColor()].<br />
<br />
The type signature of allocNamedColor is:<br />
<br />
<haskell><br />
<br />
allocNamedColor :: Display -> Colormap -> String -> IO (Color, Color)<br />
<br />
</haskell><br />
<br />
That is to say, given a display connection, a color map and a string -<br />
our color representation -, this function will return a tuple with the<br />
closest RGB values provided by the hardware and the exact RGB values,<br />
both encoded in a <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib.html#v%3AColor Haskell Color data type]. <br />
We will use the first approximated value.<br />
<br />
The Color data type has a field name we will use to retrieve the<br />
needed pixel value: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib.html#v%3Acolor_pixel color_pixel].<br />
<br />
We can now write this helper function:<br />
<br />
<haskell><br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(approx,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel approx <br />
<br />
</haskell><br />
<br />
To retrieve the colormap of the screen we used <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AdefaultColormap defaultColormap], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#DefaultColormap XDefaultColormap()], <br />
which requires the display and the screen, and returns the colormap of that screen.<br />
<br />
We can now rewrite our example using this new approach.<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel<br />
background_color <- initColor dpy "red"<br />
border_color <- initColor dpy "black" <br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
set_background_pixel attributes background_color<br />
set_border_pixel attributes border_color<br />
createWindow dpy rw x y w h 1 (defaultDepthOfScreen scr)<br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(apros,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel apros<br />
<br />
</haskell><br />
<br />
Just give it a try. Now you can also experiment with different colors.<br />
This approach will assure that our application will work no matter the<br />
color depth of the screen we are working on.<br />
<br />
==Drawing in Windows==<br />
<br />
The X server provides two objects that can be used to draw something<br />
to: windows and pixmaps.<br />
<br />
In this section we will start drawing into windows.<br />
<br />
We have seen that changing the background color of a window is a<br />
troublesome operation, since the window must be unamapped and<br />
remapped, memory for a foreign structure allocated, and so on.<br />
<br />
Instead, we can use some graphic operations to draw a rectangle over<br />
the window. We will latter manipulate the foreground, visible, color of<br />
this rectangle, that will become the new background of our window.<br />
<br />
We can also use multiple rectangles with different dimension to<br />
decorate our window with a border, for instance.<br />
<br />
Later on we will print some text over these rectangles.<br />
<br />
===Drawing Rectangles in a Window===<br />
<br />
Citing from<br />
[http://en.wikipedia.org/wiki/X_Window_System_core_protocol#Graphic_contexts_and_fonts Wikipedia]:<br />
<br />
<blockquote><br />
The client can request a number of graphic operations, such clearing<br />
an area, copying an area into another, drawing points, lines,<br />
rectangles, and text. Beside clearing, all operations are possible on<br />
all drawables, both windows and pixmaps.<br />
<br />
Most requests for graphic operations include a graphic context, which<br />
is a structure that contains the parameters of the graphic operations.<br />
A graphic context includes the foreground color, the background color,<br />
the font of text, and other graphic parameters. When requesting a<br />
graphic operation, the client includes a graphic context.<br />
</blockquote> <br />
<br />
In other words, as for setting window's attribute, we must use a<br />
foreign C structure to set parameters for graphic operations, and then<br />
we will feed this structure to the <br />
[http://www.tronche.com/gui/x/xlib/graphics/ functions] <br />
that will perform these graphic operations.<br />
<br />
We one difference: instead of operating within a function that<br />
allocates memory and creates a pointer to the foreign structure, now<br />
we have to explicitally create the Graphic Context, and free it after<br />
having used it, with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AcreateGC createGC], <br />
the interface to <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AcreateGC XCreateGC], <br />
and<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AfreeGC freeGC], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/GC/XFreeGC.html XFreeGC].<br />
<br />
Be careful: if you create a graphic con without freeing it after use,<br />
you are going to end up with a noticeable memory leak!<br />
<br />
The specific graphic functions we are going to need for drawing a<br />
rectangle into our window are:<br />
<br />
# [http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AsetForeground setForegrond] the interface to [http://www.tronche.com/gui/x/xlib/GC/convenience-functions/XSetForeground.html XSetForegroung]<br />
# [http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AsetBackground setBackground] the interface to [http://www.tronche.com/gui/x/xlib/GC/convenience-functions/XSetBackground.html XSetBackground]<br />
# [http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AfillRectangle fillRectangle] the interface to [http://www.tronche.com/gui/x/xlib/graphics/filling-areas/XFillRectangle.html XFillRectangle]<br />
<br />
The first two functions are needed to set the parameters in the<br />
[http://www.tronche.com/gui/x/xlib/GC/manipulating.html Graphic Context]. <br />
The third one will use this GC for filling a rectangle on the<br />
specified window. Just have a look to their type signatures:<br />
<br />
<haskell><br />
<br />
setForeground :: Display -> GC -> Pixel -> IO ()<br />
setBackground :: Display -> GC -> Pixel -> IO ()<br />
fillRectangle :: Display -> Drawable -> GC -> Position -> Position -> Dimension -> Dimension -> IO ()<br />
<br />
</haskell><br />
<br />
Ok, this is the function that we will be using for drawing into a<br />
window:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> IO ()<br />
drawInWin dpy win = do<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 0 0 100 100<br />
freeGC dpy gc<br />
<br />
</haskell><br />
<br />
This will just fill our window with a rectangle at (0, 0) (x, y)<br />
coordinates (relatives to the window's internal border), with the same<br />
dimensions of our window.<br />
<br />
Obviously we can play a bit with rectangles. This version, for<br />
instance, will draw 2 rectangles to simulate a blu rectangle with a<br />
green border, two pixels width:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> IO ()<br />
drawInWin dpy win = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 100 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 96 96<br />
freeGC dpy gc<br />
<br />
</haskell><br />
<br />
You can use this function on a mapped window. This is our original<br />
example rewritten with this new approach:<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
drawInWin dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
drawInWin :: Display -> Window -> IO ()<br />
drawInWin dpy win = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 100 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 96 96<br />
freeGC dpy gc<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr)<br />
inputOutput visual cWOverrideRedirect attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
<br />
</haskell><br />
<br />
As you see, now mkUnmanagedWindow sets a null border width and does<br />
not set any background color. Everything is easily done with<br />
rectangles.<br />
<br />
===Printing a String===<br />
<br />
Printing a string to a window requires operating with another foreign<br />
C structure, the <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/ XFontStruct], <br />
which contains all of the information regarding the metrics of font<br />
that the X server will use to display our string.<br />
<br />
This structure will be used to perform some computations that are<br />
required for the correct placement of the text in the window.<br />
<br />
As we have seen with the window's attributes and the Graphic Context,<br />
we need a function that returns a pointer to this foreign structure,<br />
pointer that must be freed after using it.<br />
<br />
A pointer to the XFontStruct is returned by <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AloadQueryFont loadQueryFont], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XLoadQueryFont.html XLoadQueryFont()].<br />
<br />
XLoadQueryFont, which requires the X connection and the font name,<br />
will perform two distinct operations: load the needed font and return<br />
its id<br />
(performed by [http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XLoadFont.html XLoadFont()] <br />
which doesn't have a Haskell interface) and query the font to retrieve the XFontStruct <br />
(performed by [http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XQueryFont.html XQueryFont] <br />
which does have a Haskell interface:<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AqueryFont queryFont]).<br />
<br />
The XFontStruct is needed by <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AtextExtents textExtents], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XTextExtents.html XTexteExtent()], <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AtextWidth textWidth], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XTextWidth.html XTextWidth()].<br />
<br />
These are their type signatures.<br />
<br />
<haskell><br />
<br />
textExtents :: FontStruct -> String -> (FontDirection, Int32, Int32, CharStruct)<br />
textWidth :: FontStruct -> String -> Int32<br />
<br />
</haskell><br />
<br />
Given the FontStruct and the string to be printed, these functions<br />
will provide some valuable information. The values returned by the<br />
first one are related to font direction and vertical placement, while<br />
the second one will return the total width of the string to be printed<br />
with that specific font.<br />
<br />
This information can be used with the graphic function that will<br />
actually draw the text on the window: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AdrawImageString drawImageString], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/drawing-text/XDrawImageString.html XDrawImageString].<br />
<br />
There are other version of this string, <br />
[http://www.tronche.com/gui/x/xlib/graphics/drawing-text/XDrawText.html XDrawText()] <br />
and <br />
[http://www.tronche.com/gui/x/xlib/graphics/drawing-text/XDrawText16.html XDrawText16()] <br />
for 16-bit characters.<br />
<br />
We are going to use the first one because it will also use the<br />
foreground pixel set in the Graphic Context.<br />
<br />
It's type signature is:<br />
<br />
<haskell><br />
<br />
drawImageString :: Display -> Drawable -> GC -> Position -> Position -> String -> IO ()<br />
<br />
</haskell><br />
<br />
Finally we must remember to free the FontStruct with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AfreeFont freeFont] <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XFreeFont.html XFreeFont].<br />
<br />
We can now write our function to print a string on a window, but first<br />
we need to make some small modifications to our darwInWin function,<br />
that will now take a string and will load and free the needed<br />
FontStruct to be passed to the new printString function:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> String -> IO ()<br />
drawInWin dpy win str = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
fontStruc <- loadQueryFont dpy "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 200 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 196 96<br />
printString dpy win gc fontStruc str<br />
freeGC dpy gc<br />
freeFont dpy fontStruc<br />
<br />
</haskell><br />
<br />
Here we are loading the "misc-fixed" font. You can select different<br />
fonts with the standalone utility:<br />
xfontsel<br />
<br />
As you see the FontStruct retrieved by loadQueryFont is used by<br />
printString and then freed.<br />
<br />
So, let's look printString:<br />
<br />
<br />
<haskell><br />
<br />
printString :: Display <br />
-> Drawable <br />
-> GC<br />
-> FontStruct<br />
-> String<br />
-> IO ()<br />
printString dpy d gc fontst str =<br />
do let strLen = textWidth fontst str<br />
(_,asc,_,_) = textExtents fontst str<br />
valign = (100 + fromIntegral asc) `div` 2<br />
remWidth = 200 - strLen<br />
offset = remWidth `div` 2<br />
fgcolor <- initColor dpy "white"<br />
bgcolor <- initColor dpy "blue"<br />
setForeground dpy gc fgcolor<br />
setBackground dpy gc bgcolor<br />
drawImageString dpy d gc offset valign str<br />
<br />
</haskell><br />
<br />
In the "let" part we use textWidth and textExtents to set vertical and<br />
horizontal alignment: this is done by calculating the x and y<br />
coordinates for drawImageString. In this example text will be<br />
vertically and horizontally centered (take into account that I have<br />
also enlarged the window, whose width now is 200 pixels).<br />
<br />
For a reference of the meaning of font ascent and descent, and the<br />
origins of the rectangle drawn by drawImageString read the par. 8.6<br />
([http://www.tronche.com/gui/x/xlib/graphics/drawing-text/ Drawing Text]) <br />
of the <br />
[http://www.tronche.com/gui/x/xlib/ The Xlib Manual].<br />
<br />
You may notice that printString takes a <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3ADrawable Drawable], <br />
which can be either a window or a pixmap (see below).<br />
<br />
We can now rewrite our example and finally see some text printed in<br />
our window:<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import System.Time<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 200 100<br />
setTextProperty dpy win "The Clock" wM_NAME <br />
mapWindow dpy win<br />
drawInWin dpy win =<< date<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
date :: IO String<br />
date = do <br />
t <- getClockTime<br />
return $ calendarTimeToString . toUTCTime $ t <br />
<br />
drawInWin :: Display -> Window -> String -> IO ()<br />
drawInWin dpy win str = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
fontStruc <- loadQueryFont dpy "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 200 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 196 96<br />
printString dpy win gc fontStruc str<br />
freeGC dpy gc<br />
freeFont dpy fontStruc<br />
<br />
printString :: Display <br />
-> Drawable <br />
-> GC<br />
-> FontStruct<br />
-> String<br />
-> IO ()<br />
printString dpy d gc fontst str =<br />
do let strLen = textWidth fontst str<br />
(_,asc,_,_) = textExtents fontst str<br />
valign = (100 + fromIntegral asc) `div` 2<br />
remWidth = 200 - strLen<br />
offset = remWidth `div` 2<br />
fgcolor <- initColor dpy "white"<br />
bgcolor <- initColor dpy "blue"<br />
setForeground dpy gc fgcolor<br />
setBackground dpy gc bgcolor<br />
drawImageString dpy d gc offset valign str<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr)<br />
inputOutput visual cWOverrideRedirect attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(apros,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel apros<br />
<br />
</haskell><br />
<br />
Since we are going to display the system time, I already added a<br />
"date" function and increased to width of our window. <br />
<br />
Just give it a try. We are almost there. It's a clock, after all.<br />
<br />
==Updating a Window==<br />
<br />
If you do not believe that now we have a system clock, just change the<br />
main function of the above example with the following two functions and<br />
try yourself:<br />
<br />
<haskell><br />
<br />
main :: IO ()<br />
main = do <br />
dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 200 100<br />
setTextProperty dpy win "The Clock" wM_NAME <br />
mapWindow dpy win<br />
updateWin dpy win<br />
<br />
updateWin :: Display -> Window -> IO ()<br />
updateWin dpy win = do<br />
drawInWin dpy win =<< date<br />
sync dpy False<br />
threadDelay (1 * 1000000)<br />
updateWin dpy win<br />
<br />
</haskell><br />
<br />
This piece of code just adds the eternal recursive loop and, within<br />
this loop, reduces the thread block to 1 second. That's it.<br />
<br />
Every second our window will be updated - redrawn.<br />
<br />
Now, if you let the clock run for a while, you will notice that<br />
sometimes, during an update, the window sort of flickers.<br />
<br />
This is due to the fact that we are drawing directly over the window.<br />
We need to adopt a better technique: we need to write to a pixmap<br />
first, and then copy the pixmap over the window.<br />
<br />
Citing from<br />
[http://en.wikipedia.org/wiki/X_Window_System_core_protocol#Pixmaps_and_drawables Wikipedia]:<br />
<br />
<blockquote> <br />
«A pixmap is a region of memory that can be used for<br />
drawing. Contrary to windows, the contents of pixmaps are not<br />
automatically shown on the screen. However, the content of a pixmap<br />
(or a part of it) can be transferred to a window and vice versa. This<br />
allows for techniques such as <br />
[http://en.wikipedia.org/wiki/Double_buffering double buffering]. <br />
Most of the graphical<br />
operations that can be done on windows can also be done on pixmaps.<br />
Windows and pixmaps are collectively named drawables, and their<br />
content data resides on the server.» <br />
</blockquote><br />
<br />
This is not very difficult, and requires a very small change of our<br />
drawInWin function.<br />
<br />
In order to create the pixmap we will use <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AcreatePixmap createPixmap], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/pixmap-and-cursor/XCreatePixmap.htm XCreatePixmap()], <br />
which takes the display connection, the drawable upon which the pixmap<br />
is created, the width, the height, and the depth of the screen.<br />
<br />
We will then use <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AcopyArea copyArea], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/XCopyArea.html XCopyArea], <br />
to copy the pixmap over the window.<br />
<br />
This is the copyArea type signature:<br />
<br />
<haskell><br />
<br />
copyArea :: Display <br />
-> Drawable -> Drawable <br />
-> GC <br />
-> Position -> Position <br />
-> Dimension -> Dimension <br />
-> Position -> Position <br />
-> IO ()<br />
<br />
</haskell><br />
<br />
that is to say:<br />
# the display connection<br />
# the origin and the destination drawables (our pixmap and our window respectively)<br />
# the x and y coordinates relative to the origin drawable upper left corner<br />
# the width and the height of the area the be copied<br />
# the x and y coordinates relative to the upper left corner of the destination drawable where the copied area must be placed<br />
<br />
And we will eventually free the pixmap with<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AfreePixmap freePixmap], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/pixmap-and-cursor/XFreePixmap.html XFreePixmap].<br />
<br />
Since our printString function may accept either a window or a pixmap,<br />
they both are drawables, all we need to do is to change drawInWin<br />
accordingly:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> String ->IO ()<br />
drawInWin dpy win str = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
fontStruc <- loadQueryFont dpy "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"<br />
p <- createPixmap dpy win 200 100 (defaultDepthOfScreen (defaultScreenOfDisplay dpy))<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy p gc 0 0 200 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy p gc 2 2 196 96<br />
printString dpy p gc fontStruc str<br />
copyArea dpy p win gc 0 0 200 100 0 0<br />
freeGC dpy gc<br />
freeFont dpy fontStruc<br />
freePixmap dpy p<br />
<br />
</haskell><br />
<br />
Now, all graphic functions take now "p" and not "win". After copyArea<br />
everything is freed.<br />
<br />
Our clock:<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import System.Time<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main = do <br />
dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 200 100<br />
setTextProperty dpy win "Hello World - The Clock" wM_NAME <br />
mapWindow dpy win<br />
updateWin dpy win<br />
<br />
updateWin :: Display -> Window -> IO ()<br />
updateWin dpy win = do<br />
drawInWin dpy win =<< date<br />
sync dpy False<br />
threadDelay (1 * 1000000)<br />
updateWin dpy win<br />
<br />
date :: IO String<br />
date = do <br />
t <- getClockTime<br />
return $ calendarTimeToString . toUTCTime $ t <br />
<br />
drawInWin :: Display -> Window -> String ->IO ()<br />
drawInWin dpy win str = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
fontStruc <- loadQueryFont dpy "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"<br />
p <- createPixmap dpy win 200 100 (defaultDepthOfScreen (defaultScreenOfDisplay dpy))<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy p gc 0 0 200 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy p gc 2 2 196 96<br />
printString dpy p gc fontStruc str<br />
copyArea dpy p win gc 0 0 200 100 0 0<br />
freeGC dpy gc<br />
freeFont dpy fontStruc<br />
freePixmap dpy p<br />
<br />
<br />
printString :: Display <br />
-> Drawable <br />
-> GC<br />
-> FontStruct<br />
-> String<br />
-> IO ()<br />
printString dpy d gc fontst str =<br />
do let strLen = textWidth fontst str<br />
(_,asc,_,_) = textExtents fontst str<br />
valign = (100 + fromIntegral asc) `div` 2<br />
remWidth = 200 - strLen<br />
offset = remWidth `div` 2<br />
fgcolor <- initColor dpy "white"<br />
bgcolor <- initColor dpy "blue"<br />
setForeground dpy gc fgcolor<br />
setBackground dpy gc bgcolor<br />
drawImageString dpy d gc offset valign str<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr)<br />
inputOutput visual cWOverrideRedirect attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(apros,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel apros<br />
<br />
</haskell><br />
<br />
<br />
==Dealing with XEvents==<br />
<br />
Now try this.<br />
<br />
In updateWin set threadDelay to something like:<br />
<br />
<haskell><br />
<br />
threadDelay (60 * 1000000)<br />
<br />
</haskell><br />
<br />
Run the clock, switch to a console (with Alt+Ctrl+F1) and come back to<br />
the X server where the clock is running.<br />
<br />
'''To be continued ...'''<br />
<br />
Two approaches<br />
<br />
- [[User:AndreaRossato|Andrea Rossato]]<br />
<br />
[[Category:Tutorials]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=X_window_programming_in_Haskell&diff=14554X window programming in Haskell2007-07-20T12:57:32Z<p>AndreaRossato: printing strings</p>
<hr />
<div>==Writing an X Application With Haskell Xlib Bindings==<br />
<br />
This tutorial is a side product of the research and the learning<br />
experience of writing a<br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmobar status bar] <br />
for the [http://xmonad.org XMonad Window Manager], the<br />
first WM written in Haskell.<br />
<br />
It will show you how to write a simple X application using the low<br />
level Xlib library. The goal is to write a simple text base clock,<br />
that will display the system time, to be run on top of every other<br />
applications, like a status bar.<br />
<br />
While the application is fairly simple, still it will require us to<br />
get to know quite a lot of the details that must be taken into account<br />
when writing a properly working X application.<br />
<br />
Obviously some understanding of X and Xlib is required.<br />
<br />
These are some links that can be used as reference:<br />
<br />
* [http://www.tronche.com/gui/x/xlib/ The Xlib Manual]: this is the reference manual, and you should look up here every function that we are going to use in this tutorial.<br />
* [http://en.wikipedia.org/wiki/Xlib Xlib (Wikipedia)]<br />
* [http://en.wikipedia.org/wiki/X_Window_System_core_protocol X Window System core protocol (Wikipedia)]<br />
* [http://www.sbin.org/doc/Xlib/ Xlib Programming Manual]: specifically the [http://www.sbin.org/doc/Xlib/chapt_02.html Chapter 2 X Concepts]<br />
<br />
This tutorial is dedicated to the intermediate Haskell coder. While I<br />
will try to write the simplest code I can (probably it will even look<br />
the dumbest, but that's me), I'm not going into much details about the<br />
Haskell part.<br />
<br />
What are we going to learn:<br />
* how to create a window and set, or change, its attributes;<br />
* how to draw in that window, specifically some text, with some properties, like fonts or colors;<br />
* how to properly update the window;<br />
* how to handle events, like a mouse button press.<br />
<br />
In order to compile the following code examples you need at least:<br />
<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11 X11], the Haskell binding to the X11 graphics library.<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras]: will be required in some examples. This library provides missing bindings to the X11 graphics library and is actively developed by Spencer Janssen at the time of this writing. Some functions needed in this tutorial can be found only in the darcs repository of X11-extras: [http://darcs.haskell.org/~sjanssen/X11-extras http://darcs.haskell.org/~sjanssen/X11-extras]. Read carefully the README before installing.<br />
<br />
<br />
<br />
==Hello World==<br />
<br />
Let's start from the usual simple "Hello World"<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
border = blackPixel dpy dflt<br />
background = whitePixel dpy dflt<br />
rootw <- rootWindow dpy dflt<br />
win <- createSimpleWindow dpy rootw 0 0 100 100 1 border background<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
</haskell><br />
<br />
The first function, <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AopenDisplay openDisplay],<br />
is the interface to the Xlib function <br />
[http://www.tronche.com/gui/x/xlib/display/opening.html XOpenDisplay()], <br />
and opens a connection to the X sever that controls a display. The connection is returned and bound to dpy. By applying <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AdefaultScreen defaultScreen], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#DefaultScreen XDefaultScreen], <br />
we get the default screen, that is required in many of the following functions.<br />
With<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3ArootWindow rootWindow], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#RootWindow XRootWindow()], <br />
we get the root window. We need it in order to set the parent window in the most important function of the above code: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AcreateSimpleWindow createSimpleWindow], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html XCreateSimpleWindow].<br />
<br />
This function takes, as arguments: the display, the parent window of<br />
the window to be created, the x position, the y position, the width,<br />
the height, the border width, the border pixel and the background<br />
pixel.<br />
<br />
The x and y positions are relative to the upper left corner of the<br />
parent window's inside borders.<br />
<br />
In order to retrieve the values of the black and white pixels for that<br />
specific screen, we use two specific functions:<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AblackPixel blackPixel], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#BlackPixel BlackPixel], <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AwhitePixel whitePixel], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#WhitePixel WhitePixel]<br />
<br />
The function createSimpleWindow will return the window ID and, with<br />
this ID, we can start manipulating our newly created window, as we do,<br />
in the above code, with the function<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AsetTextProperty setTextProperty], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/ICC/client-to-window-manager/XSetTextProperty.html XSetTextProperty()].<br />
<br />
This function is needed, in our code, to set the window's name, that<br />
your window manager will display on some decoration attached to the<br />
window (other window managers will not display anything, for instance<br />
a tiling WM like [http://xmonad.org XMonad])<br />
<br />
To set the window's name we need to manipulate the <br />
[http://www.tronche.com/gui/x/xlib/ICC/client-to-window-manager/converting-string-lists.html XTextProperty structure]. <br />
<br />
Properties, such as the XTextProperty, have a string name and a<br />
numerical identifier called an atom. An atom is an ID that uniquely<br />
identifies a particular property. Property name strings are typically<br />
all upper case - with the first letter in low case when translated<br />
into Haskell - with words separated by underscores. In our example we<br />
had to set the WM_NAME property to "Hello World".<br />
<br />
Creating and manipulating a window is just the first step to have a<br />
new window displayed. In order for the window to become visible we<br />
must map it with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AmapWindow mapWindow], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/window/XMapWindow.html XMapWindow()]. <br />
This will make the window visible.<br />
<br />
Xlib will not send requests and calls to the Xserver immediately, but<br />
will buffer them and send the full buffer when some given conditions<br />
are met.<br />
<br />
One way to force the flushing of the output buffer is to call <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Event.html#v%3Async sync], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/event-handling/XSync.html XSync()], <br />
which takes 2 arguments: the connection (dpy) and a Boolean value that<br />
indicates whether XSync() must discard all events on the event queue.<br />
<br />
After that the Xserver will eventually display our window. <br />
<br />
The rest of the above example does nothing else but blocking the<br />
program execution for 10 seconds (to let you stare at your newly<br />
created window) and then will exit.<br />
<br />
==Window's Attributes==<br />
<br />
Even though in our "Hello World" example we set the window's<br />
dimension, we have no assurance that the Window Manager will respect<br />
our decision.<br />
<br />
[http://xmonad.org XMonad], for instance, will just create a window<br />
with the dimensions needed to fill its tiled screen, no matter what<br />
you set in createSimpleWindow.<br />
<br />
But we decided to write a small clock that will behave as a status<br />
bar, that is to say, we want to create a window that will not be<br />
managed by a Window Manager.<br />
<br />
In order to achieve this result we need to start dealing with window's<br />
attributes.<br />
<br />
There are two ways of dealing with window's attributes: the first is<br />
to set them at window's creation time, but in that case<br />
createSimpleWindow is not powerful enough.<br />
<br />
The second way is to change window's attributes after the window's has<br />
been created. This second approach is not implemented<br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11 X11] but<br />
has been implemented in the darcs version of <br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras].<br />
<br />
===Setting Window's Attribute at Creation Time===<br />
<br />
In order to set window's attributes at creation time, the window must be created with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AcreateWindow createWindow], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html XCreateWindow()].<br />
<br />
The type signature of this function is quite long:<br />
<br />
<haskell><br />
<br />
createWindow :: Display -> Window <br />
-> Position -> Position <br />
-> Dimension -> Dimension <br />
-> CInt <br />
-> CInt <br />
-> WindowClass <br />
-> Visual <br />
-> AttributeMask <br />
-> Ptr SetWindowAttributes <br />
-> IO Window<br />
<br />
</haskell><br />
<br />
That is to say:<br />
* the connection and the parent window<br />
* the x and y position (origins in the upper left corner of the inside border of the parent window)<br />
* width and height<br />
* border width<br />
* depth of screen<br />
* the window's class<br />
* the visual<br />
* the attribute mask<br />
* and the pointer to the [http://www.tronche.com/gui/x/xlib/window/attributes/#XSetWindowAttributes XSetWindowAttributes] foreign C structure.<br />
<br />
This last one gives you an idea of the type of operation we must do in<br />
order to create a window (createSimpleWindow is just a wrapper around<br />
this more complicated createWindow, with some default arguments): we<br />
need a function to allocate some memory for the creation of the<br />
foreign C structure, and then manipulate this foreign structure from<br />
within this function.<br />
<br />
The needed function is <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AallocaSetWindowAttributes allocaSetWindowAttributes], <br />
whose type indeed is:<br />
<br />
<haskell><br />
<br />
allocaSetWindowAttributes :: (Ptr SetWindowAttributes -> IO a) -> IO a<br />
<br />
</haskell><br />
<br />
allocaSetWindowAttributes will take a function which takes the pointer<br />
to the foreign structure as its argument. This function will perform<br />
an IO action that is the action returned by allocaSetWindowAttributes.<br />
<br />
In our case allocaSetWindowAttributes will take a function that will<br />
use createWindow to return the new window.<br />
<br />
So, we will need to use createWindow inside allocaSetWindowAttributes.<br />
We will soon see how. But first let's analyze the other arguments of<br />
createWindow.<br />
<br />
The display, the parent window, the coordinates and dimensions are the<br />
same as with createSimpleWindow. But now we must specify the depth of<br />
the screen, the window's class, the visual and the attribute mask. We<br />
also need to manipulate the XSetWindowAttribute after its creation by<br />
allocaSetWindowAttributes, before calling createWindow.<br />
<br />
«The depth is the number of bits available for each pixel to represent color (or gray scales). The visual represents the way pixel values are translated to produce color or monochrome output on the monitor.»( see [http://www.sbin.org/doc/Xlib/chapt_02.html http://www.sbin.org/doc/Xlib/chapt_02.html])<br />
<br />
For the depth we are going to use <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Screen.html#v%3AdefaultDepthOfScreen defaultDepthOfScreen], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/image-format-macros.html#DefaultDepthOfScreen XDefaultDepthOfScreen()], <br />
to retrieve the default screen depth.<br />
<br />
For the visual we are going to use<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Screen.html#v%3AdefaultVisualOfScreen defaultVisualOfScreen], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/image-format-macros.html#DefaultVisualOfScreen DefaultVisualOfScreen].<br />
<br />
The <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3AWindowClass WindowClass] <br />
can either be copyFromParent, inputOutput, or inputOnly. In the first case the<br />
class is copied from the class of the parent window. An inputOnly<br />
window can only be used for receiving input events. In our code we are<br />
going to use inputOutput windows, windows that can receive input events<br />
and that can also be used to display some output.<br />
<br />
The attributeMask «specifies which window attributes are defined in<br />
the attributes argument. This mask is the bitwise inclusive OR of the<br />
valid attribute mask bits. If value mask is zero, the attributes are<br />
ignored and are not referenced.»<br />
(see [http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html]).<br />
<br />
In other words, in order to set more then one attribute, you need to pass a value mask such as:<br />
<br />
<haskell><br />
<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel .|. etc ...<br />
<br />
</haskell><br />
<br />
and set each of this attributes within allocaSetWindowAttributes with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#12 specific attributes setting functions].<br />
<br />
Among these functions the one we need: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_override_redirect set_override_redirect], <br />
whose type is:<br />
<br />
<haskell><br />
<br />
set_override_redirect :: Ptr SetWindowAttributes -> Bool -> IO ()<br />
<br />
</haskell><br />
<br />
This function takes the pointer to the XSetWindowAttributes structure and the flag to be set (True or False).<br />
<br />
For the list of avaliable attributes see<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3AAttributeMask the AttributeMask type defnition].<br />
<br />
For their meaning see <br />
[http://www.tronche.com/gui/x/xlib/window/attributes/#XSetWindowAttributes the XSetWindowAttributes structure reference].<br />
<br />
Now, our goal was to create a window that the Window Manager is going<br />
to ignore, and in order to do that all we need to set the attribute<br />
[http://www.tronche.com/gui/x/xlib/window/attributes/override-redirect.html CWOverrideRedirect] <br />
to True. And now we know how to do it.<br />
<br />
Ok, it's time to introduce our function to create new windows with the CWOverrideRedirect set to True<br />
<br />
<haskell><br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr) <br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
</haskell><br />
<br />
Like simpleCreateWindow, our function is a wrapper around<br />
createWindow, but this time we are manually setting the<br />
CWOverrideRedirect flag.<br />
<br />
As you see our function, unlike createSimpleWindow, does not have,<br />
among its arguments, the background and the border pixels. This colors<br />
can be set, for windows created with createWindow, using the attribute<br />
mask, and setting CWBackPixel and CWBorderPixel with the needed<br />
functions: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_background_pixel set_background_pixel] <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_border_pixel set_border_pixel].<br />
<br />
By the way, setting the border color with this version of<br />
mkUnmanagedWindow is actually useless since the border width is set to<br />
zero. In the next example we will set it to 1.<br />
<br />
Our function needs also the screen now, since we have to retrieve the<br />
default depth and visual.<br />
<br />
We can now rewrite our initial code using the new function now.<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
set_background_pixel attributes $ whitePixel dpy (defaultScreen dpy)<br />
set_border_pixel attributes $ blackPixel dpy (defaultScreen dpy)<br />
createWindow dpy rw x y w h 1 (defaultDepthOfScreen scr)<br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
</haskell><br />
<br />
Ok, let's give it a try. Did you see? Now the window will be placed in<br />
the specified x and y position, with the given dimensions. No Window<br />
Manager decorations, and so, no name displayed.<br />
<br />
===Changing an Existing Window's Attributes===<br />
<br />
This task requires <br />
[http://www.tronche.com/gui/x/xlib/window/XChangeWindowAttributes.html XChangeWindowAttrbutes()], <br />
implemented only in the darcs version of <br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras].<br />
<br />
In order to change a window's attributes we just need the window ID in<br />
that specific X server, after that we need to unmap the window first,<br />
and then change its attributes with changeWindowAttributes, the<br />
interface to XChangeWindowAttrbutes() implemented by<br />
[http://darcs.haskell.org/~sjanssen/X11-extras the darcs version of X11-extras].<br />
<br />
Here's the code:<br />
<br />
<haskell><br />
<br />
module Main where<br />
<br />
import Graphics.X11.Xlib<br />
import Graphics.X11.Xlib.Extras<br />
import System.Environment<br />
<br />
usage :: String -> String<br />
usage n = "Usage: " ++ n ++ " manage/unmanage windowID"<br />
<br />
main :: IO ()<br />
main = do<br />
args <- getArgs<br />
pn <- getProgName<br />
let (win,ac) = case args of<br />
[] -> error $ usage pn<br />
w -> case (w !!0) of <br />
"manage" -> (window, False)<br />
"unmanage" -> (window, True)<br />
_ -> error $ usage pn<br />
where window = case (w !! 1) of <br />
[] -> error $ usage pn<br />
w -> read w :: Window<br />
dpy <- openDisplay ""<br />
unmapWindow dpy win<br />
sync dpy False<br />
allocaSetWindowAttributes $<br />
\attributes -> do<br />
set_override_redirect attributes ac<br />
changeWindowAttributes dpy win cWOverrideRedirect attributes<br />
mapWindow dpy win<br />
sync dpy False<br />
<br />
</haskell><br />
<br />
Save it as Unmanage.hs and compile with:<br />
ghc --make Unmanage.hs -o unmanage<br />
<br />
To use it you need to retrieve the window ID with the stand alone utility<br />
xwininfo<br />
<br />
Then you run the above code with:<br />
unmanage unmanage/manage windowID<br />
<br />
to set override_redirect to True or False.<br />
<br />
Obviously the important part of the code is this:<br />
<haskell><br />
dpy <- openDisplay ""<br />
unmapWindow dpy win<br />
sync dpy False<br />
allocaSetWindowAttributes $<br />
\attributes -> do<br />
set_override_redirect attributes ac<br />
changeWindowAttributes dpy win cWOverrideRedirect attributes<br />
mapWindow dpy win<br />
sync dpy False<br />
</haskell><br />
<br />
where we:<br />
# connect to the X server<br />
# unmap the window<br />
# flush the output buffer to have the X server actually unmap the window<br />
# change the attributes with the same procedure we used to set them when creating the window<br />
# map the window again<br />
# flush the output buffer to see the change take effect.<br />
<br />
You can modify this program to change other window's attributes.<br />
<br />
==Colors and Color Depth==<br />
<br />
So far we have set the window background color as a window attribute.<br />
This is not the most convenient way to set the window background<br />
color: if we need to change it, we must change the window's attribute,<br />
and we have seen that this task requires unmapping the window,<br />
flushing the output with changeWindowAttributes within<br />
changeWindowAttributes, remapping the window and reflushing the output<br />
buffer. Moreover we can do that only we the darcs version of<br />
X11-extras...<br />
<br />
In the following sections we are going to adopt a more efficient way<br />
of setting the window's background color: we will start drawing into<br />
the window. But first we must familiarize with colors and the way the<br />
X server deals with them.<br />
<br />
So far we have set the colors by using some functions to retrieve<br />
their pixel values: blackPixel and whitePixel. These functions take<br />
the display and the default screen and return respectively the pixel<br />
values for the black and the white colors in that screen.<br />
<br />
A color is represented by a 32-bit unsigned integer, called a pixel<br />
value. The elements affecting the pixel representation of a color are:<br />
1. the color depth; 2. the colormap, which is a table containing red,<br />
green, and blue intensity values; 3. the visual type.<br />
<br />
All these elements are specific to a given piece of hardware, and so<br />
our X application must detect them in order to set colors<br />
appropriately for that given hardware.<br />
<br />
The approach we are going to use to accomplish this task is this: we<br />
are going to use named colors, or colors represented by<br />
[http://en.wikipedia.org/wiki/RGB RGB triple], such as "red",<br />
"yellow", or "#FFFFFF", etc; and we are going to translate these<br />
colors into the pixel values appropriate for the screen we are<br />
operating on.<br />
<br />
In order to achieve our goal we are going to use the function <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Color.html#v%3AallocNamedColor allocNamedColor] <br />
which is the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/color/XAllocNamedColor.html XAllocNamedColor()].<br />
<br />
The type signature of allocNamedColor is:<br />
<br />
<haskell><br />
<br />
allocNamedColor :: Display -> Colormap -> String -> IO (Color, Color)<br />
<br />
</haskell><br />
<br />
That is to say, given a display connection, a color map and a string -<br />
our color representation -, this function will return a tuple with the<br />
closest RGB values provided by the hardware and the exact RGB values,<br />
both encoded in a <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib.html#v%3AColor Haskell Color data type]. <br />
We will use the first approximated value.<br />
<br />
The Color data type has a field name we will use to retrieve the<br />
needed pixel value: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib.html#v%3Acolor_pixel color_pixel].<br />
<br />
We can now write this helper function:<br />
<br />
<haskell><br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(approx,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel approx <br />
<br />
</haskell><br />
<br />
To retrieve the colormap of the screen we used <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AdefaultColormap defaultColormap], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#DefaultColormap XDefaultColormap()], <br />
which requires the display and the screen, and returns the colormap of that screen.<br />
<br />
We can now rewrite our example using this new approach.<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel<br />
background_color <- initColor dpy "red"<br />
border_color <- initColor dpy "black" <br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
set_background_pixel attributes background_color<br />
set_border_pixel attributes border_color<br />
createWindow dpy rw x y w h 1 (defaultDepthOfScreen scr)<br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(apros,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel apros<br />
<br />
</haskell><br />
<br />
Just give it a try. Now you can also experiment with different colors.<br />
This approach will assure that our application will work no matter the<br />
color depth of the screen we are working on.<br />
<br />
==Drawing in Windows==<br />
<br />
The X server provides two objects that can be used to draw something<br />
to: windows and pixmaps.<br />
<br />
In this section we will start drawing into windows.<br />
<br />
We have seen that changing the background color of a window is a<br />
troublesome operation, since the window must be unamapped and<br />
remapped, memory for a foreign structure allocated, and so on.<br />
<br />
Instead, we can use some graphic operations to draw a rectangle over<br />
the window. We will latter manipulate the foreground, visible, color of<br />
this rectangle, that will become the new background of our window.<br />
<br />
We can also use multiple rectangles with different dimension to<br />
decorate our window with a border, for instance.<br />
<br />
Later on we will print some text over these rectangles.<br />
<br />
===Drawing Rectangles in a Window===<br />
<br />
Citing from<br />
[http://en.wikipedia.org/wiki/X_Window_System_core_protocol#Graphic_contexts_and_fonts Wikipedia]:<br />
<br />
<blockquote><br />
The client can request a number of graphic operations, such clearing<br />
an area, copying an area into another, drawing points, lines,<br />
rectangles, and text. Beside clearing, all operations are possible on<br />
all drawables, both windows and pixmaps.<br />
<br />
Most requests for graphic operations include a graphic context, which<br />
is a structure that contains the parameters of the graphic operations.<br />
A graphic context includes the foreground color, the background color,<br />
the font of text, and other graphic parameters. When requesting a<br />
graphic operation, the client includes a graphic context.<br />
</blockquote> <br />
<br />
In other words, as for setting window's attribute, we must use a<br />
foreign C structure to set parameters for graphic operations, and then<br />
we will feed this structure to the <br />
[http://www.tronche.com/gui/x/xlib/graphics/ functions] <br />
that will perform these graphic operations.<br />
<br />
We one difference: instead of operating within a function that<br />
allocates memory and creates a pointer to the foreign structure, now<br />
we have to explicitally create the Graphic Context, and free it after<br />
having used it, with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AcreateGC createGC], <br />
the interface to <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AcreateGC XCreateGC], <br />
and<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AfreeGC freeGC], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/GC/XFreeGC.html XFreeGC].<br />
<br />
Be careful: if you create a graphic con without freeing it after use,<br />
you are going to end up with a noticeable memory leak!<br />
<br />
The specific graphic functions we are going to need for drawing a<br />
rectangle into our window are:<br />
<br />
# [http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AsetForeground setForegrond] the interface to [http://www.tronche.com/gui/x/xlib/GC/convenience-functions/XSetForeground.html XSetForegroung]<br />
# [http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AsetBackground setBackground] the interface to [http://www.tronche.com/gui/x/xlib/GC/convenience-functions/XSetBackground.html XSetBackground]<br />
# [http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AfillRectangle fillRectangle] the interface to [http://www.tronche.com/gui/x/xlib/graphics/filling-areas/XFillRectangle.html XFillRectangle]<br />
<br />
The first two functions are needed to set the parameters in the<br />
[http://www.tronche.com/gui/x/xlib/GC/manipulating.html Graphic Context]. <br />
The third one will use this GC for filling a rectangle on the<br />
specified window. Just have a look to their type signatures:<br />
<br />
<haskell><br />
<br />
setForeground :: Display -> GC -> Pixel -> IO ()<br />
setBackground :: Display -> GC -> Pixel -> IO ()<br />
fillRectangle :: Display -> Drawable -> GC -> Position -> Position -> Dimension -> Dimension -> IO ()<br />
<br />
</haskell><br />
<br />
Ok, this is the function that we will be using for drawing into a<br />
window:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> IO ()<br />
drawInWin dpy win = do<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 0 0 100 100<br />
freeGC dpy gc<br />
<br />
</haskell><br />
<br />
This will just fill our window with a rectangle at (0, 0) (x, y)<br />
coordinates (relatives to the window's internal border), with the same<br />
dimensions of our window.<br />
<br />
Obviously we can play a bit with rectangles. This version, for<br />
instance, will draw 2 rectangles to simulate a blu rectangle with a<br />
green border, two pixels width:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> IO ()<br />
drawInWin dpy win = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 100 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 96 96<br />
freeGC dpy gc<br />
<br />
</haskell><br />
<br />
You can use this function on a mapped window. This is our original<br />
example rewritten with this new approach:<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
drawInWin dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
drawInWin :: Display -> Window -> IO ()<br />
drawInWin dpy win = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 100 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 96 96<br />
freeGC dpy gc<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr)<br />
inputOutput visual cWOverrideRedirect attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
<br />
</haskell><br />
<br />
As you see, now mkUnmanagedWindow sets a null border width and does<br />
not set any background color. Everything is easily done with<br />
rectangles.<br />
<br />
===Printing a String===<br />
<br />
Printing a string to a window requires operating with another foreign<br />
C structure, the <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/ XFontStruct], <br />
which contains all of the information regarding the metrics of font<br />
that the X server will use to display our string.<br />
<br />
This structure will be used to perform some computations that are<br />
required for the correct placement of the text in the window.<br />
<br />
As we have seen with the window's attributes and the Graphic Context,<br />
we need a function that returns a pointer to this foreign structure,<br />
pointer that must be freed after using it.<br />
<br />
A pointer to the XFontStruct is returned by <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AloadQueryFont loadQueryFont], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XLoadQueryFont.html XLoadQueryFont()].<br />
<br />
XLoadQueryFont, which requires the X connection and the font name,<br />
will perform two distinct operations: load the needed font and return<br />
its id<br />
(performed by [http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XLoadFont.html XLoadFont()] <br />
which doesn't have a Haskell interface) and query the font to retrieve the XFontStruct <br />
(performed by [http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XQueryFont.html XQueryFont] <br />
which does have a Haskell interface:<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AqueryFont queryFont]).<br />
<br />
The XFontStruct is needed by <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AtextExtents textExtents], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XTextExtents.html XTexteExtent()], <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AtextWidth textWidth], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XTextWidth.html XTextWidth()].<br />
<br />
These are their type signatures.<br />
<br />
<haskell><br />
<br />
textExtents :: FontStruct -> String -> (FontDirection, Int32, Int32, CharStruct)<br />
textWidth :: FontStruct -> String -> Int32<br />
<br />
</haskell><br />
<br />
Given the FontStruct and the string to be printed, these functions<br />
will provide some valuable information. The values returned by the<br />
first one are related to font direction and vertical placement, while<br />
the second one will return the total width of the string to be printed<br />
with that specific font.<br />
<br />
This information can be used with the graphic function that will<br />
actually draw the text on the window: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AdrawImageString drawImageString], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/drawing-text/XDrawImageString.html XDrawImageString].<br />
<br />
There are other version of this string, <br />
[http://www.tronche.com/gui/x/xlib/graphics/drawing-text/XDrawText.html XDrawText()] <br />
and <br />
[http://www.tronche.com/gui/x/xlib/graphics/drawing-text/XDrawText16.html XDrawText16()] <br />
for 16-bit characters.<br />
<br />
We are going to use the first one because it will also use the<br />
foreground pixel set in the Graphic Context.<br />
<br />
It's type signature is:<br />
<br />
<haskell><br />
<br />
drawImageString :: Display -> Drawable -> GC -> Position -> Position -> String -> IO ()<br />
<br />
</haskell><br />
<br />
Finally we must remember to free the FontStruct with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Font.html#v%3AfreeFont freeFont] <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/graphics/font-metrics/XFreeFont.html XFreeFont].<br />
<br />
We can now write our function to print a string on a window, but first<br />
we need to make some small modifications to our darwInWin function,<br />
that will now take a string and will load and free the needed<br />
FontStruct to be passed to the new printString function:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> String -> IO ()<br />
drawInWin dpy win str = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
fontStruc <- loadQueryFont dpy "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 200 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 196 96<br />
printString dpy win gc fontStruc str<br />
freeGC dpy gc<br />
freeFont dpy fontStruc<br />
<br />
</haskell><br />
<br />
Here we are loading the "misc-fixed" font. You can select different<br />
fonts with the standalone utility:<br />
xfontsel<br />
<br />
As you see the FontStruct retrieved by loadQueryFont is used by<br />
printString and then freed.<br />
<br />
So, let's look printString:<br />
<br />
<br />
<haskell><br />
<br />
printString :: Display <br />
-> Drawable <br />
-> GC<br />
-> FontStruct<br />
-> String<br />
-> IO ()<br />
printString dpy d gc fontst str =<br />
do let strLen = textWidth fontst str<br />
(_,asc,_,_) = textExtents fontst str<br />
valign = (100 + fromIntegral asc) `div` 2<br />
remWidth = 200 - strLen<br />
offset = remWidth `div` 2<br />
fgcolor <- initColor dpy "white"<br />
bgcolor <- initColor dpy "blue"<br />
setForeground dpy gc fgcolor<br />
setBackground dpy gc bgcolor<br />
drawImageString dpy d gc offset valign str<br />
<br />
</haskell><br />
<br />
In the "let" part we use textWidth and textExtents to set vertical and<br />
horizontal alignment: this is done by calculating the x and y<br />
coordinates for drawImageString. In this example text will be<br />
vertically and horizontally centered (take into account that I have<br />
also enlarged the window, whose width now is 200 pixels).<br />
<br />
For a reference of the meaning of font ascent and descent, and the<br />
origins of the rectangle drawn by drawImageString read the par. 8.6<br />
([http://www.tronche.com/gui/x/xlib/graphics/drawing-text/ Drawing Text]) <br />
of the <br />
[http://www.tronche.com/gui/x/xlib/ The Xlib Manual].<br />
<br />
You may notice that printString takes a <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3ADrawable Drawable], <br />
which can be either a window or a pixmap (see below).<br />
<br />
We can now rewrite our example and finally see some text printed in<br />
our window:<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import System.Time<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 200 100<br />
setTextProperty dpy win "The Clock" wM_NAME <br />
mapWindow dpy win<br />
drawInWin dpy win =<< date<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
date :: IO String<br />
date = do <br />
t <- getClockTime<br />
return $ calendarTimeToString . toUTCTime $ t <br />
<br />
drawInWin :: Display -> Window -> String -> IO ()<br />
drawInWin dpy win str = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
fontStruc <- loadQueryFont dpy "-misc-fixed-*-*-*-*-10-*-*-*-*-*-*-*"<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 200 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 196 96<br />
printString dpy win gc fontStruc str<br />
freeGC dpy gc<br />
freeFont dpy fontStruc<br />
<br />
printString :: Display <br />
-> Drawable <br />
-> GC<br />
-> FontStruct<br />
-> String<br />
-> IO ()<br />
printString dpy d gc fontst str =<br />
do let strLen = textWidth fontst str<br />
(_,asc,_,_) = textExtents fontst str<br />
valign = (100 + fromIntegral asc) `div` 2<br />
remWidth = 200 - strLen<br />
offset = remWidth `div` 2<br />
fgcolor <- initColor dpy "white"<br />
bgcolor <- initColor dpy "blue"<br />
setForeground dpy gc fgcolor<br />
setBackground dpy gc bgcolor<br />
drawImageString dpy d gc offset valign str<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr)<br />
inputOutput visual cWOverrideRedirect attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(apros,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel apros<br />
<br />
</haskell><br />
<br />
Since we are going to display the system time, I already added a<br />
"date" function and increased to width of our window. <br />
<br />
Just give it a try. We are almost there. It's a clock, after all.<br />
<br />
==Updating a Window==<br />
<br />
'''To be continued ...'''<br />
<br />
<br />
Citing from<br />
[http://en.wikipedia.org/wiki/X_Window_System_core_protocol#Pixmaps_and_drawables Wikipedia]:<br />
<br />
<blockquote> <br />
«A pixmap is a region of memory that can be used for<br />
drawing. Contrary to windows, the contents of pixmaps are not<br />
automatically shown on the screen. However, the content of a pixmap<br />
(or a part of it) can be transferred to a window and vice versa. This<br />
allows for techniques such as double buffering. Most of the graphical<br />
operations that can be done on windows can also be done on pixmaps.<br />
Windows and pixmaps are collectively named drawables, and their<br />
content data resides on the server.» <br />
</blockquote><br />
<br />
<br />
Pixmap and copyArea<br />
<br />
==Dealing with XEvents==<br />
<br />
Two approaches<br />
<br />
- [[User:AndreaRossato|Andrea Rossato]]<br />
<br />
[[Category:Tutorials]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=X_window_programming_in_Haskell&diff=14543X window programming in Haskell2007-07-20T08:52:09Z<p>AndreaRossato: drawing with the Graphic Context</p>
<hr />
<div>==Writing an X Application With Haskell Xlib Bindings==<br />
<br />
This tutorial is a side product of the research and the learning<br />
experience of writing a<br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmobar status bar] <br />
for the [http://xmonad.org XMonad Window Manager], the<br />
first WM written in Haskell.<br />
<br />
It will show you how to write a simple X application using the low<br />
level Xlib library. The goal is to write a simple text base clock,<br />
that will display the system time, to be run on top of every other<br />
applications, like a status bar.<br />
<br />
While the application is fairly simple, still it will require us to<br />
get to know quite a lot of the details that must be taken into account<br />
when writing a properly working X application.<br />
<br />
Obviously some understanding of X and Xlib is required.<br />
<br />
These are some links that can be used as reference:<br />
<br />
* [http://www.tronche.com/gui/x/xlib/ The Xlib Manual]: this is the reference manual, and you should look up here every function that we are going to use in this tutorial.<br />
* [http://en.wikipedia.org/wiki/Xlib Xlib (Wikipedia)]<br />
* [http://en.wikipedia.org/wiki/X_Window_System_core_protocol X Window System core protocol (Wikipedia)]<br />
* [http://www.sbin.org/doc/Xlib/ Xlib Programming Manual]: specifically the [http://www.sbin.org/doc/Xlib/chapt_02.html Chapter 2 X Concepts]<br />
<br />
This tutorial is dedicated to the intermediate Haskell coder. While I<br />
will try to write the simplest code I can (probably it will even look<br />
the dumbest, but that's me), I'm not going into much details about the<br />
Haskell part.<br />
<br />
What are we going to learn:<br />
* how to create a window and set, or change, its attributes;<br />
* how to draw in that window, specifically some text, with some properties, like fonts or colors;<br />
* how to properly update the window;<br />
* how to handle events, like a mouse button press.<br />
<br />
In order to compile the following code examples you need at least:<br />
<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11 X11], the Haskell binding to the X11 graphics library.<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras]: will be required in some examples. This library provides missing bindings to the X11 graphics library and is actively developed by Spencer Janssen at the time of this writing. Some functions needed in this tutorial can be found only in the darcs repository of X11-extras: [http://darcs.haskell.org/~sjanssen/X11-extras http://darcs.haskell.org/~sjanssen/X11-extras]. Read carefully the README before installing.<br />
<br />
<br />
<br />
==Hello World==<br />
<br />
Let's start from the usual simple "Hello World"<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
border = blackPixel dpy dflt<br />
background = whitePixel dpy dflt<br />
rootw <- rootWindow dpy dflt<br />
win <- createSimpleWindow dpy rootw 0 0 100 100 1 border background<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
</haskell><br />
<br />
The first function, <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AopenDisplay openDisplay],<br />
is the interface to the Xlib function <br />
[http://www.tronche.com/gui/x/xlib/display/opening.html XOpenDisplay()], <br />
and opens a connection to the X sever that controls a display. The connection is returned and bound to dpy. By applying <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AdefaultScreen defaultScreen], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#DefaultScreen XDefaultScreen], <br />
we get the default screen, that is required in many of the following functions.<br />
With<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3ArootWindow rootWindow], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#RootWindow XRootWindow()], <br />
we get the root window. We need it in order to set the parent window in the most important function of the above code: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AcreateSimpleWindow createSimpleWindow], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html XCreateSimpleWindow].<br />
<br />
This function takes, as arguments: the display, the parent window of<br />
the window to be created, the x position, the y position, the width,<br />
the height, the border width, the border pixel and the background<br />
pixel.<br />
<br />
The x and y positions are relative to the upper left corner of the<br />
parent window's inside borders.<br />
<br />
In order to retrieve the values of the black and white pixels for that<br />
specific screen, we use two specific functions:<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AblackPixel blackPixel], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#BlackPixel BlackPixel], <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AwhitePixel whitePixel], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#WhitePixel WhitePixel]<br />
<br />
The function createSimpleWindow will return the window ID and, with<br />
this ID, we can start manipulating our newly created window, as we do,<br />
in the above code, with the function<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AsetTextProperty setTextProperty], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/ICC/client-to-window-manager/XSetTextProperty.html XSetTextProperty()].<br />
<br />
This function is needed, in our code, to set the window's name, that<br />
your window manager will display on some decoration attached to the<br />
window (other window managers will not display anything, for instance<br />
a tiling WM like [http://xmonad.org XMonad])<br />
<br />
To set the window's name we need to manipulate the <br />
[http://www.tronche.com/gui/x/xlib/ICC/client-to-window-manager/converting-string-lists.html XTextProperty structure]. <br />
<br />
Properties, such as the XTextProperty, have a string name and a<br />
numerical identifier called an atom. An atom is an ID that uniquely<br />
identifies a particular property. Property name strings are typically<br />
all upper case - with the first letter in low case when translated<br />
into Haskell - with words separated by underscores. In our example we<br />
had to set the WM_NAME property to "Hello World".<br />
<br />
Creating and manipulating a window is just the first step to have a<br />
new window displayed. In order for the window to become visible we<br />
must map it with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AmapWindow mapWindow], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/window/XMapWindow.html XMapWindow()]. <br />
This will make the window visible.<br />
<br />
Xlib will not send requests and calls to the Xserver immediately, but<br />
will buffer them and send the full buffer when some given conditions<br />
are met.<br />
<br />
One way to force the flushing of the output buffer is to call <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Event.html#v%3Async sync], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/event-handling/XSync.html XSync()], <br />
which takes 2 arguments: the connection (dpy) and a Boolean value that<br />
indicates whether XSync() must discard all events on the event queue.<br />
<br />
After that the Xserver will eventually display our window. <br />
<br />
The rest of the above example does nothing else but blocking the<br />
program execution for 10 seconds (to let you stare at your newly<br />
created window) and then will exit.<br />
<br />
==Window's Attributes==<br />
<br />
Even though in our "Hello World" example we set the window's<br />
dimension, we have no assurance that the Window Manager will respect<br />
our decision.<br />
<br />
[http://xmonad.org XMonad], for instance, will just create a window<br />
with the dimensions needed to fill its tiled screen, no matter what<br />
you set in createSimpleWindow.<br />
<br />
But we decided to write a small clock that will behave as a status<br />
bar, that is to say, we want to create a window that will not be<br />
managed by a Window Manager.<br />
<br />
In order to achieve this result we need to start dealing with window's<br />
attributes.<br />
<br />
There are two ways of dealing with window's attributes: the first is<br />
to set them at window's creation time, but in that case<br />
createSimpleWindow is not powerful enough.<br />
<br />
The second way is to change window's attributes after the window's has<br />
been created. This second approach is not implemented<br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11 X11] but<br />
has been implemented in the darcs version of <br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras].<br />
<br />
===Setting Window's Attribute at Creation Time===<br />
<br />
In order to set window's attributes at creation time, the window must be created with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AcreateWindow createWindow], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html XCreateWindow()].<br />
<br />
The type signature of this function is quite long:<br />
<br />
<haskell><br />
<br />
createWindow :: Display -> Window <br />
-> Position -> Position <br />
-> Dimension -> Dimension <br />
-> CInt <br />
-> CInt <br />
-> WindowClass <br />
-> Visual <br />
-> AttributeMask <br />
-> Ptr SetWindowAttributes <br />
-> IO Window<br />
<br />
</haskell><br />
<br />
That is to say:<br />
* the connection and the parent window<br />
* the x and y position (origins in the upper left corner of the inside border of the parent window)<br />
* width and height<br />
* border width<br />
* depth of screen<br />
* the window's class<br />
* the visual<br />
* the attribute mask<br />
* and the pointer to the [http://www.tronche.com/gui/x/xlib/window/attributes/#XSetWindowAttributes XSetWindowAttributes] foreign C structure.<br />
<br />
This last one gives you an idea of the type of operation we must do in<br />
order to create a window (createSimpleWindow is just a wrapper around<br />
this more complicated createWindow, with some default arguments): we<br />
need a function to allocate some memory for the creation of the<br />
foreign C structure, and then manipulate this foreign structure from<br />
within this function.<br />
<br />
The needed function is <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AallocaSetWindowAttributes allocaSetWindowAttributes], <br />
whose type indeed is:<br />
<br />
<haskell><br />
<br />
allocaSetWindowAttributes :: (Ptr SetWindowAttributes -> IO a) -> IO a<br />
<br />
</haskell><br />
<br />
allocaSetWindowAttributes will take a function which takes the pointer<br />
to the foreign structure as its argument. This function will perform<br />
an IO action that is the action returned by allocaSetWindowAttributes.<br />
<br />
In our case allocaSetWindowAttributes will take a function that will<br />
use createWindow to return the new window.<br />
<br />
So, we will need to use createWindow inside allocaSetWindowAttributes.<br />
We will soon see how. But first let's analyze the other arguments of<br />
createWindow.<br />
<br />
The display, the parent window, the coordinates and dimensions are the<br />
same as with createSimpleWindow. But now we must specify the depth of<br />
the screen, the window's class, the visual and the attribute mask. We<br />
also need to manipulate the XSetWindowAttribute after its creation by<br />
allocaSetWindowAttributes, before calling createWindow.<br />
<br />
«The depth is the number of bits available for each pixel to represent color (or gray scales). The visual represents the way pixel values are translated to produce color or monochrome output on the monitor.»( see [http://www.sbin.org/doc/Xlib/chapt_02.html http://www.sbin.org/doc/Xlib/chapt_02.html])<br />
<br />
For the depth we are going to use <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Screen.html#v%3AdefaultDepthOfScreen defaultDepthOfScreen], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/image-format-macros.html#DefaultDepthOfScreen XDefaultDepthOfScreen()], <br />
to retrieve the default screen depth.<br />
<br />
For the visual we are going to use<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Screen.html#v%3AdefaultVisualOfScreen defaultVisualOfScreen], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/image-format-macros.html#DefaultVisualOfScreen DefaultVisualOfScreen].<br />
<br />
The <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3AWindowClass WindowClass] <br />
can either be copyFromParent, inputOutput, or inputOnly. In the first case the<br />
class is copied from the class of the parent window. An inputOnly<br />
window can only be used for receiving input events. In our code we are<br />
going to use inputOutput windows, windows that can receive input events<br />
and that can also be used to display some output.<br />
<br />
The attributeMask «specifies which window attributes are defined in<br />
the attributes argument. This mask is the bitwise inclusive OR of the<br />
valid attribute mask bits. If value mask is zero, the attributes are<br />
ignored and are not referenced.»<br />
(see [http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html]).<br />
<br />
In other words, in order to set more then one attribute, you need to pass a value mask such as:<br />
<br />
<haskell><br />
<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel .|. etc ...<br />
<br />
</haskell><br />
<br />
and set each of this attributes within allocaSetWindowAttributes with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#12 specific attributes setting functions].<br />
<br />
Among these functions the one we need: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_override_redirect set_override_redirect], <br />
whose type is:<br />
<br />
<haskell><br />
<br />
set_override_redirect :: Ptr SetWindowAttributes -> Bool -> IO ()<br />
<br />
</haskell><br />
<br />
This function takes the pointer to the XSetWindowAttributes structure and the flag to be set (True or False).<br />
<br />
For the list of avaliable attributes see<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3AAttributeMask the AttributeMask type defnition].<br />
<br />
For their meaning see <br />
[http://www.tronche.com/gui/x/xlib/window/attributes/#XSetWindowAttributes the XSetWindowAttributes structure reference].<br />
<br />
Now, our goal was to create a window that the Window Manager is going<br />
to ignore, and in order to do that all we need to set the attribute<br />
[http://www.tronche.com/gui/x/xlib/window/attributes/override-redirect.html CWOverrideRedirect] <br />
to True. And now we know how to do it.<br />
<br />
Ok, it's time to introduce our function to create new windows with the CWOverrideRedirect set to True<br />
<br />
<haskell><br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr) <br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
</haskell><br />
<br />
Like simpleCreateWindow, our function is a wrapper around<br />
createWindow, but this time we are manually setting the<br />
CWOverrideRedirect flag.<br />
<br />
As you see our function, unlike createSimpleWindow, does not have,<br />
among its arguments, the background and the border pixels. This colors<br />
can be set, for windows created with createWindow, using the attribute<br />
mask, and setting CWBackPixel and CWBorderPixel with the needed<br />
functions: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_background_pixel set_background_pixel] <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_border_pixel set_border_pixel].<br />
<br />
By the way, setting the border color with this version of<br />
mkUnmanagedWindow is actually useless since the border width is set to<br />
zero. In the next example we will set it to 1.<br />
<br />
Our function needs also the screen now, since we have to retrieve the<br />
default depth and visual.<br />
<br />
We can now rewrite our initial code using the new function now.<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
set_background_pixel attributes $ whitePixel dpy (defaultScreen dpy)<br />
set_border_pixel attributes $ blackPixel dpy (defaultScreen dpy)<br />
createWindow dpy rw x y w h 1 (defaultDepthOfScreen scr)<br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
</haskell><br />
<br />
Ok, let's give it a try. Did you see? Now the window will be placed in<br />
the specified x and y position, with the given dimensions. No Window<br />
Manager decorations, and so, no name displayed.<br />
<br />
===Changing an Existing Window's Attributes===<br />
<br />
This task requires <br />
[http://www.tronche.com/gui/x/xlib/window/XChangeWindowAttributes.html XChangeWindowAttrbutes()], <br />
implemented only in the darcs version of <br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras].<br />
<br />
In order to change a window's attributes we just need the window ID in<br />
that specific X server, after that we need to unmap the window first,<br />
and then change its attributes with changeWindowAttributes, the<br />
interface to XChangeWindowAttrbutes() implemented by<br />
[http://darcs.haskell.org/~sjanssen/X11-extras the darcs version of X11-extras].<br />
<br />
Here's the code:<br />
<br />
<haskell><br />
<br />
module Main where<br />
<br />
import Graphics.X11.Xlib<br />
import Graphics.X11.Xlib.Extras<br />
import System.Environment<br />
<br />
usage :: String -> String<br />
usage n = "Usage: " ++ n ++ " manage/unmanage windowID"<br />
<br />
main :: IO ()<br />
main = do<br />
args <- getArgs<br />
pn <- getProgName<br />
let (win,ac) = case args of<br />
[] -> error $ usage pn<br />
w -> case (w !!0) of <br />
"manage" -> (window, False)<br />
"unmanage" -> (window, True)<br />
_ -> error $ usage pn<br />
where window = case (w !! 1) of <br />
[] -> error $ usage pn<br />
w -> read w :: Window<br />
dpy <- openDisplay ""<br />
unmapWindow dpy win<br />
sync dpy False<br />
allocaSetWindowAttributes $<br />
\attributes -> do<br />
set_override_redirect attributes ac<br />
changeWindowAttributes dpy win cWOverrideRedirect attributes<br />
mapWindow dpy win<br />
sync dpy False<br />
<br />
</haskell><br />
<br />
Save it as Unmanage.hs and compile with:<br />
ghc --make Unmanage.hs -o unmanage<br />
<br />
To use it you need to retrieve the window ID with the stand alone utility<br />
xwininfo<br />
<br />
Then you run the above code with:<br />
unmanage unmanage/manage windowID<br />
<br />
to set override_redirect to True or False.<br />
<br />
Obviously the important part of the code is this:<br />
<haskell><br />
dpy <- openDisplay ""<br />
unmapWindow dpy win<br />
sync dpy False<br />
allocaSetWindowAttributes $<br />
\attributes -> do<br />
set_override_redirect attributes ac<br />
changeWindowAttributes dpy win cWOverrideRedirect attributes<br />
mapWindow dpy win<br />
sync dpy False<br />
</haskell><br />
<br />
where we:<br />
# connect to the X server<br />
# unmap the window<br />
# flush the output buffer to have the X server actually unmap the window<br />
# change the attributes with the same procedure we used to set them when creating the window<br />
# map the window again<br />
# flush the output buffer to see the change take effect.<br />
<br />
You can modify this program to change other window's attributes.<br />
<br />
==Colors and Color Depth==<br />
<br />
So far we have set the window background color as a window attribute.<br />
This is not the most convenient way to set the window background<br />
color: if we need to change it, we must change the window's attribute,<br />
and we have seen that this task requires unmapping the window,<br />
flushing the output with changeWindowAttributes within<br />
changeWindowAttributes, remapping the window and reflushing the output<br />
buffer. Moreover we can do that only we the darcs version of<br />
X11-extras...<br />
<br />
In the following sections we are going to adopt a more efficient way<br />
of setting the window's background color: we will start drawing into<br />
the window. But first we must familiarize with colors and the way the<br />
X server deals with them.<br />
<br />
So far we have set the colors by using some functions to retrieve<br />
their pixel values: blackPixel and whitePixel. These functions take<br />
the display and the default screen and return respectively the pixel<br />
values for the black and the white colors in that screen.<br />
<br />
A color is represented by a 32-bit unsigned integer, called a pixel<br />
value. The elements affecting the pixel representation of a color are:<br />
1. the color depth; 2. the colormap, which is a table containing red,<br />
green, and blue intensity values; 3. the visual type.<br />
<br />
All these elements are specific to a given piece of hardware, and so<br />
our X application must detect them in order to set colors<br />
appropriately for that given hardware.<br />
<br />
The approach we are going to use to accomplish this task is this: we<br />
are going to use named colors, or colors represented by<br />
[http://en.wikipedia.org/wiki/RGB RGB triple], such as "red",<br />
"yellow", or "#FFFFFF", etc; and we are going to translate these<br />
colors into the pixel values appropriate for the screen we are<br />
operating on.<br />
<br />
In order to achieve our goal we are going to use the function <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Color.html#v%3AallocNamedColor allocNamedColor] <br />
which is the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/color/XAllocNamedColor.html XAllocNamedColor()].<br />
<br />
The type signature of allocNamedColor is:<br />
<br />
<haskell><br />
<br />
allocNamedColor :: Display -> Colormap -> String -> IO (Color, Color)<br />
<br />
</haskell><br />
<br />
That is to say, given a display connection, a color map and a string -<br />
our color representation -, this function will return a tuple with the<br />
closest RGB values provided by the hardware and the exact RGB values,<br />
both encoded in a <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib.html#v%3AColor Haskell Color data type]. <br />
We will use the first approximated value.<br />
<br />
The Color data type has a field name we will use to retrieve the<br />
needed pixel value: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib.html#v%3Acolor_pixel color_pixel].<br />
<br />
We can now write this helper function:<br />
<br />
<haskell><br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(approx,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel approx <br />
<br />
</haskell><br />
<br />
To retrieve the colormap of the screen we used <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AdefaultColormap defaultColormap], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#DefaultColormap XDefaultColormap()], <br />
which requires the display and the screen, and returns the colormap of that screen.<br />
<br />
We can now rewrite our example using this new approach.<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel<br />
background_color <- initColor dpy "red"<br />
border_color <- initColor dpy "black" <br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
set_background_pixel attributes background_color<br />
set_border_pixel attributes border_color<br />
createWindow dpy rw x y w h 1 (defaultDepthOfScreen scr)<br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(apros,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel apros<br />
<br />
</haskell><br />
<br />
Just give it a try. Now you can also experiment with different colors.<br />
This approach will assure that our application will work no matter the<br />
color depth of the screen we are working on.<br />
<br />
==Drawing in Windows==<br />
<br />
The X server provides two objects that can be used to draw something<br />
to: windows and pixmaps.<br />
<br />
In this section we will start drawing into windows.<br />
<br />
We have seen that changing the background color of a window is a<br />
troublesome operation, since the window must be unamapped and<br />
remapped, memory for a foreign structure allocated, and so on.<br />
<br />
Instead, we can use some graphic operations to draw a rectangle over<br />
the window. We will latter manipulate the foreground, visible, color of<br />
this rectangle, that will become the new background of our window.<br />
<br />
We can also use multiple rectangles with different dimension to<br />
decorate our window with a border, for instance.<br />
<br />
Later on we will print some text over these rectangles.<br />
<br />
===Drawing Rectangles in a Window===<br />
<br />
Citing from<br />
[http://en.wikipedia.org/wiki/X_Window_System_core_protocol#Graphic_contexts_and_fonts Wikipedia]:<br />
<br />
<blockquote><br />
The client can request a number of graphic operations, such clearing<br />
an area, copying an area into another, drawing points, lines,<br />
rectangles, and text. Beside clearing, all operations are possible on<br />
all drawables, both windows and pixmaps.<br />
<br />
Most requests for graphic operations include a graphic context, which<br />
is a structure that contains the parameters of the graphic operations.<br />
A graphic context includes the foreground color, the background color,<br />
the font of text, and other graphic parameters. When requesting a<br />
graphic operation, the client includes a graphic context.<br />
</blockquote> <br />
<br />
In other words, as for setting window's attribute, we must use a<br />
foreign C structure to set parameter for graphic operations, and the<br />
we will feed this structure to the functions that will perform these<br />
graphic operations.<br />
<br />
We one difference: instead of operating within a function that<br />
allocates memory and creates a pointer to the foreign structure, now<br />
we have to explicitally create the Graphic Context, and free it after<br />
having used it, with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AcreateGC createGC], <br />
the interface to <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AcreateGC XCreateGC], <br />
and<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AfreeGC freeGC], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/GC/XFreeGC.html XFreeGC].<br />
<br />
Be careful: if you create a graphic con without freeing it after use,<br />
you are going to end up with a noticeable memory leak!<br />
<br />
The specific graphic functions we are going to need for drawing a<br />
rectangle into our window are:<br />
<br />
# [http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AsetForeground setForegrond] the interface to [http://www.tronche.com/gui/x/xlib/GC/convenience-functions/XSetForeground.html XSetForegroung]<br />
# [http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Context.html#v%3AsetBackground setBackground] the interface to [http://www.tronche.com/gui/x/xlib/GC/convenience-functions/XSetBackground.html XSetBackground]<br />
# [http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AfillRectangle fillRectangle] the interface to [http://www.tronche.com/gui/x/xlib/graphics/filling-areas/XFillRectangle.html XFillRectangle]<br />
<br />
The first two functions are needed to set the parameters in the<br />
Graphic Context. The third one will use this GC for filling a<br />
rectangle on the specified window. Just have a look to their type<br />
signatures:<br />
<br />
<haskell><br />
<br />
setForeground :: Display -> GC -> Pixel -> IO ()<br />
setBackground :: Display -> GC -> Pixel -> IO ()<br />
fillRectangle :: Display -> Drawable -> GC -> Position -> Position -> Dimension -> Dimension -> IO ()<br />
<br />
</haskell><br />
<br />
Ok, this is the function that we will be using for drawing into a<br />
window:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> IO ()<br />
drawInWin dpy win = do<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 0 0 100 100<br />
freeGC dpy gc<br />
<br />
</haskell><br />
<br />
This will just fill our window with a rectangle at (0, 0) (x, y)<br />
coordinates (relatives to the window's internal border), with the same<br />
dimensions of our window.<br />
<br />
Obviously we can play a bit with rectangles. This version, for<br />
instance, will draw 2 rectangles to simulate a blu rectangle with a<br />
green border, two pixels width:<br />
<br />
<haskell><br />
<br />
drawInWin :: Display -> Window -> IO ()<br />
drawInWin dpy win = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 100 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 96 96<br />
freeGC dpy gc<br />
<br />
</haskell><br />
<br />
You can use this function on a mapped window. This is our original<br />
example rewritten with this new approach:<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
drawInWin dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
drawInWin :: Display -> Window -> IO ()<br />
drawInWin dpy win = do<br />
bgcolor <- initColor dpy "green"<br />
fgcolor <- initColor dpy "blue"<br />
gc <- createGC dpy win<br />
setForeground dpy gc bgcolor<br />
fillRectangle dpy win gc 0 0 100 100<br />
setForeground dpy gc fgcolor<br />
fillRectangle dpy win gc 2 2 96 96<br />
freeGC dpy gc<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr)<br />
inputOutput visual cWOverrideRedirect attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
<br />
</haskell><br />
<br />
As you see, now mkUnmanagedWindow sets a null border width and does<br />
not set any background color. Everything is easily done with<br />
rectangles.<br />
<br />
===Printing a String===<br />
<br />
'''To be continued ...'''<br />
<br />
==Updating a Window==<br />
<br />
Citing from<br />
[http://en.wikipedia.org/wiki/X_Window_System_core_protocol#Pixmaps_and_drawables Wikipedia]:<br />
<br />
<blockquote> <br />
«A pixmap is a region of memory that can be used for<br />
drawing. Contrary to windows, the contents of pixmaps are not<br />
automatically shown on the screen. However, the content of a pixmap<br />
(or a part of it) can be transferred to a window and vice versa. This<br />
allows for techniques such as double buffering. Most of the graphical<br />
operations that can be done on windows can also be done on pixmaps.<br />
Windows and pixmaps are collectively named drawables, and their<br />
content data resides on the server.» <br />
</blockquote><br />
<br />
<br />
Pixmap and copyArea<br />
<br />
==Dealing with XEvents==<br />
<br />
Two approaches<br />
<br />
- [[User:AndreaRossato|Andrea Rossato]]<br />
<br />
[[Category:Tutorials]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=X_window_programming_in_Haskell&diff=14535X window programming in Haskell2007-07-19T17:44:46Z<p>AndreaRossato: added the color management section</p>
<hr />
<div>==Writing an X Application With Haskell Xlib Bindings==<br />
<br />
This tutorial is a side product of the research and the learning<br />
experience of writing a<br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmobar status bar] <br />
for the [http://xmonad.org XMonad Window Manager], the<br />
first WM written in Haskell.<br />
<br />
It will show you how to write a simple X application using the low<br />
level Xlib library. The goal is to write a simple text base clock,<br />
that will display the system time, to be run on top of every other<br />
applications, like a status bar.<br />
<br />
While the application is fairly simple, still it will require us to<br />
get to know quite a lot of the details that must be taken into account<br />
when writing a properly working X application.<br />
<br />
Obviously some understanding of X and Xlib is required.<br />
<br />
These are some links that can be used as reference:<br />
<br />
* [http://www.tronche.com/gui/x/xlib/ The Xlib Manual]: this is the reference manual, and you should look up here every function that we are going to use in this tutorial.<br />
* [http://en.wikipedia.org/wiki/Xlib Xlib (Wikipedia)]<br />
* [http://en.wikipedia.org/wiki/X_Window_System_core_protocol X Window System core protocol (Wikipedia)]<br />
* [http://www.sbin.org/doc/Xlib/ Xlib Programming Manual]: specifically the [http://www.sbin.org/doc/Xlib/chapt_02.html Chapter 2 X Concepts]<br />
<br />
This tutorial is dedicated to the intermediate Haskell coder. While I<br />
will try to write the simplest code I can (probably it will even look<br />
the dumbest, but that's me), I'm not going into much details about the<br />
Haskell part.<br />
<br />
What are we going to learn:<br />
* how to create a window and set, or change, its attributes;<br />
* how to draw in that window, specifically some text, with some properties, like fonts or colors;<br />
* how to properly update the window;<br />
* how to handle events, like a mouse button press.<br />
<br />
In order to compile the following code examples you need at least:<br />
<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11 X11], the Haskell binding to the X11 graphics library.<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras]: will be required in some examples. This library provides missing bindings to the X11 graphics library and is actively developed by Spencer Janssen at the time of this writing. Some functions needed in this tutorial can be found only in the darcs repository of X11-extras: [http://darcs.haskell.org/~sjanssen/X11-extras http://darcs.haskell.org/~sjanssen/X11-extras]. Read carefully the README before installing.<br />
<br />
<br />
<br />
==Hello World==<br />
<br />
Let's start from the usual simple "Hello World"<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
border = blackPixel dpy dflt<br />
background = whitePixel dpy dflt<br />
rootw <- rootWindow dpy dflt<br />
win <- createSimpleWindow dpy rootw 0 0 100 100 1 border background<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
</haskell><br />
<br />
The first function, <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AopenDisplay openDisplay],<br />
is the interface to the Xlib function <br />
[http://www.tronche.com/gui/x/xlib/display/opening.html XOpenDisplay()], <br />
and opens a connection to the X sever that controls a display. The connection is returned and bound to dpy. By applying <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AdefaultScreen defaultScreen], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#DefaultScreen XDefaultScreen], <br />
we get the root window. We need it in order to set out parent window in the most important function of the above code: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AcreateSimpleWindow createSimpleWindow], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html XCreateSimpleWindow].<br />
<br />
This function takes, as arguments: the display, the parent window of<br />
the window to be created, the x position, the y position, the width,<br />
the height, the border width, the border pixel and the background<br />
pixel.<br />
<br />
The x and y positions are relative to the upper left corner of the<br />
parent window's inside borders.<br />
<br />
In order to retrieve the values of the black and white pixels for that<br />
specific screen, we use two specific functions:<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AblackPixel blackPixel], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#BlackPixel BlackPixel], <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AwhitePixel whitePixel], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#WhitePixel WhitePixel]<br />
<br />
The function createSimpleWindow will return the window ID and, with<br />
this ID, we can start manipulating our newly created window, as we do,<br />
in the above code, with the function<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AsetTextProperty setTextProperty], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/ICC/client-to-window-manager/XSetTextProperty.html XSetTextProperty()].<br />
<br />
This function is needed, in our code, to set the window's name, that<br />
your window manager will display on some decoration attached to the<br />
window (other window managers will not display anything, for instance<br />
a tiling WM like [http://xmonad.org XMonad])<br />
<br />
To set the window's name we need to manipulate the <br />
[http://www.tronche.com/gui/x/xlib/ICC/client-to-window-manager/converting-string-lists.html XTextProperty structure]. <br />
<br />
Properties, such as the XTextProperty, have a string name and a<br />
numerical identifier called an atom. An atom is an ID that uniquely<br />
identifies a particular property. Property name strings are typically<br />
all upper case - with the first letter in low case when translated<br />
into Haskell - with words separated by underscores. In our example we<br />
had to set the WM_NAME property to "Hello World".<br />
<br />
Creating and manipulating a window is just the first step to have a<br />
new window displayed. In order for the window to become visible we<br />
must map it with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AmapWindow mapWindow], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/window/XMapWindow.html XMapWindow()]. <br />
This will make the window visible.<br />
<br />
Xlib will not send requests and calls to the Xserver immediately, but<br />
will buffer them and send the full buffer when some given conditions<br />
are met.<br />
<br />
One way to force the flushing of the output buffer is to call <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Event.html#v%3Async sync], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/event-handling/XSync.html XSync()], <br />
which takes 2 arguments: the connection (dpy) and a Boolean value that<br />
indicates whether XSync() must discard all events on the event queue.<br />
<br />
After that the Xserver will eventually display our window. <br />
<br />
The rest of the above example does nothing else but blocking the<br />
program execution for 10 seconds (to let you stare at your newly<br />
created window) and then will exit.<br />
<br />
==Window's Attributes==<br />
<br />
Even though in our "Hello World" example we set the window's<br />
dimension, we have no assurance that the Window Manager will respect<br />
our decision.<br />
<br />
[http://xmonad.org XMonad], for instance, will just create a window<br />
with the dimensions needed to fill its tiled screen, no matter what<br />
you set in createSimpleWindow.<br />
<br />
But we decided to write a small clock that will behave as a status<br />
bar, that is to say, we want to create a window that will not be<br />
managed by a Window Manager.<br />
<br />
In order to achieve this result we need to start dealing with window's<br />
attributes.<br />
<br />
There are two ways of dealing with window's attributes: the first is<br />
to set them at window's creation time, but in that case<br />
createSimpleWindow is not powerful enough.<br />
<br />
The second way is to change window's attributes after the window's has<br />
been created. This second approach is not implemented<br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11 X11] but<br />
has been implemented in the darcs version of <br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras].<br />
<br />
===Setting Window's Attribute at Creation Time===<br />
<br />
In order to set window's attributes at creation time, the window must be created with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AcreateWindow createWindow], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html XCreateWindow()].<br />
<br />
The type signature of this function is quite long:<br />
<br />
<haskell><br />
<br />
createWindow :: Display -> Window <br />
-> Position -> Position <br />
-> Dimension -> Dimension <br />
-> CInt <br />
-> CInt <br />
-> WindowClass <br />
-> Visual <br />
-> AttributeMask <br />
-> Ptr SetWindowAttributes <br />
-> IO Window<br />
<br />
</haskell><br />
<br />
That is to say:<br />
* the connection and the parent window<br />
* the x and y position (origins in the upper left corner of the inside border of the parent window)<br />
* width and height<br />
* border width<br />
* depth of screen<br />
* the window's class<br />
* the visual<br />
* the attribute mask<br />
* and the pointer to the [http://www.tronche.com/gui/x/xlib/window/attributes/#XSetWindowAttributes XSetWindowAttributes] foreign C structure.<br />
<br />
This last one gives you an idea of the type of operation we must do in<br />
order to create a window (createSimpleWindow is just a wrapper around<br />
this more complicated createWindow, with some default arguments): we<br />
need a function to allocate some memory for the creation of the<br />
foreign C structure, and then manipulate this foreign structure from<br />
within this function.<br />
<br />
The needed function is <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AallocaSetWindowAttributes allocaSetWindowAttributes], <br />
whose type indeed is:<br />
<br />
<haskell><br />
<br />
allocaSetWindowAttributes :: (Ptr SetWindowAttributes -> IO a) -> IO a<br />
<br />
</haskell><br />
<br />
allocaSetWindowAttributes will take a function which takes the pointer<br />
to the foreign structure as its argument. This function will perform<br />
an IO action that is the action returned by allocaSetWindowAttributes.<br />
<br />
In our case allocaSetWindowAttributes will take a function that will<br />
use createWindow to return the new window.<br />
<br />
So, we will need to use createWindow inside allocaSetWindowAttributes.<br />
We will soon see how. But first let's analyze the other arguments of<br />
createWindow.<br />
<br />
The display, the parent window, the coordinates and dimensions are the<br />
same as with createSimpleWindow. But now we must specify the depth of<br />
the screen, the window's class, the visual and the attribute mask. We<br />
also need to manipulate the XSetWindowAttribute after its creation by<br />
allocaSetWindowAttributes, before calling createWindow.<br />
<br />
«The depth is the number of bits available for each pixel to represent color (or gray scales). The visual represents the way pixel values are translated to produce color or monochrome output on the monitor.»( see [http://www.sbin.org/doc/Xlib/chapt_02.html http://www.sbin.org/doc/Xlib/chapt_02.html])<br />
<br />
For the depth we are going to use <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Screen.html#v%3AdefaultDepthOfScreen defaultDepthOfScreen], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/image-format-macros.html#DefaultDepthOfScreen XDefaultDepthOfScreen()], <br />
to retrieve the default screen depth.<br />
<br />
For the visual we are going to use<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Screen.html#v%3AdefaultVisualOfScreen defaultVisualOfScreen], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/image-format-macros.html#DefaultVisualOfScreen DefaultVisualOfScreen].<br />
<br />
The <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3AWindowClass WindowClass] <br />
can either be copyFromParent, inputOutput, or inputOnly. In the first case the<br />
class is copied from the class of the parent window. An inputOnly<br />
window can only be used for receiving input events. In our code we are<br />
going to use inputOutput windows, windows that can receive input events<br />
and that can also be used to display some output.<br />
<br />
The attributeMask «specifies which window attributes are defined in<br />
the attributes argument. This mask is the bitwise inclusive OR of the<br />
valid attribute mask bits. If value mask is zero, the attributes are<br />
ignored and are not referenced.»<br />
(see [http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html]).<br />
<br />
In other words, in order to set more then one attribute, you need to pass a value mask such as:<br />
<br />
<haskell><br />
<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel .|. etc ...<br />
<br />
</haskell><br />
<br />
and set each of this attributes within allocaSetWindowAttributes with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#12 specific attributes setting functions].<br />
<br />
Among these functions the one we need: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_override_redirect set_override_redirect], <br />
whose type is:<br />
<br />
<haskell><br />
<br />
set_override_redirect :: Ptr SetWindowAttributes -> Bool -> IO ()<br />
<br />
</haskell><br />
<br />
This function takes the pointer to the XSetWindowAttributes structure and the flag to be set (True or False).<br />
<br />
For the list of avaliable attributes see<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3AAttributeMask the AttributeMask type defnition].<br />
<br />
For their meaning see <br />
[http://www.tronche.com/gui/x/xlib/window/attributes/#XSetWindowAttributes the XSetWindowAttributes structure reference].<br />
<br />
Now, our goal was to create a window that the Window Manager is going<br />
to ignore, and in order to do that all we need to set the attribute<br />
[http://www.tronche.com/gui/x/xlib/window/attributes/override-redirect.html CWOverrideRedirect] <br />
to True. And now we know how to do it.<br />
<br />
Ok, it's time to introduce our function to create new windows with the CWOverrideRedirect set to True<br />
<br />
<haskell><br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr) <br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
</haskell><br />
<br />
Like simpleCreateWindow, our function is a wrapper around<br />
createWindow, but this time we are manually setting the<br />
CWOverrideRedirect flag.<br />
<br />
As you see our function, unlike createSimpleWindow, does not have,<br />
among its arguments, the background and the border pixels. This colors<br />
can be set, for windows created with createWindow, using the attribute<br />
mask, and setting CWBackPixel and CWBorderPixel with the needed<br />
functions: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_background_pixel set_background_pixel] <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_border_pixel set_border_pixel].<br />
<br />
By the way, setting the border color with this version of<br />
mkUnmanagedWindow is actually useless since the border width is set to<br />
zero. In the next example we will set it to 1.<br />
<br />
Our function needs also the screen now, since we have to retrieve the<br />
default depth and visual.<br />
<br />
We can now rewrite our initial code using the new function now.<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
set_background_pixel attributes $ whitePixel dpy (defaultScreen dpy)<br />
set_border_pixel attributes $ blackPixel dpy (defaultScreen dpy)<br />
createWindow dpy rw x y w h 1 (defaultDepthOfScreen scr)<br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
</haskell><br />
<br />
Ok, let's give it a try. Did you see? Now the window will be placed in<br />
the specified x and y position, with the given dimensions. No Window<br />
Manager decorations, and so, no name displayed.<br />
<br />
===Changing an Existing Window's Attributes===<br />
<br />
This task requires <br />
[http://www.tronche.com/gui/x/xlib/window/XChangeWindowAttributes.html XChangeWindowAttrbutes()], <br />
implemented only in the darcs version of <br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras].<br />
<br />
In order to change a window's attributes we just need the window ID in<br />
that specific X server, after that we need to unmap the window first,<br />
and then change its attributes with changeWindowAttributes, the<br />
interface to XChangeWindowAttrbutes() implemented by<br />
[http://darcs.haskell.org/~sjanssen/X11-extras the darcs version of X11-extras].<br />
<br />
Here's the code:<br />
<br />
<haskell><br />
<br />
module Main where<br />
<br />
import Graphics.X11.Xlib<br />
import Graphics.X11.Xlib.Extras<br />
import System.Environment<br />
<br />
usage :: String -> String<br />
usage n = "Usage: " ++ n ++ " manage/unmanage windowID"<br />
<br />
main :: IO ()<br />
main = do<br />
args <- getArgs<br />
pn <- getProgName<br />
let (win,ac) = case args of<br />
[] -> error $ usage pn<br />
w -> case (w !!0) of <br />
"manage" -> (window, False)<br />
"unmanage" -> (window, True)<br />
_ -> error $ usage pn<br />
where window = case (w !! 1) of <br />
[] -> error $ usage pn<br />
w -> read w :: Window<br />
dpy <- openDisplay ""<br />
unmapWindow dpy win<br />
sync dpy False<br />
allocaSetWindowAttributes $<br />
\attributes -> do<br />
set_override_redirect attributes ac<br />
changeWindowAttributes dpy win cWOverrideRedirect attributes<br />
mapWindow dpy win<br />
sync dpy False<br />
<br />
</haskell><br />
<br />
Save it as Unmanage.hs and compile with:<br />
ghc --make Unmanage.hs -o unmanage<br />
<br />
To use it you need to retrieve the window ID with the stand alone utility<br />
xwininfo<br />
<br />
Then you run the above code with:<br />
unmanage unmanage/manage windowID<br />
<br />
to set override_redirect to True or False.<br />
<br />
Obviously the important part of the code is this:<br />
<haskell><br />
dpy <- openDisplay ""<br />
unmapWindow dpy win<br />
sync dpy False<br />
allocaSetWindowAttributes $<br />
\attributes -> do<br />
set_override_redirect attributes ac<br />
changeWindowAttributes dpy win cWOverrideRedirect attributes<br />
mapWindow dpy win<br />
sync dpy False<br />
</haskell><br />
<br />
where we:<br />
# connect to the X server<br />
# unmap the window<br />
# flush the output buffer to have the X server actually unmap the window<br />
# change the attributes with the same procedure we used to set them when creating the window<br />
# map the window again<br />
# flush the output buffer to see the change take effect.<br />
<br />
You can modify this program to change other window's attributes.<br />
<br />
==Colors and Color Depth==<br />
<br />
So far we have set the window background color as a window attribute.<br />
This is not the most convenient way to set the window background<br />
color: if need to change it we need to change the window's attribute,<br />
and we have seen that this task requires unmapping the window,flush<br />
the output, using changeWindowAttributes within<br />
changeWindowAttributes, remap the window and reflush the buffer.<br />
Moreover we need the darcs version of X11-extras...<br />
<br />
In the following section we are gig to adopt a more efficient way of<br />
setting the window's background color: we will start drawing into the<br />
window. But first we must familiarize with colors and the way the X<br />
server deals with them.<br />
<br />
So far we set the colors using some function: blackPixel and<br />
whitePixel. These functions take the display and the default screen<br />
and return respectively the pixel for the black and the white colors<br />
in that screen.<br />
<br />
We have seen that a color is represented by a 32-bit unsigned integer,<br />
called a pixel value. The elements affecting the representation of a<br />
color are 1. the color depth; 2. the colormap, which is a table<br />
containing red, green, and blue intensity values; 3. the visual type.<br />
<br />
All these elements are specific to a given piece of hardware, and so<br />
our X application must detect them in order to set colors<br />
appropriately for that given screen.<br />
<br />
The approach we are going to use to accomplish this task is this: we<br />
are going to use named colors, or colors represented by<br />
[http://en.wikipedia.org/wiki/RGB RGB triple], such as "red",<br />
"yellow", or "#FFFFFF", etc; and we are going to translate these<br />
colors into the pixel values appropriate for the screen we are<br />
operating on.<br />
<br />
In order to achieve our goal we are going to use the function <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Color.html#v%3AallocNamedColor allocNamedColor] <br />
which is the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/color/XAllocNamedColor.html XAllocNamedColor()].<br />
<br />
The type signature of allocNamedColor is:<br />
<br />
<haskell><br />
<br />
allocNamedColor :: Display -> Colormap -> String -> IO (Color, Color)<br />
<br />
</haskell><br />
<br />
That is to say, given a display connection, a color map and a string -<br />
our color representation -, this function will return a tuple with the<br />
closest RGB values provided by the hardware and the exact RGB values.<br />
We will use the first for our future operation.<br />
<br />
The <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib.html#v%3AColor Haskell Color data type] <br />
has a field name we will use to retrieve the needed pixel value:<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib.html#v%3Acolor_pixel color_pixel].<br />
<br />
We can now write this helper function:<br />
<br />
<haskell><br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(apros,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel apros<br />
<br />
</haskell><br />
<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AdefaultColormap defaultColormap], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#DefaultColormap XDefaultColormap()], <br />
requires the display and the screen, and returns the colormap for that screen.<br />
<br />
We can now rewrite our example using this new approach.<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel<br />
background_color <- initColor dpy "red"<br />
border_color <- initColor dpy "black" <br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
set_background_pixel attributes background_color<br />
set_border_pixel attributes border_color<br />
createWindow dpy rw x y w h 1 (defaultDepthOfScreen scr)<br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
<br />
initColor :: Display -> String -> IO Pixel<br />
initColor dpy color = do<br />
let colormap = defaultColormap dpy (defaultScreen dpy)<br />
(apros,real) <- allocNamedColor dpy colormap color<br />
return $ color_pixel apros<br />
<br />
</haskell><br />
<br />
Just give it a try. Now you can also experiment with different color.<br />
This approach will assure that our application will work no matter the<br />
color depth of the screen we are operating on.<br />
<br />
==Printing a String==<br />
<br />
'''To be continued ...'''<br />
<br />
fonts, string length, colors, etc<br />
<br />
==Updating a Window==<br />
<br />
Pixmap and copyToWindow<br />
<br />
==Dealing with XEvents==<br />
<br />
Two approaches<br />
<br />
- [[User:AndreaRossato|Andrea Rossato]]<br />
<br />
[[Category:Tutorials]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=User:AndreaRossato&diff=14531User:AndreaRossato2007-07-19T13:48:32Z<p>AndreaRossato: removing spam</p>
<hr />
<div>Something about me.<br />
I'm a legal scholar so I'm not suppose to give legal advise.<br />
<br />
In this wiki I basically keep on writing unfinished tutorials.<br />
<br />
It's my way of backing up the information I'm soon going to forget. I beg your pardon.<br />
<br />
andrea dot rossato at unibz dot it.<br />
<br />
<br />
- [[User:AndreaRossato|Andrea Rossato]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=X_window_programming_in_Haskell&diff=14530X window programming in Haskell2007-07-19T13:22:47Z<p>AndreaRossato: it's true!!</p>
<hr />
<div>==Writing an X Application With Haskell Xlib Bindings==<br />
<br />
This tutorial is a side product of the research and the learning<br />
experience of writing a<br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmobar status bar] <br />
for the [http://xmonad.org XMonad Window Manager], the<br />
first WM written in Haskell.<br />
<br />
It will show you how to write a simple X application using the low<br />
level Xlib library. The goal is to write a simple text base clock,<br />
that will display the system time, to be run on top of every other<br />
applications, like a status bar.<br />
<br />
While the application is fairly simple, still it will require us to<br />
get to know quite a lot of the details that must be taken into account<br />
when writing a properly working X application.<br />
<br />
Obviously some understanding of X and Xlib is required.<br />
<br />
These are some links that can be used as reference:<br />
<br />
* [http://www.tronche.com/gui/x/xlib/ The Xlib Manual]: this is the reference manual, and you should look up here every function that we are going to use in this tutorial.<br />
* [http://en.wikipedia.org/wiki/Xlib Xlib (Wikipedia)]<br />
* [http://en.wikipedia.org/wiki/X_Window_System_core_protocol X Window System core protocol (Wikipedia)]<br />
* [http://www.sbin.org/doc/Xlib/ Xlib Programming Manual]: specifically the [http://www.sbin.org/doc/Xlib/chapt_02.html Chapter 2 X Concepts]<br />
<br />
This tutorial is dedicated to the intermediate Haskell coder. While I<br />
will try to write the simplest code I can (probably it will even look<br />
the dumbest, but that's me), I'm not going into much details about the<br />
Haskell part.<br />
<br />
What are we going to learn:<br />
* how to create a window and set, or change, its attributes;<br />
* how to draw in that window, specifically some text, with some properties, like fonts or colors;<br />
* how to properly update the window;<br />
* how to handle events, like a mouse button press.<br />
<br />
In order to compile the following code examples you need at least:<br />
<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11 X11], the Haskell binding to the X11 graphics library.<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras]: will be required in some examples. This library provides missing bindings to the X11 graphics library and is actively developed by Spencer Janssen at the time of this writing. Some functions needed in this tutorial can be found only in the darcs repository of X11-extras: [http://darcs.haskell.org/~sjanssen/X11-extras http://darcs.haskell.org/~sjanssen/X11-extras]. Read carefully the README before installing.<br />
<br />
<br />
<br />
==Hello World==<br />
<br />
Let's start from the usual simple "Hello World"<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
border = blackPixel dpy dflt<br />
background = whitePixel dpy dflt<br />
rootw <- rootWindow dpy dflt<br />
win <- createSimpleWindow dpy rootw 0 0 100 100 1 border background<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
</haskell><br />
<br />
The first function, <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AopenDisplay openDisplay],<br />
is the interface to the Xlib function <br />
[http://www.tronche.com/gui/x/xlib/display/opening.html XOpenDisplay()], <br />
and opens a connection to the X sever that controls a display. The connection is returned and bound to dpy. By applying <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AdefaultScreen defaultScreen], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#DefaultScreen XDefaultScreen], <br />
we get the root window. We need it in order to set out parent window in the most important function of the above code: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AcreateSimpleWindow createSimpleWindow], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html XCreateSimpleWindow].<br />
<br />
This function takes, as arguments: the display, the parent window of<br />
the window to be created, the x position, the y position, the width,<br />
the height, the border width, the border pixel and the background<br />
pixel.<br />
<br />
The x and y positions are relative to the upper left corner of the<br />
parent window's inside borders.<br />
<br />
In order to retrieve the values of the black and white pixels for that<br />
specific screen, we use two specific functions:<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AblackPixel blackPixel], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#BlackPixel BlackPixel], <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AwhitePixel whitePixel], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#WhitePixel WhitePixel]<br />
<br />
<br />
The function createSimpleWindow will return the window ID and, with<br />
this ID, we can start manipulating our newly created window, as we do,<br />
in the above code, with the function<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AsetTextProperty setTextProperty], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/ICC/client-to-window-manager/XSetTextProperty.html XSetTextProperty()].<br />
<br />
This function is needed, in our code, to set the window's name, that<br />
your window manager will display on some decoration attached to the<br />
window (other window managers will not display anything, for instance<br />
a tiling WM like [http://xmonad.org XMonad])<br />
<br />
To set the window's name we need to manipulate the <br />
[http://www.tronche.com/gui/x/xlib/ICC/client-to-window-manager/converting-string-lists.html XTextProperty structure]. <br />
<br />
Properties, such as the XTextProperty, have a string name and a<br />
numerical identifier called an atom. An atom is an ID that uniquely<br />
identifies a particular property. Property name strings are typically<br />
all upper case - with the first letter in low case when translated<br />
into Haskell - with words separated by underscores. In our example we<br />
had to set the WM_NAME property to "Hello World".<br />
<br />
Creating and manipulating a window is just the first step to have a<br />
new window displayed. In order for the window to become visible we<br />
must map it with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AmapWindow mapWindow], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/window/XMapWindow.html XMapWindow()]. <br />
This will make the window visible.<br />
<br />
Xlib will not send requests and calls to the Xserver immediately, but<br />
will buffer them and send the full buffer when some given conditions<br />
are met.<br />
<br />
One way to force the flushing of the output buffer is to call <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Event.html#v%3Async sync], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/event-handling/XSync.html XSync()], <br />
which takes 2 arguments: the connection (dpy) and a Boolean value that<br />
indicates whether XSync() must discard all events on the event queue.<br />
<br />
After that the Xserver will eventually display our window. <br />
<br />
The rest of the above example does nothing else but blocking the<br />
program execution for 10 seconds (to let you stare at your newly<br />
created window) and then will exit.<br />
<br />
==Window's Attributes==<br />
<br />
Even though in our "Hello World" example we set the window's<br />
dimension, we have no assurance that the Window Manager will respect<br />
our decision.<br />
<br />
[http://xmonad.org XMonad], for instance, will just create a window<br />
with the dimensions needed to fill its tiled screen, no matter what<br />
you set in createSimpleWindow.<br />
<br />
But we decided to write a small clock that will behave as a status<br />
bar, that is to say, we want to create a window that will not be<br />
managed by a Window Manager.<br />
<br />
In order to achieve this result we need to start dealing with window's<br />
attributes.<br />
<br />
There are two ways of dealing with window's attributes: the first is<br />
to set them at window's creation time, but in that case<br />
createSimpleWindow is not powerful enough.<br />
<br />
The second way is to change window's attributes after the window's has<br />
been created. This second approach is not implemented<br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11 X11] but<br />
has been implemented in the darcs version of <br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras].<br />
<br />
===Setting Window's Attribute at Creation Time===<br />
<br />
In order to set window's attributes at creation time, the window must be created with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AcreateWindow createWindow], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html XCreateWindow()].<br />
<br />
The type signature of this function is quite long:<br />
<br />
<haskell><br />
<br />
createWindow :: Display -> Window <br />
-> Position -> Position <br />
-> Dimension -> Dimension <br />
-> CInt <br />
-> CInt <br />
-> WindowClass <br />
-> Visual <br />
-> AttributeMask <br />
-> Ptr SetWindowAttributes <br />
-> IO Window<br />
<br />
</haskell><br />
<br />
That is to say:<br />
* the connection and the parent window<br />
* the x and y position (origins in the upper left corner of the inside border of the parent window)<br />
* width and height<br />
* border width<br />
* depth of screen<br />
* the window's class<br />
* the visual<br />
* the attribute mask<br />
* and the pointer to the [http://www.tronche.com/gui/x/xlib/window/attributes/#XSetWindowAttributes XSetWindowAttributes] foreign C structure.<br />
<br />
This last one gives you an idea of the type of operation we must do in<br />
order to create a window (createSimpleWindow is just a wrapper around<br />
this more complicated createWindow, with some default arguments): we<br />
need a function to allocate some memory for the creation of the<br />
foreign C structure, and then manipulate this foreign structure from<br />
within this function.<br />
<br />
The needed function is <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AallocaSetWindowAttributes allocaSetWindowAttributes], <br />
whose type indeed is:<br />
<br />
<haskell><br />
<br />
allocaSetWindowAttributes :: (Ptr SetWindowAttributes -> IO a) -> IO a<br />
<br />
</haskell><br />
<br />
allocaSetWindowAttributes will take a function which takes the pointer<br />
to the foreign structure as its last argument. This function will<br />
perform an IO action that is the action returned by<br />
allocaSetWindowAttributes.<br />
<br />
In our case allocaSetWindowAttributes will take a function that will<br />
use createWindow to return the new window.<br />
<br />
So, we will need to use createWindow inside allocaSetWindowAttributes.<br />
We will soon see how. But first let's analyze the other arguments of<br />
createWindow.<br />
<br />
The display, the parent window, the coordinates and dimensions are the<br />
same as with createSimpleWindow. But now we must specify the depth of<br />
the screen, the window's class, the visual and the attribute mask. We<br />
also need to manipulate the XSetWindowAttribute after its creation by<br />
allocaSetWindowAttributes, before calling createWindow.<br />
<br />
«The depth is the number of bits available for each pixel to represent color (or gray scales). The visual represents the way pixel values are translated to produce color or monochrome output on the monitor.»( see [http://www.sbin.org/doc/Xlib/chapt_02.html http://www.sbin.org/doc/Xlib/chapt_02.html])<br />
<br />
For the depth we are going to use <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Screen.html#v%3AdefaultDepthOfScreen defaultDepthOfScreen], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/image-format-macros.html#DefaultDepthOfScreen XDefaultDepthOfScreen()], <br />
to retrieve the default screen depth.<br />
<br />
For the visual we are going to use<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Screen.html#v%3AdefaultVisualOfScreen defaultVisualOfScreen], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/image-format-macros.html#DefaultVisualOfScreen DefaultVisualOfScreen].<br />
<br />
The <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3AWindowClass WindowClass] <br />
can either be copyFromParent, inputOutput, or inputOnly. In the first case the<br />
class is copied from the class of the parent window. An inputOnly<br />
window can only be used for receiving input events. In our code we are<br />
going to use inputOutput windows, windows that can receive input events<br />
and that can also be used to display some output.<br />
<br />
The attributeMask «specifies which window attributes are defined in<br />
the attributes argument. This mask is the bitwise inclusive OR of the<br />
valid attribute mask bits. If value mask is zero, the attributes are<br />
ignored and are not referenced.»<br />
(see [http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html]).<br />
<br />
In other words, in order to set more then one attribute, you need to pass a value mask such as:<br />
<br />
<haskell><br />
<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel .|. etc ...<br />
<br />
</haskell><br />
<br />
and set each of this attributes within allocaSetWindowAttributes with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#12 specific attributes setting functions].<br />
<br />
Among this functions the one we need: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_override_redirect set_override_redirect], <br />
whose type is:<br />
<br />
<haskell><br />
<br />
set_override_redirect :: Ptr SetWindowAttributes -> Bool -> IO ()<br />
<br />
</haskell><br />
<br />
This function takes the pointer to the XSetWindowAttributes structure and the flag to be set (True or False).<br />
<br />
For the list of avaliable attributes see<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3AAttributeMask the AttributeMask type defnition].<br />
<br />
For their meaning see <br />
[http://www.tronche.com/gui/x/xlib/window/attributes/#XSetWindowAttributes the XSetWindowAttributes structure reference].<br />
<br />
Now, our goal was to create a window that the Window Manager is going<br />
to ignore, and in order to do that all we need to set the attribute<br />
[http://www.tronche.com/gui/x/xlib/window/attributes/override-redirect.html CWOverrideRedirect] <br />
to True. And now we know how to do it.<br />
<br />
Ok, it's time to introduce our function to create new windows with the CWOverrideRedirect set to True<br />
<br />
<haskell><br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr) <br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
</haskell><br />
<br />
Like simpleCreateWindow, our function is a wrapper around<br />
createWindow, but this time we are manually setting the<br />
CWOverrideRedirect flag.<br />
<br />
As you see our function, unlike createSimpleWindow, does not have,<br />
among its arguments, the background and the border pixels. This colors<br />
can be set, for windows created with createWindow, using the attribute<br />
mask, and setting CWBackPixel and CWBorderPixel with the needed<br />
functions: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_background_pixel set_background_pixel] <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_border_pixel set_border_pixel].<br />
<br />
Our function needs also the screen now, since we have to retrieve the<br />
default depth and visual.<br />
<br />
We can now rewrite our initial code using the new function now.<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
set_background_pixel attributes $ whitePixel dpy (defaultScreen dpy)<br />
set_border_pixel attributes $ blackPixel dpy (defaultScreen dpy)<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr)<br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
</haskell><br />
<br />
Ok, let's give it a try. Did you see? Now the window will be placed in<br />
the specified x and y position, with the given dimensions. No Window<br />
Manager decorations, and so, no name displayed.<br />
<br />
===Changing and Existing Window's Attributes===<br />
<br />
This task requires <br />
[http://www.tronche.com/gui/x/xlib/window/XChangeWindowAttributes.html XChangeWindowAttrbutes()], <br />
implemented only in the darcs version of <br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras].<br />
<br />
In order to change a window's attributes we just need the window ID in<br />
that specific X server, after that we need to unmap the window first,<br />
and then change its attributes with changeWindowAttributes, the<br />
interface to XChangeWindowAttrbutes() implemented by<br />
[http://darcs.haskell.org/~sjanssen/X11-extras the darcs version of X11-extras].<br />
<br />
Here's the code:<br />
<br />
<haskell><br />
<br />
module Main where<br />
<br />
import Graphics.X11.Xlib<br />
import Graphics.X11.Xlib.Extras<br />
import System.Environment<br />
<br />
usage :: String -> String<br />
usage n = "Usage: " ++ n ++ " manage/unmanage windowID"<br />
<br />
main :: IO ()<br />
main = do<br />
args <- getArgs<br />
pn <- getProgName<br />
let (win,ac) = case args of<br />
[] -> error $ usage pn<br />
w -> case (w !!0) of <br />
"manage" -> (window, False)<br />
"unmanage" -> (window, True)<br />
_ -> error $ usage pn<br />
where window = case (w !! 1) of <br />
[] -> error $ usage pn<br />
w -> read w :: Window<br />
dpy <- openDisplay ""<br />
unmapWindow dpy win<br />
sync dpy False<br />
allocaSetWindowAttributes $<br />
\attributes -> do<br />
set_override_redirect attributes ac<br />
changeWindowAttributes dpy win cWOverrideRedirect attributes<br />
mapWindow dpy win<br />
sync dpy False<br />
<br />
</haskell><br />
<br />
Save it as Unmanage.hs and compile with:<br />
ghc --make Unmanage.hs -o unmanage<br />
<br />
To use it you need to retrieve the window ID with the stand alone utility<br />
xwininfo<br />
<br />
Then you run the above code with:<br />
unmanage unmanage/manage windowID<br />
<br />
to set override_redirect to True or False.<br />
<br />
Obviously the important part of the code is this:<br />
<haskell><br />
dpy <- openDisplay ""<br />
unmapWindow dpy win<br />
sync dpy False<br />
allocaSetWindowAttributes $<br />
\attributes -> do<br />
set_override_redirect attributes ac<br />
changeWindowAttributes dpy win cWOverrideRedirect attributes<br />
mapWindow dpy win<br />
sync dpy False<br />
</haskell><br />
<br />
where we:<br />
# connect to the X server<br />
# unmap the window<br />
# flush the output buffer to have the X server actually unmap the window<br />
# change the attributes with the same procedure we used to set them when creating the window<br />
# map the window again<br />
# flush the output buffer to see the change take effect.<br />
<br />
You can modify this program to change other window's attributes.<br />
<br />
==Colors and Color Depth==<br />
<br />
'''To be continued ...'''<br />
<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Color.html#v%3AallocNamedColor allocNamedColor] <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/color/XAllocNamedColor.html XAllocNamedColor()]<br />
<br />
allocNamedColor :: Display -> Colormap -> String -> IO (Color, Color)<br />
<br />
data Color:<br />
http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib.html#v%3AColor<br />
<br />
<br />
==Printing a String==<br />
<br />
fonts, string length, colors, etc<br />
<br />
==Updating a Window==<br />
<br />
Pixmap and copyToWindow<br />
<br />
==Dealing with XEvents==<br />
<br />
Two approaches<br />
<br />
- [[User:AndreaRossato|Andrea Rossato]]<br />
<br />
[[Category:Tutorials]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=X_window_programming_in_Haskell&diff=14529X window programming in Haskell2007-07-19T13:18:03Z<p>AndreaRossato: initial import</p>
<hr />
<div>==Writing an X Application With Haskell Xlib Bindings==<br />
<br />
This tutorial is a side product of the research and the learning<br />
experience of writing a<br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/xmobar status bar] <br />
for the [http://xmonad.org XMonad Window Manager], the<br />
first WM written in Haskell.<br />
<br />
It will show you how to write a simple X application using the low<br />
level Xlib library. The goal is to write a simple text base clock,<br />
that will display the system time, to be run on top of every other<br />
applications, like a status bar.<br />
<br />
While the application is fairly simple, still it will require us to<br />
get to know quite a lot of the details that must be taken into account<br />
when writing a properly working X application.<br />
<br />
Obviously some understanding of X and Xlib is required.<br />
<br />
These are some links that can be used as reference:<br />
<br />
* [http://www.tronche.com/gui/x/xlib/ The Xlib Manual]: this is the reference manual, and you should look up here every function that we are going to use in this tutorial.<br />
* [http://en.wikipedia.org/wiki/Xlib Xlib (Wikipedia)]<br />
* [http://en.wikipedia.org/wiki/X_Window_System_core_protocol X Window System core protocol (Wikipedia)]<br />
* [http://www.sbin.org/doc/Xlib/ Xlib Programming Manual]: specifically the [http://www.sbin.org/doc/Xlib/chapt_02.html Chapter 2 X Concepts]<br />
<br />
This tutorial is dedicated to the intermediate Haskell coder. While I<br />
will try to write the simplest code I can (probably it will even look<br />
the dumbest, but that's me), I'm not going into much details about the<br />
Haskell part.<br />
<br />
What are we going to learn:<br />
* how to create a window and set, or change, its attributes;<br />
* how to draw in that window, specifically some text, with some properties, like fonts or colors;<br />
* how to properly update the window;<br />
* how to handle events, like a mouse button press.<br />
<br />
In order to compile the following code examples you need at least:<br />
<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11 X11], the Haskell binding to the X11 graphics library.<br />
* [http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras]: will be required in some examples. This library provides missing bindings to the X11 graphics library and is actively developed by Spencer Janssen at the time of this writing. Some functions needed in this tutorial can be found only in the darcs repository of X11-extras: [http://darcs.haskell.org/~sjanssen/X11-extras http://darcs.haskell.org/~sjanssen/X11-extras]. Read carefully the README before installing.<br />
<br />
<br />
<br />
==Hello World==<br />
<br />
Let's start from the usual simple "Hello World"<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
border = blackPixel dpy dflt<br />
background = whitePixel dpy dflt<br />
rootw <- rootWindow dpy dflt<br />
win <- createSimpleWindow dpy rootw 0 0 100 100 1 border background<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
</haskell><br />
<br />
The first function, <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AopenDisplay openDisplay],<br />
is the interface to the Xlib function <br />
[http://www.tronche.com/gui/x/xlib/display/opening.html XOpenDisplay()], <br />
and opens a connection to the X sever that controls a display. The connection is returned and bound to dpy. By applying <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AdefaultScreen defaultScreen], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#DefaultScreen XDefaultScreen], <br />
we get the root window. We need it in order to set out parent window in the most important function of the above code: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AcreateSimpleWindow createSimpleWindow], <br />
the interface to <br />
[http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html XCreateSimpleWindow].<br />
<br />
This function takes, as arguments: the display, the parent window of<br />
the window to be created, the x position, the y position, the width,<br />
the height, the border width, the border pixel and the background<br />
pixel.<br />
<br />
The x and y positions are relative to the upper left corner of the<br />
parent window's inside borders.<br />
<br />
In order to retrieve the values of the black and white pixels for that<br />
specific screen, we use two specific functions:<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AblackPixel blackPixel], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#BlackPixel BlackPixel], <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Display.html#v%3AwhitePixel whitePixel], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/display-macros.html#WhitePixel WhitePixel]<br />
<br />
<br />
The function createSimpleWindow will return the window ID and, with<br />
this ID, we can start manipulating our newly created window, as we do,<br />
in the above code, with the function<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AsetTextProperty setTextProperty], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/ICC/client-to-window-manager/XSetTextProperty.html XSetTextProperty()].<br />
<br />
This function is needed, in our code, to set the window's name, that<br />
your window manager will display on some decoration attached to the<br />
window (other window managers will not display anything, for instance<br />
a tiling WM like [http://xmonad.org XMonad])<br />
<br />
To set the window's name we need to manipulate the <br />
[http://www.tronche.com/gui/x/xlib/ICC/client-to-window-manager/converting-string-lists.html XTextProperty structure]. <br />
<br />
Properties, such as the XTextProperty, have a string name and a<br />
numerical identifier called an atom. An atom is an ID that uniquely<br />
identifies a particular property. Property name strings are typically<br />
all upper case - with the first letter in low case when translated<br />
into Haskell - with words separated by underscores. In our example we<br />
had to set the WM_NAME property to "Hello World".<br />
<br />
Creating and manipulating a window is just the first step to have a<br />
new window displayed. In order for the window to become visible we<br />
must map it with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AmapWindow mapWindow], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/window/XMapWindow.html XMapWindow()]. <br />
This will make the window visible.<br />
<br />
Xlib will not send requests and calls to the Xserver immediately, but<br />
will buffer them and send the full buffer when some given conditions<br />
are met.<br />
<br />
One way to force the flushing of the output buffer is to call <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Event.html#v%3Async sync], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/event-handling/XSync.html XSync()], <br />
which takes 2 arguments: the connection (dpy) and a Boolean value that<br />
indicates whether XSync() must discard all events on the event queue.<br />
<br />
After that the Xserver will eventually display our window. <br />
<br />
The rest of the above example does nothing else but blocking the<br />
program execution for 10 seconds (to let you stare at your newly<br />
created window) and then will exit.<br />
<br />
==Window's Attributes==<br />
<br />
Even though in our "Hello World" example we set the window's<br />
dimension, we have no assurance that the Window Manager will respect<br />
our decision.<br />
<br />
[http://xmonad.org XMonad], for instance, will just create a window<br />
with the dimensions needed to fill its tiled screen, no matter what<br />
you set in createSimpleWindow.<br />
<br />
But we decided to write a small clock that will behave as a status<br />
bar, that is to say, we want to create a window that will not be<br />
managed by a Window Manager.<br />
<br />
In order to achieve this result we need to start dealing with window's<br />
attributes.<br />
<br />
There are two ways of dealing with window's attributes: the first is<br />
to set them at window's creation time, but in that case<br />
createSimpleWindow is not powerful enough.<br />
<br />
The second way is to change window's attributes after the window's has<br />
been created. This second approach is not implemented<br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11 X11] but<br />
has been implemented in the darcs version of <br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras].<br />
<br />
===Setting Window's Attribute at Creation Time===<br />
<br />
In order to set window's attributes at creation time, the window must be created with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Window.html#v%3AcreateWindow createWindow], <br />
the interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html XCreateWindow()].<br />
<br />
The type signature of this function is quite long:<br />
<br />
<haskell><br />
<br />
createWindow :: Display -> Window <br />
-> Position -> Position <br />
-> Dimension -> Dimension <br />
-> CInt <br />
-> CInt <br />
-> WindowClass <br />
-> Visual <br />
-> AttributeMask <br />
-> Ptr SetWindowAttributes <br />
-> IO Window<br />
<br />
</haskell><br />
<br />
That is to say:<br />
* the connection and the parent window<br />
* the x and y position (origins in the upper left corner of the inside border of the parent window)<br />
* width and height<br />
* border width<br />
* depth of screen<br />
* the window's class<br />
* the visual<br />
* the attribute mask<br />
* and the pointer to the [http://www.tronche.com/gui/x/xlib/window/attributes/#XSetWindowAttributes XSetWindowAttributes] foreign C structure.<br />
<br />
This last one gives you an idea of the type of operation we must do in<br />
order to create a window (createSimpleWindow is just a wrapper around<br />
this more complicated createWindow, with some default arguments): we<br />
need a function to allocate some memory for the creation of the<br />
foreign C structure, and then manipulate this foreign structure from<br />
within this function.<br />
<br />
The needed function is <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3AallocaSetWindowAttributes allocaSetWindowAttributes], <br />
whose type indeed is:<br />
<br />
<haskell><br />
<br />
allocaSetWindowAttributes :: (Ptr SetWindowAttributes -> IO a) -> IO a<br />
<br />
</haskell><br />
<br />
allocaSetWindowAttributes will take a function which takes the pointer<br />
to the foreign structure as its last argument. This function will<br />
perform an IO action that is the action returned by<br />
allocaSetWindowAttributes.<br />
<br />
In our case allocaSetWindowAttributes will take a function that will<br />
use createWindow to return the new window.<br />
<br />
So, we will need to use createWindow inside allocaSetWindowAttributes.<br />
We will soon see how. But first let's analyze the other arguments of<br />
createWindow.<br />
<br />
The display, the parent window, the coordinates and dimensions are the<br />
same as with createSimpleWindow. But now we must specify the depth of<br />
the screen, the window's class, the visual and the attribute mask. We<br />
also need to manipulate the XSetWindowAttribute after its creation by<br />
allocaSetWindowAttributes, before calling createWindow.<br />
<br />
«The depth is the number of bits available for each pixel to represent color (or gray scales). The visual represents the way pixel values are translated to produce color or monochrome output on the monitor.»( see [http://www.sbin.org/doc/Xlib/chapt_02.html http://www.sbin.org/doc/Xlib/chapt_02.html])<br />
<br />
For the depth we are going to use <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Screen.html#v%3AdefaultDepthOfScreen defaultDepthOfScreen], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/image-format-macros.html#DefaultDepthOfScreen XDefaultDepthOfScreen()], <br />
to retrieve the default screen depth.<br />
<br />
For the visual we are going to use<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Screen.html#v%3AdefaultVisualOfScreen defaultVisualOfScreen], <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/display/image-format-macros.html#DefaultVisualOfScreen DefaultVisualOfScreen].<br />
<br />
The <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3AWindowClass WindowClass] <br />
can either be copyFromParent, inputOutput, or inputOnly. In the first case the<br />
class is copied from the class of the parent window. An inputOnly<br />
window can only be used for receiving input events. In our code we are<br />
going to use inputOutput windows, windows that can receive input events<br />
and that can also be used to display some output.<br />
<br />
The attributeMask «specifies which window attributes are defined in<br />
the attributes argument. This mask is the bitwise inclusive OR of the<br />
valid attribute mask bits. If value mask is zero, the attributes are<br />
ignored and are not referenced.»<br />
(see [http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html http://www.tronche.com/gui/x/xlib/window/XCreateWindow.html]).<br />
<br />
In other words, in order to set more then one attribute, you need to pass a value mask such as:<br />
<br />
<haskell><br />
<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel .|. etc ...<br />
<br />
</haskell><br />
<br />
and set each of this attributes within allocaSetWindowAttributes with <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#12 specific attributes setting functions].<br />
<br />
Among this functions the one we need: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_override_redirect set_override_redirect], <br />
whose type is:<br />
<br />
<haskell><br />
<br />
set_override_redirect :: Ptr SetWindowAttributes -> Bool -> IO ()<br />
<br />
</haskell><br />
<br />
This function takes the pointer to the XSetWindowAttributes structure and the flag to be set (True or False).<br />
<br />
For the list of avaliable attributes see<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Types.html#t%3AAttributeMask the AttributeMask type defnition].<br />
<br />
For their meaning see <br />
[http://www.tronche.com/gui/x/xlib/window/attributes/#XSetWindowAttributes the XSetWindowAttributes structure reference].<br />
<br />
Now, our goal was to create a window that the Window Manager is going<br />
to ignore, and in order to do that all we need to set the attribute<br />
[http://www.tronche.com/gui/x/xlib/window/attributes/override-redirect.html CWOverrideRedirect] <br />
to True. And now we know how to do it.<br />
<br />
Ok, it's time to introduce our function to create new windows with the CWOverrideRedirect set to True<br />
<br />
<haskell><br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes True<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr) <br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
</haskell><br />
<br />
Like simpleCreateWindow, our function is a wrapper around<br />
createWindow, but this time we are manually setting the<br />
CWOverrideRedirect flag.<br />
<br />
As you see our function, unlike createSimpleWindow, does not have,<br />
among its arguments, the background and the border pixels. This colors<br />
can be set, for windows created with createWindow, using the attribute<br />
mask, and setting CWBackPixel and CWBorderPixel with the needed<br />
functions: <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_background_pixel set_background_pixel] <br />
and <br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Misc.html#v%3Aset_border_pixel set_border_pixel].<br />
<br />
Our function needs also the screen now, since we have to retrieve the<br />
default depth and visual.<br />
<br />
We can now rewrite our initial code using the new function now.<br />
<br />
<haskell><br />
<br />
module Main where<br />
import Data.Bits<br />
import Graphics.X11.Xlib<br />
import System.Exit (exitWith, ExitCode(..))<br />
import Control.Concurrent (threadDelay)<br />
<br />
main :: IO ()<br />
main =<br />
do dpy <- openDisplay ""<br />
let dflt = defaultScreen dpy<br />
scr = defaultScreenOfDisplay dpy<br />
rootw <- rootWindow dpy dflt<br />
win <- mkUnmanagedWindow dpy scr rootw 0 0 100 100<br />
setTextProperty dpy win "Hello World" wM_NAME <br />
mapWindow dpy win<br />
sync dpy False<br />
threadDelay (10 * 1000000)<br />
exitWith ExitSuccess<br />
<br />
mkUnmanagedWindow :: Display<br />
-> Screen<br />
-> Window<br />
-> Position<br />
-> Position<br />
-> Dimension<br />
-> Dimension<br />
-> IO Window<br />
mkUnmanagedWindow dpy scr rw x y w h = do<br />
let visual = defaultVisualOfScreen scr<br />
attrmask = cWOverrideRedirect .|. cWBorderPixel .|. cWBackPixel<br />
win <- allocaSetWindowAttributes $ <br />
\attributes -> do<br />
set_override_redirect attributes False<br />
set_background_pixel attributes $ whitePixel dpy (defaultScreen dpy)<br />
set_border_pixel attributes $ blackPixel dpy (defaultScreen dpy)<br />
createWindow dpy rw x y w h 0 (defaultDepthOfScreen scr)<br />
inputOutput visual attrmask attributes <br />
return win<br />
<br />
</haskell><br />
<br />
Ok, let's give it a try. Did you see? Now the window will be placed in<br />
the specified x and y position, with the given dimensions. No Window<br />
Manager decorations, and so, no name displayed.<br />
<br />
===Changing and Existing Window's Attributes===<br />
<br />
This task requires <br />
[http://www.tronche.com/gui/x/xlib/window/XChangeWindowAttributes.html XChangeWindowAttrbutes()], <br />
implemented only in the darcs version of <br />
[http://hackage.haskell.org/cgi-bin/hackage-scripts/package/X11-extras X11-extras].<br />
<br />
In order to change a window's attributes we just need the window ID in<br />
that specific X server, after that we need to unmap the window first,<br />
and then change its attributes with changeWindowAttributes, the<br />
interface to XChangeWindowAttrbutes() implemented by<br />
[http://darcs.haskell.org/~sjanssen/X11-extras the darcs version of X11-extras].<br />
<br />
Here's the code:<br />
<br />
<haskell><br />
<br />
module Main where<br />
<br />
import Graphics.X11.Xlib<br />
import Graphics.X11.Xlib.Extras<br />
import System.Environment<br />
<br />
usage :: String -> String<br />
usage n = "Usage: " ++ n ++ " manage/unmanage windowID"<br />
<br />
main :: IO ()<br />
main = do<br />
args <- getArgs<br />
pn <- getProgName<br />
let (win,ac) = case args of<br />
[] -> error $ usage pn<br />
w -> case (w !!0) of <br />
"manage" -> (window, False)<br />
"unmanage" -> (window, True)<br />
_ -> error $ usage pn<br />
where window = case (w !! 1) of <br />
[] -> error $ usage pn<br />
w -> read w :: Window<br />
dpy <- openDisplay ""<br />
unmapWindow dpy win<br />
sync dpy False<br />
allocaSetWindowAttributes $<br />
\attributes -> do<br />
set_override_redirect attributes ac<br />
changeWindowAttributes dpy win cWOverrideRedirect attributes<br />
mapWindow dpy win<br />
sync dpy False<br />
<br />
</haskell><br />
<br />
Save it as Unmanage.hs and compile with:<br />
ghc --make Unmanage.hs -o unmanage<br />
<br />
To use it you need to retrieve the window ID with the stand alone utility<br />
xwininfo<br />
<br />
Then you run the above code with:<br />
unmanage unmanage/manage windowID<br />
<br />
to set override_redirect to True or False.<br />
<br />
Obviously the important part of the code is this:<br />
<haskell><br />
dpy <- openDisplay ""<br />
unmapWindow dpy win<br />
sync dpy False<br />
allocaSetWindowAttributes $<br />
\attributes -> do<br />
set_override_redirect attributes ac<br />
changeWindowAttributes dpy win cWOverrideRedirect attributes<br />
mapWindow dpy win<br />
sync dpy False<br />
</haskell><br />
<br />
where we:<br />
# connect to the X server<br />
# unmap the window<br />
# flush the output buffer to have the X server actually unmap the window<br />
# change the attributes with the same procedure we used to set them when creating the window<br />
# map the window again<br />
# flush the output buffer to see the change take effect.<br />
<br />
You can modify this program to change other window's attributes.<br />
<br />
==Colors and Color Depth==<br />
<br />
'''To be continued ...'''<br />
<br />
[http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib-Color.html#v%3AallocNamedColor allocNamedColor] <br />
interface to the X11 library function <br />
[http://www.tronche.com/gui/x/xlib/color/XAllocNamedColor.html XAllocNamedColor()]<br />
<br />
allocNamedColor :: Display -> Colormap -> String -> IO (Color, Color)<br />
<br />
data Color:<br />
http://hackage.haskell.org/packages/archive/X11/1.2.2/doc/html/Graphics-X11-Xlib.html#v%3AColor<br />
<br />
<br />
==Printing a String==<br />
<br />
fonts, string length, colors, etc<br />
<br />
==Updating a Window==<br />
<br />
Pixmap and copyToWindow<br />
<br />
==Dealing with XEvents==<br />
<br />
Two approaches<br />
<br />
- [[User:AndreaRossato|Andrea Rossato]]<br />
<br />
[[Category:Tutorials]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=A_brief_introduction_to_Haskell&diff=7446A brief introduction to Haskell2006-10-27T14:16:04Z<p>AndreaRossato: corrected a tag</p>
<hr />
<div>[[Category:Tutorials]]<br />
<br />
[http://haskell.org Haskell] is:<br />
<br />
* A language [http://haskell.org/haskellwiki/History_of_Haskell developed] by the programming languages [http://haskell.org/haskellwiki/Research_papers research] community.<br />
* Is a lazy, purely functional language (that also has imperative features such as side effects and mutable state, along with strict evaluation)<br />
* Born as an open source vehicle for programming language research<br />
* One of the youngest children of ML and Lisp<br />
* Particularly useful for programs that manipulate data structures (such as [http://haskell.org/ghc compilers] and [http://pugscode.org interpreters]), and for concurrent/parallel programming<br />
<br />
Inspired by the article [http://www.cs.jhu.edu/~scott/pl/lectures/caml-intro.html Introduction to OCaml], and translated from the OCaml by Don Stewart.<br />
<br />
== Background ==<br />
<br />
[http://haskell.org/haskellwiki/Old_news#Archives_by_year History]:<br />
* ''1990''. Haskell 1.0<br />
* ''1991''. Haskell 1.1<br />
* ''1993''. Haskell 1.2<br />
* ''1996''. Haskell 1.3<br />
* ''1997''. Haskell 1.4<br />
* ''1998''. Haskell 98<br />
* ''2000-2006''. Period of rapid language and community growth<br />
* ''~2007''. Haskell Prime<br />
<br />
[http://haskell.org/haskellwiki/Implementations Implementations]:<br />
* [http://haskell.org/ghc GHC]<br />
* [http://haskell.org/hugs Hugs]<br />
* [http://haskell.org/haskellwiki/Yhc YHC]<br />
* [http://repetae.net/computer/jhc/JHC JHC]<br />
* [http://haskell.org/nhc98 nhc98]<br />
* [http://www.cs.uu.nl/helium/ Helium]<br />
* [http://www.cs.chalmers.se/~augustss/hbc/hbc.html HBC]<br />
<br />
== Haskell features ==<br />
<br />
Has some novel features relative to Java (and C++).<br />
<br />
* Immutable variables by default (mutable state programmed via monads)<br />
* Pure by default (side effects are programmed via monads)<br />
* Lazy evaluation: results are only computed if they're required (strictness optional)<br />
* Everything is an expression<br />
* First-class functions: functions can be defined anywhere, passed as arguments, and returned as values.<br />
* Both compiled and interpreted implementations available<br />
* Full type inference -- type declarations optional<br />
* Pattern matching on data structures -- data structures are first class!<br />
* Parametric polymorphism<br />
* Bounded parametric polymorphism<br />
<br />
These are all conceptually more [http://haskell.org/haskellwiki/Research_papers/Type_systems advanced ideas].<br />
<br />
Compared to similar functional languages, Haskell differs in that it<br />
has support for:<br />
<br />
* Lazy evaluation<br />
* Pure functions by default<br />
* Monadic side effects<br />
* Type classes<br />
* Syntax based on layout<br />
<br />
The GHC Haskell compiler, in particular, provides some interesting<br />
extensions:<br />
<br />
* Generalised algebraic data types<br />
* Impredicative types system<br />
* Software transactional memory<br />
* Parallel, SMP runtime system<br />
<br />
== The Basics ==<br />
<br />
Read the [http://haskell.org/onlinereport/ language definition] <br />
to supplement these notes. For more depth and examples<br />
[http://haskell.org/haskellwiki/Books_and_tutorials see the Haskell wiki].<br />
<br />
=== Interacting with the language ===<br />
<br />
Haskell is both compiled and interpreted. For exploration purposes,<br />
we'll consider [http://haskell.org/haskellwiki/Haskell_in_5_steps interacting with Haskell] via the GHCi interpreter:<br />
<br />
* expressions are entered at the prompt<br />
* newline signals end of input<br />
<br />
Here is a GHCi sessoin, starting from a UNIX prompt.<br />
<br />
$ ghci<br />
___ ___ _<br />
/ _ \ /\ /\/ __(_)<br />
/ /_\// /_/ / / | | GHC Interactive, version 6.4.2, for Haskell 98.<br />
/ /_\\/ __ / /___| | http://www.haskell.org/ghc/<br />
\____/\/ /_/\____/|_| Type :? for help.<br />
<br />
Loading package base-1.0 ... linking ... done.<br />
<br />
Here the incredibly simple Haskell program <hask>let x = 3+4</hask> is<br />
compiled and loaded, and available via the variable <hask>x</hask>.<br />
<br />
<haskell><br />
Prelude> let x = 3 + 4<br />
</haskell><br />
<br />
We can ask the system what type it automaticaly inferred for our<br />
variable. <hask>x :: Integer</hask> means that the variable<br />
<hask>x</hask> "has type" <hask>Integer</hask>, the type of unbounded<br />
integer values. <br />
<br />
<haskell><br />
Prelude> :t x<br />
x :: Integer<br />
</haskell><br />
<br />
A variable evaluates to its value.<br />
<br />
<haskell><br />
Prelude> x<br />
7<br />
</haskell><br />
<br />
The variable <hask>x</hask> is in scope, so we can reuse it in later<br />
expressions.<br />
<br />
<haskell><br />
Prelude> x + 4<br />
11<br />
</haskell><br />
<br />
Local variables may be bound using <hask>let</hask>, which declares a<br />
new binding for a variable with local scope. <br />
<br />
<haskell><br />
Prelude> let x = 4 in x + 3<br />
7<br />
</haskell><br />
<br />
Alternatively, declarations<br />
typed in at the top level are like an open-ended let:<br />
<br />
<haskell><br />
Prelude> let x = 4<br />
Prelude> let y = x + 3<br />
Prelude> x * x<br />
16<br />
<br />
Prelude> :t x<br />
x :: Integer<br />
Prelude> :t y<br />
y :: Integer<br />
Prelude> :t x * x<br />
x * x :: Integer<br />
</haskell><br />
<br />
Notice that ''type inference'' infers the correct type for all the<br />
expressions, without us having to ever specify the type explicitly.<br />
<br />
== Basic types ==<br />
<br />
There is a range of basic types, defined in the language [http://www.cse.unsw.edu.au/~dons/data/Prelude.html Prelude].<br />
<br />
<haskell><br />
Int -- bounded, word-sized integers<br />
Integer -- unbounded integers<br />
Double -- floating point values<br />
Char -- characters<br />
String -- strings<br />
() -- the unit type<br />
Bool -- booleans<br />
[a] -- lists<br />
(a,b) -- tuples / product types<br />
Either a b -- sum types<br />
Maybe a -- optional values<br />
</haskell><br />
<br />
For example:<br />
<haskell><br />
7<br />
12312412412412321<br />
3.1415<br />
'x'<br />
"haskell"<br />
()<br />
True, False<br />
[1,2,3,4,5]<br />
('x', 42)<br />
Left True, Right "string"<br />
Nothing, Just True<br />
</haskell><br />
<br />
These types have all the usual operations on them, in the<br />
[http://haskell.org/ghc/docs/latest/html/libraries/ standard libraries].<br />
<br />
=== Libraries ===<br />
<br />
* The [http://haskell.org/ghc/docs/latest/html/libraries/base/Prelude.html Prelude] contains the core operations on basic types. It is imported by default into every Haskell module. For example;<br />
<br />
<haskell><br />
+ - div mod && || not < > == /=<br />
</haskell><br />
<br />
Learn the Prelude well. Less basic functions are found in the [http://haskell.org/ghc/docs/latest/html/libraries/ standard libraries]. For data structures such as [http://haskell.org/ghc/docs/latest/html/libraries/base/Data-List.html List], [http://haskell.org/ghc/docs/latest/html/libraries/base/Data-Array.html Array] and [http://haskell.org/ghc/docs/latest/html/libraries/base/Data-Map.html finite maps].<br />
<br />
To use functions from these modules you have to import them, or in GHCi,<br />
refer to the qualified name, for example to use the toUpper function on<br />
Chars:<br />
<haskell><br />
Prelude> Char.toUpper 'x'<br />
'X'<br />
<br />
Prelude> :m + Char<br />
Prelude Char> toUpper 'y'<br />
'Y'<br />
</haskell><br />
<br />
In a source file, you have to import the module explicitly:<br />
<haskell><br />
import Char<br />
</haskell><br />
<br />
=== Overloading ===<br />
<br />
Haskell uses ''typeclasses'' to methodically allow overloading. A<br />
typeclass describes a set of functions, and any type which provides<br />
those functions can be made an ''instance'' of that type. This avoids<br />
the syntactic redundancy of languages like OCaml.<br />
<br />
For example, the function <hask>*</hask> is a method of the typeclass<br />
<hask>Num</hask>, as we can see from its type:<br />
<br />
<haskell><br />
Prelude> :t (*)<br />
(*) :: (Num a) => a -> a -> a<br />
</haskell><br />
<br />
Which can be read as "* is a polymorphic function, taking two values of some<br />
type 'a', and returning a result of the same type, where the type 'a' is a<br />
member of the class Num".<br />
<br />
This means that it will operate on any type in the <hask>Num</hask><br />
class, of which the following types are members: <hask>Double, Float,<br />
Int, Integer</hask>. Thus:<br />
<br />
<haskell><br />
Prelude> 2.3 * 5.7<br />
13.11<br />
</haskell><br />
<br />
or on integers:<br />
<br />
<haskell><br />
Prelude> 2 * 5<br />
10<br />
</haskell><br />
<br />
The type of the arguments determines which instance of <hask>*</hask> is<br />
used. Haskell also never performs implicit coercions, all coercions must<br />
be explicit. For example, if we try to multiply two different types,<br />
then the type check against <hask>* :: Num a => a -> a -> a</hask> will<br />
fail.<br />
<br />
<haskell><br />
Prelude> (2.3 :: Double) * (5 :: Int)<br />
<br />
<interactive>:1:19:<br />
Couldn't match `Double' against `Int'<br />
Expected type: Double<br />
Inferred type: Int<br />
In the expression: 5 :: Int<br />
In the second argument of `(*)', namely `(5 :: Int)'<br />
</haskell><br />
<br />
To convert 5 to a <hask>Double</hask> we'd write:<br />
<br />
<haskell><br />
Prelude> (2.3 :: Double) * fromIntegral (5 :: Int)<br />
11.5<br />
</haskell><br />
<br />
Why bother -- why not allow the system to implicitly coerce types?<br />
Implicit type conversions by the system are the source of innumerable<br />
hard to find bugs in languages that support them, and makes reasoning<br />
about a program harder, since you must apply not just the language's<br />
semantics, but an extra set of coercion rules.<br />
<br />
Note that If we leave off the type signatures however, Haskell will<br />
helpfully infer the most general type:<br />
<br />
<haskell><br />
Prelude> 2.3 * 5<br />
11.5<br />
</haskell><br />
<br />
=== Expressions ===<br />
<br />
In Haskell, expressions are everything. There are no pure "statements"<br />
like in Java/C++. For instance, in Haskell, <hask>if-then-else</hask> is a kind of expression, and results in a value based on the condition part.<br />
<br />
<haskell><br />
Prelude> (if 2 == 3 then 5 else 6) + 1<br />
7<br />
</haskell><br />
<br />
<haskell><br />
Prelude> (if 2 == 3 then 5 else 6.5) + 1<br />
7.5<br />
</haskell><br />
<br />
=== Local bindings ===<br />
<br />
In Haskell <hask>let</hask> allows local declarations to be made in the context of a single expression.<br />
<br />
<haskell><br />
let x = 1 + 2 in x + 3<br />
</haskell><br />
<br />
is analogous to:<br />
<br />
{<br />
int x = 1 + 2;<br />
... x + 3 ... ;<br />
}<br />
<br />
in C, but the Haskell variable x is given a value that is immutable (can<br />
never change).<br />
<br />
=== Allocation ===<br />
<br />
When you declare a new variable, Haskell automatically allocates that<br />
value for you -- no need to explicitly manage memory. The garbage<br />
collector will then collect any unreachable values once they go out of<br />
scope.<br />
<br />
Advanced users can also manage memory by hand using the foreign function<br />
interface.<br />
<br />
=== Lists ===<br />
<br />
Lists are ... lists of Haskell values. Defining a new list is trivial,<br />
easier than in Java.<br />
<br />
<haskell><br />
Prelude> [2, 1+2, 4]<br />
[2,3,4]<br />
<br />
Prelude> :t [2, 1+2, 4]<br />
[2, 1+2, 4] :: (Num a) => [a]<br />
</haskell><br />
<br />
This automatically allocates space for the list and puts in the elements.<br />
Haskell is garbage-collected like Java so no explicit de-allocation is<br />
needed. The type of the list is inferred automatically. All elements of<br />
a list must be of the same type. <br />
<br />
<haskell><br />
Prelude> ["e", concat ["f", "g"], "h"]<br />
["e","fg","h"]<br />
</haskell><br />
<br />
Notice how the function call <hask>concat ["f","g"]</hask> does not require <br />
parenthesis to delimit the function's arguments. Haskell<br />
uses whitespace, and not commas, and:<br />
<br />
* You don't need parentheses for function application in Haskell: <hask>sin 0.3</hask><br />
* Multiple arguments can be passed in one at a time (curried) which means they can be separated by spaces: <hask>max 3 4</hask>.<br />
<br />
Lists must be uniform in their type ("homogeneous").<br />
<br />
<haskell><br />
Prelude> ['x', True]<br />
<br />
Couldn't match `Char' against `Bool'<br />
</haskell><br />
<br />
Here we tried to build a list containing a Char and a Boolean, but the<br />
list ''constructor'', <hask>[]</hask>, has type: <br />
<br />
<haskell><br />
Prelude> :t []<br />
[] :: [a]<br />
</haskell><br />
<br />
meaning that all elements must be of the same type, <hask>a</hask>.<br />
(For those wondering how to build a list of heterogeneous values, you<br />
would use a ''sum type''):<br />
<br />
<haskell><br />
Prelude> [Left 'x', Right True]<br />
[Left 'x',Right True]<br />
<br />
Prelude> :t [Left 'x', Right True]<br />
[Left 'x', Right True] :: [Either Char Bool]<br />
</haskell><br />
<br />
List operations are numerous, as can be seen in the [http://haskell.org/ghc/docs/latest/html/libraries/base/Data-List.html Data.List library].<br />
<br />
<haskell><br />
Prelude> let x = [2,3]<br />
Prelude> let y = 1 : x -- 'cons' the value 1 onto the list<br />
Prelude> x -- the list is immutable<br />
[2,3]<br />
Prelude> y<br />
[1,2,3]<br />
Prelude> x ++ y -- joining lists<br />
[2,3,1,2,3]<br />
Prelude> head y -- first element of the list is the 'head'<br />
1<br />
Prelude> tail y -- the rest of the list is the 'tail'<br />
[2,3]<br />
</haskell><br />
<br />
=== Pattern matching ===<br />
<br />
Haskell supports pattern matching on data structures. This is a powerful<br />
language feature, making code that manipulates data structures<br />
incredibly simple. The core language feature for pattern matching is the<br />
<hask>case</hask> expression:<br />
<br />
<haskell><br />
Prelude> case x of h:t -> h<br />
2<br />
</haskell><br />
<br />
The <hask>case</hask> forces <hask>x</hask> (the scrutinee) to match<br />
pattern <hask>h:t</hask>, a list with head and tail, and then we extract<br />
the head, <hask>h</hask>. Tail is similar, and we can use a ''wildcard<br />
pattern'' to ignore the part of the pattern we don't care about:<br />
<br />
<haskell><br />
Prelude> case x of _:t -> t<br />
[3]<br />
</haskell><br />
<br />
=== Tuples ===<br />
<br />
Tuples are fixed length structures, whose fields may be of differing<br />
types ("heterogeneous"). They are known as ''product types'' in<br />
programming language theory.<br />
<br />
<haskell><br />
Prelude> let x = (2, "hi")<br />
Prelude> case x of (y,_) -> y<br />
2<br />
</haskell><br />
<br />
Unlike the ML family of languages, Haskell uses the same syntax for the<br />
value level as on the type level. So the type of the above tuple is:<br />
<br />
<haskell><br />
Prelude> :t x<br />
x :: (Integer, [Char])<br />
</haskell><br />
<br />
All the data mentioned so far are immutable - it is impossible to change<br />
an entry in an existing list, tuple, or record without implicitly<br />
copying the data! Also, all variables are immutable.<br />
By default Haskell is a ''pure'' language. Side effects, such as<br />
mutation, are discussed later.<br />
<br />
== Functions ==<br />
<br />
Here is a simple recursive factorial function definition.<br />
<br />
<haskell><br />
Prelude> let fac n = if n == 0 then 1 else n * fac (n-1)<br />
<br />
Prelude> :t fac<br />
fac :: (Num a) => a -> a<br />
<br />
Prelude> fac 42<br />
1405006117752879898543142606244511569936384000000000<br />
</haskell><br />
<br />
The function name is <hask>fac</hask>, and the argument is<br />
<hask>n</hask>. This function is recursive (and there is no need to<br />
specially tag it as such, as you would in the ML family of languages).<br />
<br />
When you ''apply'' (or invoke) the fac function, you don't need any<br />
special parenthesis around the code. Note that there is no return<br />
statement; instead, the value of the whole body-expression is implicitly<br />
what gets returned.<br />
<br />
Functions of more than one argument may be defined:<br />
<br />
<haskell><br />
Prelude> let max a b = if a > b then a else b<br />
Prelude> max 3 7<br />
7<br />
</haskell><br />
<br />
Other important aspects of Haskell functions:<br />
<br />
* Functions can be defined anywhere in the code via ''lambda abstractions'':<br />
<br />
<haskell><br />
Prelude> ((\x -> x + 1) 4) + 7<br />
12<br />
</haskell><br />
<br />
Or, identical to <hask>let f x = x + 1</hask>:<br />
<haskell><br />
Prelude> let f = \x -> x + 1<br />
Prelude> :t f<br />
f :: Integer -> Integer<br />
</haskell><br />
<br />
Anonymous functions like this can be very useful. Also, functions can<br />
be passed to and returned from functions. For example, the ''higher order'' <br />
function <hask>map</hask>, applies its function argument to each<br />
element of a list (like a for-loop):<br />
<br />
<haskell><br />
Prelude> map (\x -> x ^ 2) [1..10]<br />
[1,4,9,16,25,36,49,64,81,100]<br />
</haskell><br />
<br />
In Haskell, we can use ''section'' syntax for more concise anonymous<br />
functions:<br />
<br />
<haskell><br />
Prelude> map (^ 2) [1..10]<br />
[1,4,9,16,25,36,49,64,81,100]<br />
</haskell><br />
<br />
Here <hask>map</hask> takes two arguments, the function <br />
<hask>(^2) :: Integer -> Integer</hask>, and a list of numbers.<br />
<br />
=== Currying ===<br />
<br />
Currying is a method by which function arguments may be passed one at a<br />
time to a function, rather than passing all arguments in one go in a<br />
structure:<br />
<br />
<haskell><br />
Prelude> let comb n m = if m == 0 || m == n then 1 else comb (n-1) m + comb (n-1) (m-1)<br />
<br />
Prelude> comb 10 4<br />
210<br />
</haskell><br />
<br />
The type of comb, <hask>Num a => a -> a -> a</hask>, can be rewritten as<br />
<hask>Num a => a -> (a -> a)</hask>. That is, it takes a single argument<br />
of some numeric type <hask>a</hask>, and returns a function that takes<br />
another argument of that type!<br />
<br />
Indeed, we can give comb only one argument, in which case it returns a<br />
function that we can later use:<br />
<br />
<haskell><br />
Prelude> let comb10 = comb 10<br />
Prelude> comb10 4<br />
210<br />
Prelude> comb10 3<br />
120<br />
</haskell><br />
<br />
Mutually recursive functions may defined in the same way as normal<br />
functions:<br />
<br />
<haskell><br />
let take [] = []<br />
(x:xs) = x : skip xs<br />
skip [] = []<br />
(_:ys) = take ys<br />
<br />
Prelude> :t take<br />
take :: [a] -> [a]<br />
<br />
Prelude> :t skip<br />
skip :: [a] -> [a]<br />
<br />
Prelude> take [1..10]<br />
[1,3,5,7,9]<br />
<br />
Prelude> skip [1..10]<br />
[2,4,6,8,10]<br />
</haskell><br />
<br />
This example also shows a pattern match with multiple cases, either<br />
empty list or nonempty list. More on patterns now.<br />
<br />
=== Patterns === <br />
<br />
Patterns make function definitions much more succinct, as we just saw.<br />
<br />
<haskell><br />
let rev [] = []<br />
rev (x:xs) = rev xs ++ [x]<br />
</haskell><br />
<br />
In this function definition, <hask>[]</hask> and <hask>(x:xs)</hask><br />
are patterns against which the value passed to the function is matched.<br />
The match occurs on the structure of the data -- that is, on its<br />
''constructors''.<br />
<br />
Lists are defined as:<br />
<br />
<haskell><br />
data [] a = [] | a : [a]<br />
</haskell><br />
<br />
That is, a list of some type a has type <hask>[a]</hask>, and it can be<br />
built two ways:<br />
* either the empty list, <hask>[]</hask><br />
* or an element ''consed'' onto a list, such as <hask>1 : []</hask> or <hask>1 : 2 : 3 : []</hask>. <br />
* For the special case of lists, Haskell provides the syntax sugar: <hask>[1,2,3]</hask> to build the same data.<br />
<br />
Thus, <hask>[]</hask> matches against the empty list constructor, and<br />
<hask>(x:xs)</hask>, match against the cons constructor, binding<br />
variables x and xs to the head and tail components of the list.<br />
<br />
Remember that <hask>case</hask> is the syntactic primitive for<br />
performing pattern matching (pattern matching in let bindings is sugar<br />
for <hask>case</hask>). Also, the first successful match is taken if<br />
more than one pattern matches:<br />
<br />
<haskell><br />
case [1,2,3] of<br />
(x:y) -> True<br />
(x:y:z) -> False<br />
[] -> True<br />
<br />
Warning: Pattern match(es) are overlapped<br />
In a case alternative: (x : y : z) -> ...<br />
<br />
True<br />
</haskell><br />
<br />
Warnings will be generated at compile time if patterns don't cover all<br />
possibilities, or contain redundant branches. <br />
<br />
<haskell><br />
Prelude> :set -Wall<br />
Prelude> case [1,2,3] of (x:_) -> x<br />
<br />
Warning: Pattern match(es) are non-exhaustive<br />
In a case alternative: Patterns not matched: []<br />
<br />
1<br />
</haskell><br />
<br />
An exception will be thrown at runtime if a pattern match fails:<br />
<br />
<haskell><br />
Prelude> let myhead (x:_) = x<br />
<br />
Warning: Pattern match(es) are non-exhaustive<br />
In a case alternative: Patterns not matched: []<br />
<br />
Prelude> myhead []<br />
*** Exception: <interactive>:1:16-36: Non-exhaustive patterns in case<br />
</haskell><br />
<br />
As we have seen, patterns may be used in function definitions. For<br />
example, this looks like a function of two arguments, but its a function<br />
of one argument which matches a pair pattern.<br />
<br />
<haskell><br />
Prelude> let add (x,y) = x + y<br />
Prelude> add (2,3)<br />
5<br />
</haskell><br />
<br />
=== Immutable declarations ===<br />
<br />
Immutable Declarations<br />
<br />
* Important feature of let-defined variable values in Haskell (and some other functional languages): they cannot change their value later.<br />
* Greatly helps in reasoning about programs---we know the variable's value is fixed.<br />
* Smalltalk also forces method arguments to be immutable; C++'s const and Java's final on fields has a similar effect. <br />
<br />
<haskell><br />
let x = 5 in<br />
let f y = x + 1 in<br />
let x = 7 in f 0 -- f uses the particular x in lexical scope where it is defined <br />
6<br />
</haskell><br />
<br />
Here's the one that will mess with your mind: the same thing as above<br />
but with the declarations typed into GHCi. (The GHCi environment<br />
conceptually an open-ended series of lets which never close).<br />
<br />
<haskell><br />
Prelude> let x = 5<br />
Prelude> let f y = x + 1<br />
Prelude> f 0<br />
6<br />
Prelude> let x = 7 -- not an assignment, a new declaration<br />
Prelude> f 0<br />
6<br />
</haskell><br />
<br />
=== Higher order functions ===<br />
<br />
Haskell, like ML, makes wide use of higher-order functions: functions<br />
that either take other functions as argument or return functions as<br />
results, or both. Higher-order functions are an important component of a<br />
programmer's toolkit.<br />
<br />
* They allow "pluggable" programming by passing in and out chunks of code.<br />
* Many new programming design patterns are possible.<br />
* It greatly increases the reusability of code.<br />
* [http://www.cs.kent.ac.uk/pubs/1997/224/index.html Higher-order + Polymorphic = Reusable]<br />
<br />
The classic example of a function that takes another function as<br />
argument is the <hask>map</hask> function on lists. It takes a list and<br />
a function and applies the function to every element of the list.<br />
<br />
<haskell><br />
map :: (a -> b) -> [a] -> [b]<br />
map _ [] = []<br />
map f (x:xs) = f x : map f xs<br />
</haskell><br />
<br />
The lower case variables in the type declaration of map are ''type<br />
variables'', meaning that the function is ''polymorphic'' in that<br />
argument (can take any type).<br />
<br />
<haskell><br />
Prelude> map (*10) [4,2,7]<br />
[40,20,70]<br />
</haskell><br />
<br />
Perhaps the simplest higher-order function is the composer, in<br />
mathematics expressed as ''g o f''. it takes two functions and returns a<br />
new function which is their composition:<br />
<br />
<haskell><br />
(.) :: (b -> c) -> (a -> b) -> a -> c<br />
(.) f g x = f (g x)<br />
</haskell><br />
<br />
This function takes three arguments: two functions, ''f'' and ''g'', and<br />
a value, ''x''. It then applies ''g'' to ''x'', before applying ''f'' to<br />
the result. For example:<br />
<br />
<haskell><br />
Prelude> let plus3times2 = (*2) . (+3)<br />
Prelude> plus3times2 10<br />
26<br />
</haskell><br />
<br />
As we have seen before, functions are just expressions so can also be<br />
immediately applied after being defined:<br />
<br />
<haskell><br />
Prelude> ((*2) . (+3)) 10<br />
26<br />
Prelude> (.) (*2) (+3) 10<br />
26<br />
</haskell><br />
<br />
Note how Haskell allows the infix function ''.'' to be used in prefix<br />
form, when wrapped in parenthesis. <br />
<br />
=== Currying ===<br />
<br />
Currying is an important concept of functional programming; it is named<br />
after logician [http://haskell.org/haskellwiki/Haskell_Brooks_Curry Haskell Curry], <br />
after which the languages Haskell and Curry are named! Multi-argument<br />
functions as defined thus far are curried, lets look at what is really<br />
happening.<br />
<br />
Here is a two-argument function defined in our usual manner.<br />
<br />
<haskell><br />
Prelude> let myadd x y = x + y<br />
Prelude> myadd 3 4<br />
7<br />
</haskell><br />
<br />
Here is another completely equivalent way to define the same function:<br />
<br />
<haskell><br />
Prelude> let myadd x = \y -> x + y<br />
Prelude> :t myadd<br />
myadd :: (Num a) => a -> a -> a<br />
<br />
Prelude> myadd 3 4<br />
7<br />
Prelude> let inc3 = myadd 3<br />
Prelude> inc3 4 <br />
7<br />
</haskell><br />
<br />
The main observation is myadd is a function returning a function, so the<br />
way we supply two arguments is<br />
* Invoke the function, get a function back<br />
* Then invoke the returned function passing the second argument.<br />
* Our final value is returned, victory.<br />
* <hask>(myadd 3) 4</hask> is an inlined version of this where the function returned by <hask>myadd 3</hask> is not put in any variable <br />
<br />
Here is a third equivalent way to define myadd, as an anonymous function<br />
returning another anonymous function.<br />
<br />
<haskell><br />
Prelude> let myadd = \x -> \y -> x + y<br />
Prelude> :t myadd<br />
myadd :: Integer -> Integer -> Integer<br />
</haskell><br />
<br />
With currying, all functions "really" take exactly one argument.<br />
Currying also naturally arises when functions return functions, as in<br />
the map application above showed. Multiple-argument functions should<br />
always be written in curried form; all the library functions are<br />
curried.<br />
<br />
Note thus far we have curried only two-argument functions; in general,<br />
n-argument currying is possible. Functions can also take pairs as<br />
arguments to achieve the effect of a two-argument function:<br />
<br />
<haskell><br />
Prelude> let mypairadd (x,y) = x + y <br />
Prelude> mypairadd (2,3)<br />
5<br />
</haskell><br />
<br />
So, either we can curry or we can pass a pair. We can also write<br />
higher-order functions to switch back and forth between the two forms.<br />
<br />
<haskell><br />
fst :: (a,b) -> a<br />
fst (x,_) = x<br />
<br />
snd :: (a,b) -> b<br />
snd (_,y) = y<br />
<br />
curry :: ((a, b) -> c) -> a -> b -> c<br />
curry f x y = f (x, y)<br />
<br />
uncurry :: (a -> b -> c) -> ((a, b) -> c)<br />
uncurry f p = f (fst p) (snd p)<br />
<br />
Prelude> :t uncurry myadd<br />
uncurry myadd :: (Integer, Integer) -> Integer<br />
<br />
Prelude> :t curry mypairadd<br />
curry mypairadd :: Integer -> Integer -> Integer<br />
<br />
Prelude> :t uncurry map<br />
uncurry map :: (a -> b, [a]) -> [b]<br />
<br />
Prelude> :t curry (uncurry myadd) -- a no-op<br />
curry (uncurry myadd) :: Integer -> Integer -> Integer<br />
</haskell><br />
<br />
Look at the types: these mappings in both directions in some sense<br />
"implement" the well-known isomorphism on sets: <hask>A * B -> C = A -> B -> C</hask><br />
<br />
=== A bigger example ===<br />
<br />
Here is a more high-powered example of the use of currying.<br />
<br />
<haskell><br />
foldr f [] y = y<br />
foldr f (x:xs) y = f x (foldr f xs y)<br />
<br />
*Main> :t foldr<br />
foldr :: (a -> t -> t) -> [a] -> t -> t<br />
<br />
*Main> let prod = foldr (\a x -> a * x)<br />
*Main> :t prod<br />
prod :: [Integer] -> Integer -> Integer<br />
<br />
*Main> let prod0 = prod [1,2,3,4]<br />
*Main> :t prod0<br />
prod0 :: Integer -> Integer<br />
<br />
*Main> (prod0 1, prod0 2)<br />
(24,48)<br />
</haskell><br />
<br />
Here is an analysis of this recursive function, for the arbitrary<br />
2-element list [x1,x2], the call<br />
<br />
<haskell><br />
foldr f [x1, x2] y<br />
</haskell><br />
<br />
''reduces'' to (by inlining the body of fold):<br />
<br />
<haskell><br />
f x1 (foldr f [x2] y)<br />
</haskell><br />
<br />
which in turn reduces to:<br />
<br />
<haskell><br />
f x1 (f x2 (foldr f [] y)))<br />
</haskell><br />
<br />
and then:<br />
<br />
<haskell><br />
f x1 (f x2 y)<br />
</haskell><br />
<br />
From this we can assert that the general result returned from <br />
<hask>foldr f [x1,x2,...,xn] y</hask> is <hask>f x1 (f x2 f ...(f xn<br />
y)...))))</hask>. Currying allows us to specialize foldr to a<br />
particular function f, as with prod above.<br />
<br />
=== Proving program properties by induction ===<br />
<br />
We should in fact be able to prove this property by induction. Its<br />
easier if we reverse the numbering of the list.<br />
<br />
'''Lemma'''. <hask>foldr f [xn,...,x1] y</hask> evaluates to <br />
<hask>f xn (f xn-1 f ...(f x1 y)...)))</hask> for n greater than 0.<br />
<br />
'''Proof'''. Proceed by induction on the length of the list<br />
<hask>[xn,..,x1]</hask>.<br />
<br />
''Base Case'' n=1, i.e. the list is [x1]. The function reduces to <br />
<hask>f x1 (foldr f [] y)</hask> which reduces to <hask>f x1 y</hask> as<br />
hypothesized.<br />
<br />
''Induction Step''. Assume<br />
<br />
<haskell><br />
foldr f [xn,...,x1] y<br />
</haskell><br />
<br />
reduces to<br />
<br />
<haskell><br />
f xn (f xn-1 f ...(f x1 y)...)))<br />
</haskell><br />
<br />
and show<br />
<br />
<haskell><br />
foldr f [xn+1, xn,...,x1] y<br />
</haskell><br />
<br />
reduces to<br />
<br />
<haskell><br />
f xn+1 (f xn f ...(f x1 y)...))))<br />
</haskell><br />
<br />
Computing<br />
<br />
<haskell><br />
foldr f [x1,x2,...,xn,xn+1] y<br />
</haskell><br />
<br />
it matches the pattern with x being xn+1 and xs being<br />
<hask>[xn,...,x1]</hask>. Thus the recursive call is<br />
<br />
<haskell><br />
foldr f [xn,...,x1] y<br />
</haskell><br />
<br />
which by our inductive assumption reduces to<br />
<br />
<haskell><br />
f xn (f xn-1 f ...(f x1 y)...)))<br />
</haskell><br />
<br />
And, given this result for the recursive call, the whole function then<br />
returns<br />
<br />
<haskell><br />
f xn+1 (...result of recursive call...)<br />
</haskell><br />
<br />
which is<br />
<br />
<haskell><br />
f xn+1 (f xn (f xn-1 f ...(f x1 y)...)))<br />
</haskell><br />
<br />
which is what we needed to show. '''QED'''<br />
<br />
The above implementation is inefficient in that ''f'' is explicitly passed to<br />
every recursive call. Here is a more efficient version with identical<br />
functionality.<br />
<br />
<haskell><br />
foldr :: (a -> b -> b) -> b -> [a] -> b<br />
foldr k z xs = go xs<br />
where<br />
go [] = z<br />
go (y:ys) = y `k` go ys<br />
</haskell><br />
<br />
This function also illustrates how functions may be defined in a local scope,<br />
using <hask>where</hask>. Observe 'go' is defined locally but then exported<br />
since it is the return value of f.<br />
<br />
Question: How does the return value 'go' know where to look for k when its called?? <br />
<br />
<haskell><br />
Prelude> let summate = foldr (+)<br />
Prelude> summate [1,2,3,4] 0<br />
10<br />
</haskell><br />
<br />
<hask>summate</hask> is just <hask>go</hask> but somehow it "knows" that k is<br />
<hask>(+)</hask>, even though k is undefined at the top level:<br />
<br />
<haskell><br />
Prelude> k<br />
<interactive>:1:0: Not in scope: `k'<br />
</haskell><br />
<br />
<hask>go</hask> in fact knew the right k to call, so it must have been kept<br />
somewhere: in a ''closure''. At function definition point, the current values<br />
of variables not local to the function definition are remembered in a structure<br />
called a closure. Function values in Haskell are thus really a pair consisting<br />
of the function (pointer) and the local environment, in a closure.<br />
<br />
Without making a closure, higher-order functions will do awkward things (such<br />
as binding to whatever 'k' happens to be in scope). Java, C++, C can pass and<br />
return function (pointers), but all functions are defined at the top level so<br />
they have no closures. <br />
<br />
=== Loading source from a file ===<br />
<br />
You should never type large amounts of code directly into GHCi! Its impossible<br />
to fix errors. Instead, you should edit in a file. Usingg any editor, save each<br />
group of interlinked functions in a separate file, for example "A.hs". Then,<br />
from GHCi type:<br />
<br />
<haskell><br />
Prelude> :l A.hs<br />
*Main><br />
</haskell><br />
<br />
This will compile everything in the file.<br />
<br />
=== Show ===<br />
<br />
Haskell has the show function.<br />
<br />
<haskell><br />
*Main> show 1<br />
"1"<br />
*Main> show (1,2,'x')<br />
"(1,2,'x')"<br />
*Main> show "haskell"<br />
"\"haskell\""<br />
*Main> show (Just ())<br />
"Just ()"<br />
</haskell><br />
<br />
It simply returns a string representation for its arguments.<br />
<br />
== Types ==<br />
<br />
We have generally been ignoring the type system of Haskell up to now. Its time<br />
to focus on typing in more detail.<br />
<br />
=== Type Declarations ===<br />
<br />
Haskell infers types for you, but you can add explicit type declarations if you like.<br />
<br />
<haskell><br />
let myadd :: Int -> Int -> Int<br />
myadd x y = x + y<br />
<br />
*Main> :t myadd<br />
myadd :: Int -> Int -> Int<br />
<br />
*Main> :set -fglasgow-exts<br />
let myadd (x :: Int) (y :: Int) = x + y<br />
<br />
*Main> :t myadd<br />
myadd :: Int -> Int -> Int<br />
</haskell><br />
<br />
You can in fact put type assertions on any variable in an expression to clarify<br />
what type the variable has:<br />
<br />
<haskell><br />
*Main> let myadd (x :: Int) (y :: Int) = (x :: Int) + y<br />
*Main> :t myadd<br />
myadd :: Int -> Int -> Int<br />
</haskell><br />
<br />
=== Type synonyms ===<br />
<br />
You can also make up your own name for any type. To do this, you must work in a<br />
separate file and load it into GHCi using the ":load A.hs" command.<br />
<br />
<haskell><br />
type IntPair = (Int,Int)<br />
<br />
f :: IntPair -> Int<br />
f (l,r) = l + r<br />
</haskell><br />
<br />
Working from GHCi:<br />
<br />
<haskell><br />
f :: IntPair -> Int<br />
*Main> :l A.hs<br />
*Main> :t f<br />
f :: IntPair -> Int<br />
*Main> f (2,3)<br />
5<br />
</haskell><br />
<br />
=== Polymorphic Types and Type Inference ===<br />
<br />
<haskell><br />
*Main> let id x = x<br />
*Main> :t id<br />
id :: forall t. t -> t<br />
*Main> id 3<br />
3<br />
*Main> id True<br />
True<br />
</haskell><br />
<br />
Since <hask>id</hask> was not used as any type in particular, the type of the<br />
function is polymorphic ("many forms").<br />
* <hask>t</hask> is a type variable, meaning it stands for some arbitrary type.<br />
* Polymorphism is really needed with type inference -- inferring <hask>Int -> Int</hask> would not be completely general. <br />
<br />
=== Parametric polymorphism ===<br />
<br />
The form of polymorphism in Haskell is to be precise, parametric polymorphism. The type above is parametric in ''t'': what comes out is the same type as what came in. ''Generics'' is another term for parametric polymorphism used in some communities.<br />
<br />
* Java has no parametric polymorphism, but does have object polymorphism (unfortunately this is often just called polymorphism by some writers) in that a subclass object can fit into a superclass-declared variable.<br />
* When you want parametric polymorphism in Java you declare the variable to be of type Object, but you have to cast when you get it out which requires a run-time check.<br />
* The Java JDK version 1.5 will have parametrically polymorphic types in it. <br />
<br />
The general intuition to have about the type inference algorithm is everything<br />
starts out as having arbitrary types, t, u, etc, but then the use of functions<br />
on values generates constraints that "this thing has the same type as that<br />
thing".<br />
<br />
Use of type-specific operators obviously restricts polymorphism:<br />
<br />
<haskell><br />
*Main> let doublenegate x = not (not x)<br />
*Main> :t doublenegate<br />
doublenegate :: Bool -> Bool<br />
</haskell><br />
<br />
When a function is defined via let to have polymorphic type, every use can be at a different type:<br />
<br />
<haskell><br />
let id x = x<br />
in case id True of<br />
True -> id 3<br />
False -> id 4<br />
3<br />
</haskell><br />
<br />
=== Algebraic Data Types ===<br />
<br />
Algebraic data types in Haskell are the analogue of union/variant types in<br />
C/Pascal. Following in the Haskell tradition of lists and tuples, they are not<br />
mutable. Haskell data types must be declared. Here is a really simple algebraic<br />
data type declaration to get warmed up, remember to write this in a separate<br />
file, and load it in to HHCi:<br />
<br />
<haskell><br />
data Height = Tall | Medium | Short<br />
</haskell><br />
<br />
Three constructors have been defined. These are now official constants.<br />
Constructors must be capitalized, and variables must be lower-case in Haskell.<br />
<br />
<haskell><br />
*Main> :l A.hs<br />
*Main> :t Tall<br />
Tall :: Height<br />
*Main> Tall<br />
<br />
Top level:<br />
No instance for (Show Height)<br />
</haskell><br />
<br />
So we can type check them, but can't show them yet. Let's ''derive'' the<br />
typeclass <hask>Show</hask> for our data type, which generates a 'show'<br />
function for our data type, which GHCi can then use to display the value.<br />
<br />
<haskell><br />
data Height = Tall | Medium | Short<br />
deriving Show<br />
<br />
*Main> :reload<br />
*Main> Tall<br />
Tall<br />
</haskell><br />
<br />
The previous type is only an enumerated type. Much more interesting data types<br />
can be defined. Remember the (recursive) list type:<br />
<br />
<haskell><br />
data [] a = [] | a : [a]<br />
<br />
*Main> (:) 3 ((:) (3+1) [])<br />
[3,4]<br />
*Main> 3 : 3+1 : []<br />
[3,4]<br />
</haskell><br />
<br />
This form of type has several new features:<br />
<br />
* As in C/Pascal, the data types can have values and they can be recursively defined, plus,<br />
* Polymorphic data types can be defined; ''a'' here is a type argument.<br />
* Note how there is no need to use pointers in defining recursive variant types. The compiler does all that mucking around for you.<br />
* Also note how <hask>(:)</hask>, the constructor, can be used as a function.<br />
<br />
We can define trees rather simply:<br />
<br />
<haskell><br />
data Tree a = Empty | Node a (Tree a) (Tree a)<br />
</haskell><br />
<br />
Patterns automatically work for new data types.<br />
<br />
=== Record Declarations ===<br />
<br />
Records are data types with labels on fields. They are very similar to structs<br />
of C/C++. Their types are declared just like normal data types, and can be used<br />
in pattern matches.<br />
<br />
<haskell><br />
data OneTwo = OneTwo { one :: Int, two :: String }<br />
deriving Show<br />
<br />
*Main> let x = OneTwo { one = 2 , two = "ni" }<br />
*Main> one x<br />
2<br />
*Main> case x of OneTwo { one = x, two = s } -> x<br />
2<br />
</haskell><br />
<br />
== Monads ==<br />
<br />
TODO, this is where Haskell and OCaml diverge. OCaml mixes pure and impure<br />
code, Haskell separates them statically, and keeps them apart, with monads. So<br />
we have to write this section from scratch.<br />
<br />
=== State ===<br />
<br />
The State monad<br />
<br />
=== Mutable references ===<br />
<br />
Data.STRef<br />
Data.IORef<br />
<br />
=== Mutable arrays ===<br />
<br />
Data.Array.MArray<br />
<br />
=== Exceptions ===<br />
<br />
Control.Exception<br />
<br />
=== Concurrency ===<br />
<br />
Control.Concurrent<br />
Control.Concurrent.MVar<br />
<br />
=== Monad transformers ===<br />
<br />
== Compilation ==<br />
<br />
You can easily compile your Haskell modules to standalone executables. The<br />
compiler, on unix systems, is '''ghc'''. For example, the file "A.hs",<br />
containing:<br />
<br />
<haskell><br />
main = print "Hello, World!"<br />
</haskell><br />
<br />
can be compiled and run as:<br />
<br />
$ ghc A.hs<br />
$ ./a.out <br />
"Hello, World!"<br />
<br />
For multiple modules, use the ''--make'' flag to GHC.</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=GNU/Linux&diff=5925GNU/Linux2006-09-13T08:56:06Z<p>AndreaRossato: new page with Debian and Slackware information</p>
<hr />
<div>==Debian GNU/Linux==<br />
[http://www.debian.org Debian Gnu/Linux] provides a very good and<br />
comprehensive toolchain for Haskell development.<br />
The most important packages are available in the<br />
[http://packages.debian.org/stable/devel/ stable],<br />
[http://packages.debian.org/testing/devel/ testing] and<br />
[http://packages.debian.org/unstable/devel/ unstable] trees.<br />
<br />
A mailing list for discussing the packaging of Haskell tools, libraries and programs<br />
on the Debian OS can be found at the following address:<br />
http://urchin.earth.li/mailman/listinfo/debian-haskell<br />
<br />
==Fedora==<br />
Write something please.<br />
<br />
==Slackware Linux==<br />
The official [http://www.slackware.come Slackware Linux] does not<br />
support Haskell at all. No packages are provided.<br />
<br />
An unofficial Slackware repository for Haskell tools, library and<br />
programs can be found at this address:<br />
<br />
http://gorgias.mine.nu/slack/<br />
<br />
This repository works with [http://software.jaos.org/#slapt-get slapt-get] <br />
and [http://swaret.sourceforge.net/index.php swaret].<br />
<br />
The SlackBuild scripts can be found at the following address:<br />
<br />
http://gorgias.mine.nu/repos/slackBuild/<br />
<br />
The scripts can be retrieved via darcs:<br />
darcs get http://gorgias.mine.nu/repos/slackBuild/<br />
<br />
==Suse==<br />
Write something please.<br />
<br />
==Ubuntu==<br />
Write something please.<br />
<br />
[[Category:OS]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=The_Monadic_Way/Part_I&diff=5920The Monadic Way/Part I2006-09-12T13:50:30Z<p>AndreaRossato: added monadic version with state and with state/output</p>
<hr />
<div>'''Note: this is the first part of [[The Monadic Way]]'''<br />
==An evaluation of Philip Wadler's "Monads for functional programming"==<br />
<br />
This tutorial is a "translation" of Philip Wadler's [http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf "Monads for functional programming"].<br />
(avail. from [http://homepages.inf.ed.ac.uk/wadler/topics/monads.html here])<br />
<br />
I'm a Haskell newbie trying to grasp such a difficult concept as the<br />
one of Monad and monadic computation.<br />
<br />
While [http://www.cs.utah.edu/~hal/htut/ "Yet Another Haskell Tutorial"] <br />
gave me a good understanding of the type system when it<br />
comes to monads I find it almost unreadable.<br />
<br />
But I had also Wadler's paper, and started reading it. Well, just<br />
wonderful! It explains how to ''create'' a monad!<br />
<br />
So I decided to "translate it", in order to clarify to myself the<br />
topic. And I'm now sharing this traslation ('''not completed yet'')<br />
with the hope it will be useful to someone else.<br />
<br />
Moreover, that's a wiki, so please improve it. And, specifically,<br />
correct my poor English. I'm Italian, after all.<br />
<br />
'''Note: The source of this page can be used as a Literate Haskell<br />
file and can be run with ghci or hugs: so cut paste change and run (in<br />
emacs for instance) while reading it...'''<br />
<br />
==A Simple Evaluator==<br />
<br />
Let's start with something simple: suppose we want to implement a new<br />
programming language. We just finished with<br />
[http://swiss.csail.mit.edu/classes/6.001/abelson-sussman-lectures/ Abelson and Sussman's Structure and Interpretation of Computer Programs] <br />
and we want to test what we have learned.<br />
<br />
Our programming language will be very simple: it will just compute the<br />
sum of two terms.<br />
<br />
So we have just one primitive operation (Add) that takes two constants<br />
and calculates their sum.<br />
<br />
Moreover we have just one kind of data type: Con a, which is an Int.<br />
<br />
For instance, something like:<br />
<br />
(Add (Con 5) (Con 6))<br />
<br />
should yeld:<br />
<br />
11<br />
<br />
===The basic evaluator===<br />
<br />
We will implement our language with the help of a data type<br />
constructor such as:<br />
<br />
<div id="BasicEval"><br />
<haskell><br />
<br />
> module TheMonadicWay where<br />
> data Term = Con Int<br />
> | Add Term Term<br />
> deriving (Show)<br />
<br />
</haskell><br />
<br />
After that we build our interpreter:<br />
<br />
<haskell><br />
<br />
> eval :: Term -> Int<br />
> eval (Con a) = a<br />
> eval (Add a b) = eval a + eval b<br />
<br />
</haskell><br />
<br />
That's it. Just an example:<br />
<br />
*TheMonadicWay> eval (Add (Con 5) (Con 6))<br />
11<br />
*TheMonadicWay><br />
<br />
Very very simple. The evaluator checks if its argument is of type Con<br />
Int: if it is it just returns the Int.<br />
<br />
If the argument is not of type Con, but it is of type Term, it<br />
evaluates the first Term and sums the result with the result of the<br />
evaluation of the second Term.<br />
<br />
As you may understand, our evaluator uses some of the powerful<br />
features of Haskell type system. Instead of writing a parser that<br />
takes a string (the user input) and transforms that string into an<br />
expression to be evaluated, we use the two type constructors defined<br />
for our data type Term (Con and Add) to build the expression - such as<br />
(Add (Con 5) (Con 6)) - and to match the expression's elements in our<br />
"eval" function.<br />
<br />
<br />
== Some Output, Please!==<br />
<br />
Now, that's fine, but we'd like to add some features, like providing<br />
some output, to show how the computation was carried out.<br />
<br />
Well, but Haskell is a pure functional language, with no side effects,<br />
we were told.<br />
<br />
Now we seem to be wanting to create a side effect of the computation,<br />
its output, and be able to stare at it...<br />
<br />
If we had some global variable to store the out that would be<br />
simple...<br />
<br />
But we can create the output and carry it along the computation,<br />
concatenating it with the old one, and present it at the end of the<br />
evaluation together with the evaluation of the expression given to our<br />
evaluator/interpreter!<br />
<br />
===The basic evaluator with output===<br />
<br />
Simple and neat:<br />
<div id="BasivEvalO"><br />
<haskell><br />
<br />
> type MOut a = (a, Output)<br />
> type Output = String<br />
> <br />
> formatLine :: Term -> Int -> Output<br />
> formatLine t a = "eval (" ++ show t ++ ") <= " ++ show a ++ " - " <br />
> <br />
> evalO :: Term -> MOut Int<br />
> evalO (Con a) = (a, formatLine (Con a) a)<br />
> evalO (Add t u) = ((a + b),(x ++ y ++ formatLine (Add t u) (a + b)))<br />
> where (a, x) = evalO t<br />
> (b, y) = evalO u<br />
<br />
</haskell><br />
<br />
Now we have what we want. But we had to change our evaluator quite a<br />
bit. <br />
<br />
First we added a function, formatLine, that takes an argument of type<br />
Term (the expression to be evaluated), one of type Int (the result of<br />
the evaluation of Term) and gives back an output of type Output (that<br />
is a synonymous of String). This is just a helper function to format<br />
the string to output. Not very interesting at all.<br />
<br />
The evaluator itself changed quite a lot! Now it has a different type<br />
signature: it takes an argument of type Term and produces a new type,<br />
we called it MOut, that is actually a compound pair of a variable type<br />
a (an Int in our evaluator) and a type Output, a string.<br />
<br />
So our evaluator, now, will take a Term (the type of the expressions<br />
in our new programming language) and will produce a pair, composed of<br />
the result of the evaluation (an Int) and the Output, a string.<br />
<br />
So far so good. But what's happening inside the evaluator?<br />
<br />
The first part will just return a pair with the number evaluated ("a")<br />
and the output formatted by formatLine.<br />
<br />
The second part does something more complicated: it returns a pair<br />
composed by <br />
1. the result of the evaluation of the right Term summed to the result<br />
of the evaluation of the second Term<br />
2. the output: the concatenation of the output produced by the<br />
evaluation of the right Term, the output produced by the evaluation of<br />
the left Term (each this evaluation returns a pair with the number and<br />
the output), and the formatted output of the evaluation.<br />
<br />
Let's try it:<br />
*TheMonadicWay> evalO (Add (Con 5) (Con 6))<br />
(11,"eval (Con 5) <= 5 - eval (Con 6) <= 6 - eval (Add (Con 5) (Con 6)) <= 11 - ")<br />
*TheMonadicWay><br />
<br />
It works! Let's put the output this way:<br />
eval (Con 5) <= 5 - <br />
eval (Con 6) <= 6 - <br />
eval (Add (Con 5) (Con 6)) <= 11 -<br />
<br />
Great! We are able to produce a side effect of our evaluation and<br />
present it at the end of the computation, after all.<br />
<br />
Let's have a closer look at this expression:<br />
<haskell><br />
<br />
evalO (Add t u) = ((a + b),(x ++ y ++ formatLine (Add t u) (a + b)))<br />
where (a, x) = evalO t<br />
(b, y) = evalO u<br />
<br />
</haskell><br />
<br />
Why all that? The problem is that we need:<br />
* "a" and "b" to calculate their sum (a + b), that will be the first element of the compund pair rapresenting the type (MOut) our evaluator will return <br />
* "x and "y" (the output of each evaluation) to be concatenated with the ourput of formatLine by the expression (x ++ y ++ formatLine(...)): this will be the second element of the compound pair MOut, the string part.<br />
<br />
So we need to separate the pairs produced by "evalO t" and "evalO u".<br />
<br />
We do that within the where clause (remember: evalO now produces a value of type<br />
MOut Int, i.e. a pair of an Int and a String).<br />
<br />
Then we use the single element, "extraded" within the where clause, to<br />
return a new MOut composed by <br />
<br />
((a + b),(x ++ y ++ formatLine (Add t u) (a + b))).<br />
------ -------------------------------------<br />
Int Output = String<br />
<br />
== Let's Go Monadic==<br />
<br />
Is there a more general way of doing so?<br />
<br />
Let's analyze the evaluator from another perspective. From the type<br />
perspective.<br />
<br />
We solved our problem by creating a new type, a pair of an Int (the<br />
result of the evaluation) and a String (the output of the process of<br />
evaluation).<br />
<br />
The first part of the evaluator does nothing else but creating, from a<br />
value of type Int, an object of type MOut Int (Int,Output). It does so<br />
by creating a pair with that Int and some text produced by formatLine.<br />
<br />
The second part evaluates the two Term(s) and "stores" the values thus<br />
produced in some variables to be use later to compute the output.<br />
<br />
Let's focus on the "stores" action. The correct term should be<br />
"binds".<br />
<br />
Take a function:<br />
<haskell><br />
f x = x + x<br />
</haskell><br />
"x" appears on both sides of the expression. We say that on the right<br />
side "x" is bound to the value of x given on the left side.<br />
<br />
So<br />
<haskell><br />
f 3<br />
</haskell><br />
binds x to 3 for the evaluation of the expression "x + x".<br />
<br />
Our evaluator binds "a" and "x" / "b" and "y" with the evaluation of<br />
"evalO t" and "evalO u" respectively. <br />
<br />
Then "a","b","x" and "y" will be used in the evaluation of<br />
((a+b),(x++y++formatLine)), that will produce a value of type MOut Int:<br />
<br />
<pre><br />
<br />
((a + b),(x ++ y ++ formatLine (Add t u) (a + b))).<br />
------ -------------------------------------<br />
\ / \ /<br />
Int Output = String<br />
---------------------------------<br />
\ /<br />
MOut Int <br />
</pre><br />
<br />
The binding happens in the "where" clause:<br />
<haskell><br />
where (a, x) = evalO t<br />
(b, y) = evalO u<br />
</haskell><br />
<br />
We know that there is an ad hoc operator for binding variables to a<br />
value: lambda, or \.<br />
<br />
Indeed f x = x + x is syntactic sugar for:<br />
<haskell><br />
f = \x -> x + x<br />
</haskell><br />
When we write f 3 we are actually binding "x" to 3 within what's next<br />
"->", that will be used (substituted) for evaluating f 3.<br />
<br />
So we can try to abstract this phenomenon.<br />
<br />
===Monadic evaluator with output===<br />
What we need is a function that takes our composed type MOut Int and a<br />
function in order to produce a new MOut Int, concatenating the<br />
output of the computation of the first with the output of the<br />
computation of the second.<br />
<br />
This is what bindM does:<br />
<br />
<haskell><br />
<br />
> bindM :: MOut a -> (a -> MOut b) -> MOut b<br />
> bindM m f = (b, x ++ y)<br />
> where (a, x) = m<br />
> (b, y) = f a<br />
<br />
</haskell><br />
<br />
It takes:<br />
* "m": the compound type MOut Int carrying the result of an "eval Term",<br />
* a function "f". This function will take the Int ("a") extracted by the evaluation of "m" ((a,x)=m). This function will produce a new pair: a new Int produced by a new evaluation; some new output.<br />
<br />
bindM will return the new Int in pair with the concatenated outputs<br />
resulting from the evaluation of "m" and "f a".<br />
<br />
As you see, we took the binding part out from evalO and put it in this new function.<br />
<br />
So let's write the new version of the evaluator, that we will call evalM_1:<br />
<br />
<haskell><br />
<br />
> evalM_1 :: Term -> MOut Int<br />
> evalM_1 (Con a) = (a, formatLine (Con a) a)<br />
> evalM_1 (Add t u) = bindM (evalM_1 t) (\a -> <br />
> bindM (evalM_1 u) (\b -> <br />
> ((a + b), formatLine (Add t u) (a + b))<br />
> )<br />
> )<br />
<br />
</haskell><br />
<br />
Ugly, isn't it?<br />
<br />
Let's start from the outside:<br />
<br />
<haskell><br />
bindM (evalM_1 u) (\b -> ((a + b), formatLine (Add t u) (a + b)))<br />
</haskell><br />
<br />
bindM takes the result of the evaluation "evalM_1 u", a type Mout Int,<br />
and a function. It will extract the Int from that type and use it to<br />
bind "b".<br />
<br />
So in bindM (evalM_1 u) (\b ->) "b" will be bound to the value<br />
returned by evalM_1 u, and this bound variable will be available in<br />
what comes after "->" as a bound variable (not free).<br />
<br />
Then the outer part (bindM (evalM_1 t) (\a...) will bind "a" to the<br />
value returned "evalM_1 t", the result of the evaluatuion of the first<br />
Term. This value is needed to evaluate "((a+b), formatLine...) and<br />
produce our final MOut Int.<br />
<br />
We can try to explain "bindM" in a different way by using more descriptive names.<br />
<br />
As we have seen, "bindM" extracts the Int part from our type. The Int<br />
part will be used for further computations and the Output part will be<br />
concatenated. As a result we will have a new pair with a new Int and<br />
an accumulated Output.<br />
<br />
The new version of "bindM":<br />
<haskell><br />
<br />
> getIntFromType typeMOut doSomething = (newInt,oldOutput ++ newOutput)<br />
> where (oldInt,oldOutput) = typeMOut<br />
> (newInt,newOutput) = (doSomething oldInt)<br />
<br />
</haskell><br />
<br />
As you can see it does the very same things that "bindM" does: it<br />
takes something of type MOut and a function to perform some<br />
computation with the Int part. <br />
<br />
In the "where" clause, the old Int and the old output<br />
will be extracted from our type MOut (first line of the "where"<br />
clause). <br />
<br />
A new Int and a new output will be extracted from evaluating<br />
(doSomething oldInt) in the second line.<br />
<br />
Our function will return the new Int and the concatenated outputs.<br />
<br />
We do not need to define our doSomething function, because it will be<br />
an anonymous function:<br />
<br />
<haskell><br />
<br />
> evaluator (Con a) = (a, "output-")<br />
> evaluator (Add t u) = <br />
> getIntFromType (evaluator t) <br />
> (\firstInt -> getIntFromType (evaluator u) <br />
> (\secondInt -> ((firstInt + secondInt),("-newoutput"))))<br />
<br />
</haskell><br />
<br />
As you can see we are feeding our "getIntFromType" with the evaluation<br />
of an expression ("evaluator t" and "evaluator u"). The second<br />
argument of "getIntFromType" is an anonymous function that takes the<br />
"oldInt" and does something with it.<br />
<br />
So we have a series of nested anonymous functions. Their arguments<br />
("\firstInt" and "\secondInt") will be used to produce the computation<br />
we need ("(firstInt + secondInt). Moreover "getIntFromType" will take<br />
care of concatenating the outputs.<br />
<br />
This is the result:<br />
<br />
*TheMonadicWay> evaluator (Add (Con 5) (Con 6))<br />
(11,"output-output--newoutput")<br />
*TheMonadicWay> <br />
<br />
Going back to our "bindM", we can now use lambda notation to write our<br />
evaluator in a more convinient way:<br />
<br />
<haskell><br />
<br />
> evalM_2 :: Term -> MOut Int<br />
> evalM_2 (Con a) = (a, formatLine (Con a) a)<br />
> evalM_2 (Add t u) = evalM_2 t `bindM` \a -><br />
> evalM_2 u `bindM` \b -><br />
> ((a + b), (formatLine (Add t u) (a + b)))<br />
<br />
</haskell><br />
<br />
Now, look at the first part:<br />
<br />
<haskell><br />
evalM_2 (Con a) = (a, formatLine (Con a) a)<br />
</haskell><br />
<br />
We could use a more general way of creating some output. <br />
<br />
We can create a function that takes an Int and returns the type MOut<br />
Int. We do that by pairing the received Int with an empty string "".<br />
<br />
This will be a general way of creating an object with type MOut Int starting from an Int.<br />
<br />
Or, more generaly, a function that takes something of a variable type<br />
a, and return an object of type MOut a, a coumpunt object made up of<br />
an element of type a, and one of type String.<br />
<br />
There it is:<br />
<br />
<haskell><br />
<br />
> mkM :: a -> MOut a<br />
> mkM a = (a, "")<br />
<br />
</haskell><br />
<br />
As you can see, this function will just push an Int and an empty<br />
string ("") inside our type MOut.<br />
<br />
Then we need a method of inserting some text in our object of type<br />
MOut. So we will take a string and return it paired with a void<br />
element "()":<br />
<br />
<haskell><br />
<br />
> outPut :: Output -> MOut ()<br />
> outPut x = ((), x)<br />
<br />
</haskell><br />
<br />
Very simple: we have a string "x" (Output) and create a pair with a ()<br />
instead of an Int, and the output.<br />
<br />
You can see this function as one that pushes a string, paired with a<br />
void int, inside our type MOut.<br />
<br />
Now we can rewrite:<br />
<haskell><br />
evalM_2 (Con a) = (a, formatLine (Con a) a)<br />
</haskell><br />
using the bindM function:<br />
<haskell><br />
evalM_2 (Con a) = outPut (formatLine (Con a) a) `bindM` \_ -> mkM a<br />
</haskell><br />
<br />
First we create an object of type MOut with the Int part (). As you<br />
see bindM will not use it ("\_"), but will concatenate the String part<br />
with the result of mkM, which in turn is the empry string "".<br />
<br />
In other words, first we insert the Output part (a string) in our<br />
MOut, and then we insert the Int paired with an empty string: "bindM"<br />
will not use the void int (the anonymous function will not use it's<br />
argument: "\_"), but will take care of concatenating the non empty<br />
string inserted by "outPut" with the empty one inserted by "mkM".<br />
<br />
Let's rewrite the evaluator:<br />
<br />
<haskell><br />
<br />
> evalM_3 :: Term -> MOut Int<br />
> evalM_3 (Con a) = outPut (formatLine (Con a) a) `bindM` \_ -> <br />
> mkM a<br />
> evalM_3 (Add t u) = evalM_3 t `bindM` \a -><br />
> evalM_3 u `bindM` \b -><br />
> outPut (formatLine (Add t u) (a + b)) `bindM` \_ -> <br />
> mkM (a + b)<br />
<br />
</haskell><br />
<br />
Well, this is fine, definetly better then before, anyway.<br />
<br />
Still we use `bindM` \_ -> that binds something we do not use (_). We<br />
could write a function for this specific case, when we concatenate<br />
computations without the need of binding variables for later uses.<br />
Let's call it `combineM`:<br />
<br />
<haskell><br />
<br />
> combineM :: MOut a -> MOut b -> MOut b<br />
> combineM m f = m `bindM` \_ -> f<br />
<br />
</haskell><br />
<br />
This is just something that will allow us to write the evaluator in a<br />
more concise way. <br />
<br />
So the new evaluator:<br />
<br />
<haskell><br />
<br />
> evalM :: Term -> MOut Int<br />
> evalM (Con a) = outPut (formatLine (Con a) a) `combineM` <br />
> mkM a<br />
> evalM (Add t u) = evalM t `bindM` \a -><br />
> evalM u `bindM` \b -><br />
> outPut (formatLine (Add t u) (a + b)) `combineM` <br />
> mkM (a + b)<br />
<br />
</haskell><br />
<br />
Let's put everything together (changing M into MO, so that this file<br />
will be still usable as a Literate Haskell file):<br />
<br />
<haskell><br />
<br />
> type MO a = (a, Out)<br />
> type Out = String<br />
<br />
> mkMO :: a -> MO a<br />
> mkMO a = (a, "")<br />
<br />
> bindMO :: MO a -> (a -> MO b) -> MO b<br />
> bindMO m f = (b, x ++ y)<br />
> where (a, x) = m<br />
> (b, y) = f a<br />
<br />
> combineMO :: MO a -> MO b -> MO b<br />
> combineMO m f = m `bindM` \_ -> f<br />
<br />
> outMO :: Out -> MO ()<br />
> outMO x = ((), x)<br />
<br />
> evalMO :: Term -> MO Int<br />
> evalMO (Con a) = outMO (formatLine (Con a) a) `combineMO`<br />
> mkMO a<br />
> evalMO (Add t u) = evalMO t `bindMO` \a -><br />
> evalMO u `bindMO` \b -><br />
> outMO (formatLine (Add t u) (a + b)) `combineMO` <br />
> mkMO (a + b)<br />
<br />
</haskell><br />
<br />
==What Does Bind Bind?==<br />
<br />
<div id="Bind"><br />
The evaluator looks like:<br />
<haskell><br />
evalM t >>= \a -> evalM u >>= \b -> outPut "something" >>= \_ -> mkM (a +b)<br />
</haskell><br />
where >>= is bindMO, obviously.<br />
<br />
Let's do some substitution, writing the type of their output of each function:<br />
* evalMO t => (a,Out) - where a is Int<br />
* evalMO u => (b,Out) - where b is the same of a, an Int, but with a different value<br />
* outMO Out = ((),Out)<br />
* mkMO (a+b) => ((a+b),Out) - where (a+b) is the same of a and b, but with a different value from either a and b<br />
<br />
<pre><br />
B | (a,Out) >>= \a -> (b,Out) >>= \b -> ((),Out) >>= \_ >>= ((a + b), Out)---\<br />
i | V V V V V V V V ^ ^ ^ ^ |\<br />
n | |__|________^ | | ^ | | | | | | | MOut Int <=> ((a+b), Out)<br />
d |_____|__(++)__|_Out_|__|__(++)__V_Out_|___|___(++)_|_(++)__|___|____|_____|/<br />
i | | |______(b)__|_____|_____(b)____|__(b)__|___|<br />
n | |_________(a)___________|____________|__(a)__|<br />
g | |_____()_____|<br />
<br />
</pre><br />
<br />
Clear, isn't it?<br />
<br />
"bindMO" is just a function that takes care of gluing together, inside<br />
a data type, a sequence of computations!<br />
<br />
== Some Sugar, Please!==<br />
Now our evaluator has been completely transformed into a monadic<br />
evaluator. That's what it is: a monad.<br />
<br />
We have a function that constructs an object of type MO Int, formed by<br />
a pair: the result of the evaluation and the accumulated<br />
(concatenated) output.<br />
<br />
The process of accumulation and the act of parting the MO Int into its<br />
component is buried into bindMO, now, that can also preserve some<br />
value for later uses.<br />
<br />
So we have:<br />
* MO a type constructor for a type carrying a pair composed by an Int and a String;<br />
* bindMO, that gives a direction to the process of evaluation: it concatenates computations and captures some side effects we created (the direction is given by the changes in the Out part: there's a "before" when Out was something and there's a "later" when Out is something else).<br />
* mkMO lets us create an object of type MO Int starting from an Int.<br />
<br />
As you see this is all we need to create a monad. In other words<br />
monads arise from the type system and the lambda calculus. Everything<br />
else is just syntactic sugar.<br />
<br />
So, let's have a look at that sugar: the famous do-notation!<br />
<br />
===Monadic evaluator with output in do-notation===<br />
<br />
In order to be able to use the "do-notation" we need to define a new<br />
type and make it an instance of the Monad class. To make a new type an<br />
instance of the Monad class we will have to define the two methods of<br />
this class: (>>=) and "return".<br />
<br />
This is not going to be difficult, because we already created these<br />
two methods: "bindM" and "mkM". Now we will have to rewrite them in<br />
order to reflect the fact that we are not going to use a type, for our<br />
evaluator, that is a synonymous of other types, as we did before.<br />
Indeed our MOut was defined with the "type" keyword. Now we will have<br />
to define a "real" new type with either "newtype" or "data". Since we<br />
are not going to need multiple constructors, we will use "newtype".<br />
<br />
<div id="MonadicEvalIO"><br />
<haskell><br />
<br />
> newtype Eval_IO a = Eval_IO (a, O)<br />
> deriving (Show)<br />
> type O = String<br />
<br />
</haskell><br />
<br />
This is our new type: it will have a single type constructor, whose<br />
name is the same of the type name ("Eval_IO"). The type constructor<br />
takes a parameter ("a"), a variable type, and will build a type formed<br />
by a type "a" (an Int in our case) and a String (O is indeed<br />
synonymous of String).<br />
<br />
We now need to define our "bind" function to reflect the fact that we<br />
are now using a "real" type, and, to unpack its content, we need to do<br />
pattern-matching we the type constructor "Eval_IO". Moreover, since we<br />
must return an Eval_IO type, we will use the type constructor also for<br />
building the new type with the new int and the concatenated output.<br />
<br />
For the rest our "bind" function will be identical to the one we<br />
defined before.<br />
<br />
We are going to use very descriptive names:<br />
<br />
<haskell><br />
<br />
> getInt monad doSomething = Eval_IO (newInt,oldOutput ++ newOutput)<br />
> where Eval_IO (oldInt,oldOutput) = monad<br />
> Eval_IO (newInt,newOutput) = (doSomething oldInt)<br />
<br />
</haskell><br />
<br />
As you can see, we are using Eval_IO to build the result of the<br />
computation to be returned by getInt: "Eval_IO (newInt,oldOutput ++<br />
newOutput)". And we are using it to match the internal components of<br />
our type in the "where" clause.<br />
<br />
We also need to create a function that, like mkO, will take an Int and,<br />
using the type constructor "Eval_IO", will create an object of type<br />
Eval_IO with that Int and an empty string:<br />
<br />
<haskell><br />
<br />
> createEval_IO :: a -> Eval_IO a<br />
> createEval_IO int = Eval_IO (int,"")<br />
<br />
</haskell><br />
<br />
And, finally, we need a function that will insert, in our type, a<br />
string and a void ():<br />
<br />
<haskell><br />
<br />
> print_IO :: O -> Eval_IO ()<br />
> print_IO string = Eval_IO ((), string)<br />
<br />
</haskell> <br />
<br />
With these functions we could write our monadic evaluator without the<br />
"do-notation" like this:<br />
<br />
<haskell><br />
<br />
> evalM_4 :: Term -> Eval_IO Int<br />
> evalM_4 (Con a) = createEval_IO a<br />
> evalM_4 (Add t u) = evalM_4 t `getInt` \a -><br />
> evalM_4 u `getInt` \b -><br />
> print_IO (formatLine (Add t u) (a + b)) `getInt` \_ -><br />
> createEval_IO (a + b)<br />
<br />
</haskell><br />
<br />
It is very similar to the previous evaluator, as you can see. The only<br />
differences are related to the fact that we are now using a "real"<br />
type and not a type synonymous: this requires the use of the type<br />
constructor to match the type and its internal part (as we do in the<br />
"where" clause of our "bind" function: "getInt") or to build the type<br />
(as we do in the "bind" function to return the new Int with the<br />
concatenated output).<br />
<br />
Running this evaluator will produce:<br />
<br />
*TheMonadicWay> evalM_4 (Add (Con 6) (Con 12))<br />
Eval_IO (18,"eval (Add (Con 6) (Con 12)) <= 18 - ")<br />
*TheMonadicWay> <br />
<br />
Now we have everything we need to declare our type, Eval_IO, as an<br />
instance of the Monad class:<br />
<haskell><br />
<br />
> instance Monad Eval_IO where<br />
> return a = createEval_IO a<br />
> (>>=) m f = getInt m f<br />
<br />
</haskell><br />
<br />
As you see we are just using our defined functions as methods for our<br />
instance of the Monad class.<br />
<br />
This is all we need to do. Notice that we do not have to define the<br />
"combineM" function, for chaining computation, as we do with "getInt",<br />
without binding variables for later use within the series of nested<br />
anonymous functions (the "doSomething" part) that will form the "do"<br />
block.<br />
<br />
This function comes for free by just defining our type as an instance<br />
of the Monad class. Indeed, if you look at the definition of the Monad<br />
class in the Prelude you see that "combineM", or (>>) is deduced by<br />
the definition of (>>=):<br />
<br />
<haskell><br />
class Monad m where<br />
return :: a -> m a<br />
(>>=) :: m a -> (a -> m b) -> m b<br />
(>>) :: m a -> m b -> m b<br />
fail :: String -> m a<br />
<br />
-- Minimal complete definition: (>>=), return<br />
p >> q = p >>= \ _ -> q<br />
fail s = error s<br />
</haskell><br />
<br />
You can see that the "combineM"" method (or (>>)) is automatically<br />
derived by the "bindMO" (or >>=) method:<br />
<br />
<haskell><br />
p >> q = p >>= \ _ -> q<br />
</haskell><br />
<br />
We can now write our evaluator using the do-notation:<br />
<br />
<haskell><br />
<br />
> eval_IO :: Term -> Eval_IO Int<br />
> eval_IO (Con a) = do print_IO (formatLine (Con a) a)<br />
> return a<br />
> eval_IO (Add t u) = do a <- eval_IO t<br />
> b <- eval_IO u<br />
> print_IO (formatLine (Add t u) (a + b))<br />
> return (a + b)<br />
<br />
</haskell><br />
<br />
As you can see the anonymous functions are gone. Instead we use this:<br />
a <- eval_IO t<br />
<br />
This seems like an assignment, that cannot be possible in Haskell. In<br />
fact it is just the way our anonymous function's arguments is bound<br />
within a do block.<br />
<br />
Even if it does not seem like a series of nested anonymous functions,<br />
this is what actually a do block is.<br />
<br />
Our monad is defined by three elements: <br />
* a type, with its type constructor(s);<br />
* a bind method: it will bind an unwritten anonymous function's argument to the value of the Int part of our type, or more generally, of the variable type "a". It will also create a series of anonymous functions: a line for each function;<br />
* a "return" function, to insert, into out type, a value of type Int, or, more generally, a value of variable type "a".<br />
<br />
Additionally, we need a function to insert a string in our type,<br />
string that the derived "bind" (>>) will concatenate ignoring the void<br />
(), our "print_IO".<br />
<br />
Within a do block we can thus perform only tree kinds of operations:<br />
* a computation that produces a new Int, packed inside our monad's type, to be extracted and bound to a variable (an anonymous function's argument really):<br />
** this operation requires a binding (">>= \varName ->"), translated into "varName <- computation"<br />
** example: a <- eval_IO t<br />
* a computation that inserts a string into our monad, a string to be concatenated, without the need of binding a variable (an anonymous function's argument really):<br />
** this operation does not require a binding: it will be ">>= \_ ->", i.e. ">>", translated into a simple new line<br />
** example: print_IO (formatLine (Add t u) (a + b))<br />
* a computation that inserts an Int into our monad without the need of binding a variable (an anonymous function's argument really):<br />
** this operation is carried out by the <hask>return</hask> method (usually at the end of a do block, useless in the middle)<br />
** example <hask>return (a + b)</hask><br />
<br />
To sum up, within a block, "do" will take care of creating and<br />
nesting, for us, all the needed anonymous functions so that bound<br />
variables will be available for later computations.<br />
<br />
In this way we can emulate a direction of our computation, a "before"<br />
and an "after", even within a pure functional language. And we can use<br />
this possibility to create and accumulate side effects, like output.<br />
<br />
Let's see the evaluator with output in action:<br />
*TheMonadicWay> eval_IO (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) <br />
Eval_IO (54,"eval (Con 6) <= 6 - eval (Con 16) <= 16 - eval (Con 20) <= 20 - eval (Con 12) <= 12 - \<br />
eval (Add (Con 20) (Con 12)) <= 32 - eval (Add (Con 16) (Add (Con 20) (Con 12))) <= 48 - \<br />
eval (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) <= 54 - ")<br />
*TheMonadicWay> <br />
<br />
Let's format the output part:<br />
eval (Con 6) <= 6 <br />
eval (Con 16) <= 16 <br />
eval (Con 20) <= 20 <br />
eval (Con 12) <= 12 <br />
eval (Add (Con 20) (Con 12)) <= 32 <br />
eval (Add (Con 16) (Add (Con 20) (Con 12))) <= 48 <br />
eval (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) <= 54 <br />
<br />
==Type and Newtype: What Happened to Our Output?==<br />
<br />
Well, actually something happened to the output. Let's compare the<br />
output of evalMO (the monadic evaluator written without the<br />
do-notation) and eval_IO:<br />
<br />
*TheMonadicWay> evalMO (Con 6)<br />
(6,"eval (Con 6) <= 6 - ")<br />
*TheMonadicWay> eval_IO (Con 6)<br />
Eval_IO (6,"eval (Con 6) <= 6 - ")<br />
*TheMonadicWay> <br />
<br />
They look almost the same, but they are not the same: the output of<br />
eval_IO has the Eval_IO stuff. It must be related to the changes we<br />
had to do to our evaluator in order to use the do-conation, obviously.<br />
<br />
We can now review some of our basic knowledge of Haskell's type<br />
system.<br />
<br />
What's changed? First the type definition. We have now:<br />
<br />
<haskell><br />
newtype Eval_IO a = Eval_IO (a, O)<br />
deriving (Show)<br />
</haskell><br />
<br />
instead of <br />
<br />
<haskell><br />
type MO a = (a, Out)<br />
</haskell><br />
<br />
Now <hask>return a</hask> is the product of the application of the<br />
type constructor Eval_IO to the pair that are going to form our monad.<br />
<br />
"return" takes an Int and inserts it into our monad. It will also<br />
insert an empty String "" that (>>=) or (>>) will then concatenate in<br />
the sequence of computations they glue together.<br />
<br />
The same for (>>=). It will now return something constructed by<br />
Eval_IO: <br />
<br />
* "newInt", the result of the application of "doSomething" to "oldInt" (better, the binding of "oldInt" in "doSomething");<br />
* the concatenation of "oldOutput" (matched by <hask>Eval_IO (oldInt, oldOutput)</hask> with the evaluation of "monad" - "eval_IO t") and "newOutput", (matched by "Eval_IO(newInt,newOutput)" with the evaluation of "(doSomething monad)" - "eval_IO u").<br />
<br />
That is to say: in the "where" clause, we are matching for the<br />
elements paired in a type Eval_IO: this is indeed the type of "monad"<br />
(corresponding to "eval_IO t" in the body of the evaluator) and<br />
"(doSomething monad)" (where "doSomething" correspond to the<br />
evaluation of "eval_IO u" within an anonymous function with \oldInt as<br />
its argument, argument bound to the result of the previous evaluation<br />
of "monad", that is to say "eval_IO t").<br />
<br />
And so, "Eval_IO (oldInt,oldOutput) = monad" means: match "oldInt" and<br />
"oldOutput", paired in a type Eval_IO, and that are produced by the<br />
evaluation of "monad" (that is to say: "eval_IO t"). The same for<br />
Eval_IO (newInt,newOutput): match "newInt" and "newOutput" produced by<br />
the evaluation of "(doSomething monad)".<br />
<br />
So the output of the evaluator is now not simply a pair made of and<br />
Int and a String. It is a specific type (Eval_IO) that happens to<br />
carry a pair of an Int and a String. But, if we want the Int and the<br />
string, we have to extract them from the Eval_IO type, as we do in the<br />
"where" clause: we ''unpack'' our type object (let's call it with its<br />
name: our monad!) and take out the Int and the String to feed the next<br />
function application and the output generation.<br />
<br />
The same to insert something in our monad: if we want to create a pair<br />
of an Int and a String, pair of type Eval_IO, we now have to ''pack''<br />
them together by using our type constructor, feeding it with a pair<br />
composed by and Int and a String. This is what we do with the "return"<br />
method of out monad and with "print_IO" function, where:<br />
* return insert into the monad an Int;<br />
* print_IO insert into the monad a String.<br />
<br />
So, why cannot we use the old <hask>type MO a = (a, Out)</hask> that<br />
did not required all this additional work (apart the need to<br />
specifically define (>>)?<br />
<br />
Type MO is just a synonymous for (a,Out): the two can be substituted<br />
one for the other. That's it.<br />
<br />
We did not have to pack "a" and "Out" together with a type constructor<br />
to have a new type MO.<br />
<br />
As a consequence, we cannot use MO as an instance of Monad, and so, we<br />
cannot use with it the syntactic sugar we needed: the do-notation.<br />
<br />
That is to say: a type created with the "type" keyword cannot be an<br />
instance of a class, and cannot inherits its methods (in our case<br />
(>>=, >> and return). And without those methods the do-notation is not<br />
usable.<br />
<br />
==Errare Monadicum Est==<br />
<br />
Now that we have a basic understanding of what a monad is, and does,<br />
we will further explore it by making some changes to our evaluator.<br />
<br />
In this section we will se how to handle exceptions in our monadic<br />
evaluator.<br />
<br />
Suppose that we want to stop the execution of our monad if some<br />
conditions occurs. If our evaluator was to compute divisions, instead<br />
of sums, then we would like to stop the evaluator when a division by<br />
zero occurs, possibly producing some output, instead of the result of<br />
the evaluation of the expression, that explains what happened.<br />
<br />
Basic error handling.<br />
<br />
We will do so starting from the beginning once again...<br />
<br />
===The basic evaluator, non monadic, with exception===<br />
<br />
We just take our basic evaluator, without any output, and write a<br />
method to stop execution if a condition occurs: <br />
<br />
<haskell><br />
<br />
> data M a = Raise Exception<br />
> | Return a<br />
> deriving (Show)<br />
> type Exception = String<br />
<br />
</haskell><br />
<br />
Now, our monad is of datatype "M a" which can either be constructed<br />
with the "Raise" constructor, that takes a String (Exception is a<br />
synonymous of String), or by the "Return" constructor, that takes a<br />
variable type ("a"), an Int in our case.<br />
<br />
<haskell><br />
<br />
> evalE :: Term -> M Int<br />
> evalE (Con a) = Return a<br />
<br />
</haskell><br />
<br />
If evalE matches a Con it will construct a type Return with, inside, the content of the Con.<br />
<br />
<haskell><br />
<br />
> evalE (Add a b) = <br />
> case evalE a of<br />
> Raise e -> Raise e<br />
> Return a -><br />
> case evalE b of <br />
> Raise e -> Raise e<br />
> Return b -><br />
> if (a+b) == 42<br />
> then Raise "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else Return (a+b)<br />
<br />
</haskell><br />
<br />
If evalE matches an Add it will check if evaluating the first part<br />
produces a "Raise" or a "Return": in the first case it will return a<br />
"Raise" whose content is the same received. <br />
<br />
If instead the evaluation produces a value of a type matched by<br />
"Return", the evaluator will evaluate the second term of Add.<br />
<br />
If this returns a "Raise", a "Raise" will be returned all the way up<br />
the recursion, otherwise the evaluator will check whether a condition<br />
for raising a "Raise" exists. If not, it will return a "Return" with<br />
the sum inside.<br />
<br />
Test it with:<br />
<br />
evalE (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2)))<br />
<br />
<br />
===The basic evaluator, monadic, with exceptions===<br />
<br />
In order to produce a monadic version of the previous evaluator, the<br />
one that raises exceptions, we just need to abstract out from the<br />
evaluator all that case analysis.<br />
<br />
<haskell><br />
<br />
> data M1 a = Except Exception<br />
> | Ok {showM :: a }<br />
> deriving (Show)<br />
<br />
</haskell><br />
<br />
The data type didn't change at all. Well, we changed the name of the<br />
Return type constructor (now Ok) so that this constructor can coexist<br />
with the previous one in the same Literate Haskell file.<br />
<br />
<div id="MonadicEvalE"><br />
<haskell><br />
<br />
> instance Monad M1 where<br />
> return a = Ok a<br />
> m >>= f = case m of<br />
> Except e -> Except e<br />
> Ok a -> f a<br />
<br />
</haskell><br />
<br />
Binding operations are now very easy. Basically we check:<br />
* if the result of the evaluation of "m" produces an exception (first match: Except e ->...), in which case we return its content by constructing our M1 Int with the "Raise" constructor".<br />
* if the result of the evaluation of "m" is matched with the "Ok" constructor, we get its content and use it to bind the argument of "f" to its value.<br />
<br />
<hask>return a</hask> will just use the Ok type constructor for<br />
inserting "a" (in our case an Int) into M1 Int, the type of our monad.<br />
<br />
<haskell><br />
<br />
> raise :: Exception -> M1 a<br />
> raise e = Except e<br />
<br />
</haskell><br />
<br />
This is just a helper function to construct our "M1 a" type with the<br />
Raise constructor. It takes a string and returns a type (M1 a) to be<br />
matched with the "Raise" constructor.<br />
<br />
<haskell><br />
<br />
> eval_ME :: Term -> M1 Int<br />
> eval_ME (Con a) = do return a<br />
> eval_ME (Add t u) = do a <- eval_ME t<br />
> b <- eval_ME u<br />
> if (a+b) == 42<br />
> then raise "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else return (a + b)<br />
<br />
</haskell><br />
<br />
The evaluator itself is very simple. We bind "a" with the result of<br />
"eval_ME t", "b" with the result of "eval_ME u", and we check for a<br />
condition: <br />
* if the condition is met we raise an exception, that is to say: we return a value constructed with the "Raise" constructor. This value will be matched by ">>=" in the next recursion. And >>= will just return it all the way up the recursion.<br />
* if the condition is not met, we return a value constructed with the "Return" type constructor and go on with the recursion.<br />
<br />
Run with:<br />
<br />
eval_ME (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2)))<br />
<br />
It is noteworthy the fact that in our datatype definition we used a<br />
label field with a label selector (we called it showM), even though it<br />
was not used in our code. We will use this methodology later on.<br />
<br />
So, just to refresh your memory:<br />
<br />
<haskell><br />
<br />
> data Person = Person {name :: String,<br />
> age :: Int,<br />
> hobby :: String<br />
> } deriving (Show)<br />
<br />
> andreaRossato = Person "Andrea" 37 "Haskell The Monadic Way"<br />
> personName (Person a b c) = a<br />
<br />
</haskell><br />
<br />
will produce:<br />
*TheMonadicWay> andreaRossato<br />
Person {name = "Andrea", age = 37, hobby = "Haskell The Monadic Way"}<br />
*TheMonadicWay> personName andreaRossato<br />
"Andrea"<br />
*TheMonadicWay> name andreaRossato<br />
"Andrea"<br />
*TheMonadicWay> age andreaRossato<br />
37<br />
*TheMonadicWay> hobby andreaRossato<br />
"Haskell The Monadic Way"<br />
*TheMonadicWay> <br />
<br />
<br />
===Monadic evaluator with output and exceptions===<br />
<br />
We will now try to combine the [[The Monadic Way Part I#MonadicEvalIO|output-producing monadic evaluator]] <br />
with [[The Monadic Way Part I#MonadicEvalE| exception producing one]].<br />
<br />
<br />
<haskell><br />
<br />
> data M2 a = Ex Exception<br />
> | Done {unpack :: (a,O) }<br />
> deriving (Show)<br />
<br />
</haskell><br />
<br />
Now we need a datatype with two constructor: one to produce a value<br />
type "M2 a" using "Ex String" and one for value type "M2 a" (Int in<br />
this case) using "Done a".<br />
<br />
'''Note''' that we changed the name of the exception type constructor<br />
from "Raise" to "Ex" just to make the two coexist in the same Literate<br />
Haskell file.<br />
<br />
The constructor "Done a" is defined with a label sector: <hask>Done {unpack :: (a,O)}</hask> <br />
and is equivalent to <hask>Done (a,O)</hask>. <br />
<br />
The only difference is that, this way, we are also defining a method<br />
to retrieve the pair (a,O) (in our case "O" is a synonymous for<br />
String, whereas "a" is a variable type) from an object of type "Done<br />
a".<br />
<br />
<haskell><br />
<br />
> instance Monad M2 where<br />
> return a = Done (a, "")<br />
> m >>= f = case m of<br />
> Ex e -> Ex e<br />
> Done (a, x) -> case (f a) of<br />
> Ex e1 -> Ex e1<br />
> Done (b, y) -> Done (b, x ++ y)<br />
<br />
</haskell><br />
<br />
Now our binding operations gets more complicated by the fact that we<br />
have to concatenate the output, as we did before, '''and''' check for<br />
exceptions.<br />
<br />
It is not possible to do has we did in the [[The Monadic Way Part I#MonadicEvalE| exception producing evaluator]], <br />
where we could check just for "m" (remember the "m" in the first run<br />
stands for "eval t").<br />
<br />
Since at the end we must return the output produced by the evaluation<br />
of "m" ''concatenated'' with the output produced by the evaluation of<br />
"f a" (where "a" is returned by "m", paired with "x" by "Done"), now<br />
we must check if we '''do have''' an output from "f a" produced by<br />
"Done".<br />
<br />
Indeed, now, "f a" can also produce a value constructed by "Ex", and<br />
this value does not contain the pair as the value produced by "Done".<br />
<br />
So, we evaluate "m": <br />
* if we match a value produced by type constructor "Ex" we return a value produced by type constructor "Ex" whose content is the one we extracted in the matching;<br />
* if we match a value produced by "Done" we match the pair it carries "(a,x)" and we analyze what "f a" returns:<br />
** if "f a" returns a value produced by "Ex" we extract the exception and we return it, constructing a value with "Ex"<br />
** if "f a" returns a value produced by "Done" we return "b" and the concatenated "x" and "y". <br />
<br />
And now the evaluator:<br />
<br />
<haskell><br />
<br />
> raise_IOE :: Exception -> M2 a<br />
> raise_IOE e = Ex e<br />
<br />
</haskell><br />
<br />
This is the function to insert in our monad (M2) an exception: we take<br />
a String and produce a value applying the type constructor for<br />
exception "Ex" to its value.<br />
<br />
<haskell><br />
<br />
> print_IOE :: O -> M2 ()<br />
> print_IOE x = Done ((), x)<br />
<br />
</haskell><br />
<br />
The function to produce output is the very same of the one of the [[The Monadic Way Part I#MonadicEvalIO|output-producing monadic evaluator]].<br />
<br />
<haskell><br />
<br />
> eval_IOE :: Term -> M2 Int<br />
> eval_IOE (Con a) = do print_IOE (formatLine (Con a) a)<br />
> return a<br />
> eval_IOE (Add t u) = do a <- eval_IOE t<br />
> b <- eval_IOE u<br />
> let out = formatLine (Add t u) (a + b)<br />
> print_IOE out<br />
> if (a+b) == 42<br />
> then raise_IOE $ out ++ "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else return (a + b)<br />
<br />
</haskell><br />
<br />
The evaluator procedure did not change very much from the one of the [[The Monadic Way Part I#MonadicEvalIO|output-producing monadic evaluator]].<br />
<br />
We just added the case analysis to see if the condition for raising an exception is met.<br />
<br />
Running with<br />
<br />
eval_IOE (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2)))<br />
<br />
will produce <br />
<br />
Ex "eval (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2))) <= 42 - <br />
The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
<br />
Look at the <hask>let</hask> clause within the do-notation. We do not<br />
need to use the "let ... in" construction: since all bound variables<br />
remain bound within a <hask>do</hask> procedure (see [[The Monadic Way Part I#Bind|here]]), <br />
we do not need the "in" to specify "where" the variable "out" will be bound in!<br />
<br />
==We Need A State==<br />
<br />
We will keep on adding complexity to our monadic evaluator and this<br />
time we will add a counter. We just want to count the number of<br />
iterations (the number of times "eval" will be called) needed to<br />
evaluate the expression.<br />
<br />
===The basic evaluator, non monadic, with a counter===<br />
<br />
As before we will start by adding this feature to our [[The Monadic Way Part I#BasicEval|basic evaluator]]. <br />
<br />
A method to count the number of iterations, since the lack of<br />
assignment and destructive updates (such as for i=0;i<10;i++;),<br />
is to add an argument to our function, the initial state, number that<br />
in each call of the function will be increased and passed to the next<br />
function call.<br />
<br />
And so, very simply:<br />
<br />
<haskell><br />
<br />
> -- non monadic<br />
> type St a = State -> (a, State)<br />
> type State = Int<br />
> evalNMS :: Term -> St Int<br />
> evalNMS (Con a) x = (a, x + 1)<br />
> evalNMS (Add t u) x = let (a, y) = evalNMS t x in<br />
> let (b, z) = evalNMS u y in<br />
> (a + b, z +1)<br />
<br />
</haskell><br />
<br />
Now evalNMS takes two arguments: the expression of type Term and an<br />
State (which is a synonymous for Int), and will produce a pair<br />
(a,State), that is to say a pair with a variable type "a" and an Int.<br />
<br />
The operations in the evaluator are very similar to the non monadic [[The Monadic Way Part I#BasivEvalO|output producing evaluator]].<br />
<br />
We are now using the "let ... in" clause, instead of the "where", and we are increasing the counter "z" the comes from the evaluation of the second term, but the basic operation are the same:<br />
* we evaluate "evalNMS t x" where "x" is the initial state, and we match and bind the result in "let (a, y) ... in"<br />
* we evaluate "evalNMS u y", where "y" was bound to the value returned by the previous evaluation, and we match and bind the result in "let (b, z) ... in"<br />
* we return a pair formed by the sum of the result (a+b) and the state z increased by 1. <br />
<br />
Let's try it:<br />
<br />
*TheMonadicWay> evalNMS (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2))) 0<br />
(42,7)<br />
*TheMonadicWay> <br />
<br />
As you see we must pass to "evalNMS"the initial state of our counter: 0.<br />
<br />
Look at the type signature of the function "evalNMS":<br />
<haskell><br />
evalNMS :: Term -> St Int<br />
</haskell><br />
<br />
From this signature you could argue that our function takes only<br />
'''one''' argument. But since our type St is defined with the "type"<br />
keyword, St can be substituted with what comes after the "=" sign. So,<br />
the real type signature of our function is:<br />
<br />
<haskell><br />
evalNMS :: Term -> State -> (Int,State)<br />
</haskell><br />
<br />
<div if="typeNewtype"><br />
Just to refresh your memory:<br />
<br />
<haskell><br />
<br />
> type IamAfunction a = (a -> a)<br />
> newtype IamNotAfunction a = NF (a -> a)<br />
> newtype IamNotAfunctionButYouCanUnPackAndRunMe a = F { unpackAndRun :: (a -> a) }<br />
<br />
> a = \x -> x * x<br />
<br />
> a1 :: IamAfunction Integer<br />
> a1 = a<br />
<br />
> a2 :: IamNotAfunction Integer<br />
> a2 = NF a<br />
<br />
> a3 :: IamNotAfunctionButYouCanUnPackAndRunMe Integer<br />
> a3 = F a<br />
<br />
</haskell><br />
<br />
<br />
*TheMonadicWay> a 4<br />
16<br />
*TheMonadicWay> a1 4<br />
16<br />
*TheMonadicWay> a2 4<br />
<br />
<interactive>:1:0:<br />
The function `a2' is applied to one arguments,<br />
but its type `IamNotAfunction Int' has only 0<br />
In the definition of `it': it = a2 4<br />
*TheMonadicWay> a3 4<br />
<br />
<interactive>:1:0:<br />
The function `a3' is applied to one arguments,<br />
but its type `IamNotAfunctionButYouCanUnPackAndRunMe Int' has only 0<br />
In the definition of `it': it = a3 4<br />
*TheMonadicWay> unpackAndRun a3 4<br />
16<br />
*TheMonadicWay><br />
<br />
This means that "a1" is a partial application hidden by a type<br />
synonymous. <br />
<br />
"a2" and "a3" are not function types. They are types that<br />
have a functional value. <br />
<br />
Moreover, since we defined the type constructor of type<br />
"IamNotAfunctionButYouCanUnPackAndRunMe", F, with a label field, in<br />
that label field we defined a method (a label selector) to "extract"<br />
the function from the type "IamNotAfunctionButYouCanUnPackAndRunMe",<br />
and run it:<br />
<br />
<haskell><br />
unpackAndRun a3 4<br />
</haskell><br />
<br />
And what about "a2"? Is it lost forever?<br />
<br />
Obviously not! We need to write a function that unpacks a type<br />
"IamNotAfunction", using its type constructor NF to match the internal<br />
function:<br />
<br />
<haskell><br />
<br />
> unpackNF :: IamNotAfunction a -> a -> a<br />
> unpackNF (NF f) = f<br />
<br />
</haskell><br />
<br />
and run:<br />
<br />
*TheMonadicWay> unpackNF a2 4<br />
16<br />
*TheMonadicWay> <br />
<br />
As you see, "unpackNF" definition is a partial application: we specify<br />
one argument to get a function that gets another argument.<br />
<br />
A label selector does the same thing.<br />
<br />
Later we will see the importance of this distinction, quite obvious<br />
for haskell gurus, but not for us. Till now.<br />
<br />
===The evaluator, monadic, with a counter===<br />
<br />
We will now rewrite our basic evaluator with the counter in<br />
do-notation.<br />
<br />
As we have seen, in order to do so we need:<br />
* a new type that we must declare as an instance of the Monad class;<br />
* a function for binding method (>>=) and a function for the "return" method, for the instance declaration; <br />
<br />
Now our type will be holding a function that will take the initial<br />
state 0 as we did before.<br />
<br />
In order to simplify the process of unpacking the monad each time to<br />
get the function, we will use a label sector:<br />
<br />
<haskell><br />
<br />
> newtype MS a = MS { unpackMSandRun :: (State -> (a, State)) }<br />
<br />
</haskell><br />
<br />
This is it: MS will be out type constructor for matching and for<br />
building our monad. "unpackMSandRun" will be the method to get the<br />
function out of the monad to feed it with the initial state of the<br />
counter, 0, to get our result.<br />
<br />
Then we need the "return" function that, as we have seen does nothing<br />
but inserting into our monad an Integer:<br />
<br />
<haskell><br />
<br />
> mkMS :: a -> MS a<br />
> mkMS int = MS (\x -> (int, x))<br />
<br />
</haskell><br />
<br />
"mkMS" will just take an Integer "a" and apply the MS type constructor<br />
to our anonymous function that takes an initial state and produces the<br />
final state "x" and the integer "a".<br />
<br />
In other words, we are just creating our monad with inside an Integer.<br />
<br />
Our binding function will be a bit more complicated then before. We<br />
must create a type that holds an anonymous function with elements to<br />
be extracted from our type and passed to the anonymous function that<br />
comes next:<br />
<br />
<haskell><br />
<br />
> bindMS :: MS a -> (a -> MS b) -> MS b<br />
> bindMS monad doNext = MS $ \initialState -> <br />
> let (oldInt, oldState) = unpackMSandRun monad initialState in<br />
> let (newInt, newState) = unpackMSandRun (doNext oldInt) oldState in<br />
> (newInt,newState)<br />
<br />
</haskell><br />
<br />
So, we are creating an anonymous function that will take an initial<br />
state, 0, and return a "newInt" and "newState". <br />
<br />
To do that we need to unpack and run our "monad" against the<br />
initialState in order to get the "oldInt" and the "oldState".<br />
<br />
The "oldInt" will be passed to the "doNext" function (the next<br />
anonymous function in our do block) together with the "oldState" to<br />
get the "newInt" and the "newState".<br />
<br />
We can now declare our type "MS" as an instance of the Monad class:<br />
<haskell><br />
<br />
> instance Monad MS where<br />
> return a = mkMS a<br />
> (>>=) m f = bindMS m f<br />
<br />
</haskell><br />
<br />
We now need a function to increase the counter in our monad from<br />
within a do block:<br />
<br />
<haskell><br />
<br />
> incState :: MS ()<br />
> incState = MS (\s -> ((), s + 1))<br />
<br />
</haskell><br />
<br />
This is easier then it looks like. We use the type constructor MS to<br />
create a function that takes a state an returns a void integer ()<br />
paired with the state increased by one. We do not need any binding,<br />
since we are just modifying the state, an integer, and, in our do<br />
block, we will insert this function before a new line, so that the non<br />
binding ">>" operator will be applied.<br />
<br />
And now the evaluator:<br />
<br />
<haskell><br />
<br />
> evalMS :: Term -> MS Int<br />
> evalMS (Con a) = do incState<br />
> mkMS a<br />
> evalMS (Add t u) = do a <- evalMS t<br />
> b <- evalMS u<br />
> incState <br />
> return (a + b)<br />
<br />
</haskell><br />
<br />
Very easy: we just added the "incState" function before returning the<br />
sum of the evaluation of the Terms of our expression.<br />
<br />
Let's try it:<br />
<br />
*TheMonadicWay> unpackMSandRun (evalMS (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12))))) 0<br />
(54,7)<br />
*TheMonadicWay> <br />
<br />
As you can see, adding a counter makes our binding operations a bit<br />
more complicated by the fact that we have an anonymous function within<br />
our monad. This means that we must recreate that anonymous function in<br />
each step of our do block. This makes "incState" and, as we are going<br />
to see in the next paragraph, the function to produce output a bit<br />
more complicated. Anyway we can handle this complexity quite well, for<br />
now.<br />
<br />
===The monadic evaluator with output and counter in do-notation===<br />
<br />
Adding output to our evaluator is now quite easy. It's just a matter<br />
of adding a field to our type, where we are going to accumulate the<br />
output, and take care of extracting it in our bind function to<br />
concatenate the old one with the new one.<br />
<br />
<haskell><br />
<br />
> newtype Eval_SIO a = Eval_SIO { unPackMSIOandRun :: State -> (a, State, Output) }<br />
<br />
</haskell><br />
<br />
Now our monad contains an anonymous function that takes the initial<br />
state, 0, and will produce the final Integer, the final state and the<br />
concatenated output.<br />
<br />
So, this is bind:<br />
<br />
<haskell><br />
<br />
> bindMSIO monad doNext = <br />
> Eval_SIO (\initialState -><br />
> let (oldInt, oldState, oldOutput) = unPackMSIOandRun monad initialState in<br />
> let (newInt, newState, newOutput) = unPackMSIOandRun (doNext oldInt) oldState in<br />
> (newInt, newState, oldOutput ++ newOutput))<br />
<br />
</haskell><br />
<br />
And this is our "return":<br />
<haskell><br />
<br />
> mkMSIO int = Eval_SIO (\s -> (int, s, ""))<br />
<br />
</haskell><br />
<br />
Now we can declare our type, "Eval_SIO", as an instance of the Monad class:<br />
<br />
<haskell><br />
<br />
> instance Monad Eval_SIO where<br />
> return a = mkMSIO a<br />
> (>>=) m f = bindMSIO m f<br />
<br />
</haskell><br />
<br />
Now, the function to increment the counter will also insert an empty<br />
string "" in our monad: "bind" will take care of concatenating it with<br />
the old output:<br />
<br />
<haskell><br />
<br />
> incSIOstate :: Eval_SIO () <br />
> incSIOstate = Eval_SIO (\s -> ((), s + 1, ""))<br />
<br />
</haskell><br />
<br />
The function to insert some new output will just insert a string into<br />
our monad, together with a void Integer (). Since no binding will<br />
occur (>> will be applied), the () will not be taken into<br />
consideration within the anonymous functions automatically created for<br />
us within the do block:<br />
<br />
<haskell><br />
<br />
> print_SIO :: Output -> Eval_SIO ()<br />
> print_SIO x = Eval_SIO (\s -> ((),s, x))<br />
<br />
</haskell><br />
<br />
And now the evaluator, that puts everything together. As you can see<br />
it did not change too much from the previous versions:<br />
<br />
<haskell><br />
<br />
> eval_SIO :: Term -> Eval_SIO Int<br />
> eval_SIO (Con a) = do incSIOstate<br />
> print_SIO (formatLine (Con a) a)<br />
> return a<br />
> eval_SIO (Add t u) = do a <- eval_SIO t<br />
> b <- eval_SIO u<br />
> incSIOstate<br />
> print_SIO (formatLine (Add t u) (a + b))<br />
> return (a + b)<br />
<br />
</haskell><br />
<br />
Running it will require unpacking the monad and feeding it with the initial state 0:<br />
<br />
unPackMSIOandRun (eval_SIO (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12))))) 0<br />
*TheMonadicWay> unPackMSIOandRun (eval_SIO (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12))))) 0<br />
(54,7,"eval (Con 6) <= 6 - eval (Con 16) <= 16 - <br />
eval (Con 20) <= 20 - <br />
eval (Con 12) <= 12 - <br />
eval (Add (Con 20) (Con 12)) <= 32 - <br />
eval (Add (Con 16) (Add (Con 20) (Con 12))) <= 48 - <br />
eval (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) <= 54 - ")<br />
*TheMonadicWay> <br />
<br />
(I formatted the output).<br />
<br />
<br />
==If There's A State We Need Some Discipline: Dealing With Complexity==<br />
<br />
'''(Text to be done yet: just a summary)'''<br />
<br />
In order to increase the complexity of our monad now we will try to<br />
mix State (counter), Exceptions and Output.<br />
<br />
This is an email [http://www.haskell.org/pipermail/haskell-cafe/2006-August/017672.html I send to the haskell-cafe mailing list]:<br />
<br />
<pre><br />
Now I'm trying to create a statefull evaluator, with output and<br />
exception, but I'm facing a problem I seem not to be able to<br />
conceptually solve.<br />
<br />
Take the code below.<br />
Now, in order to get it run (and try to debug) the Eval_SOI type has a<br />
Raise constructor that produces the same type of SOIE. Suppose instead it<br />
should be constructing something like Raise "something". <br />
Moreover, I wrote a second version of >>=, commented out.<br />
This is just to help me illustrate to problem I'm facing.<br />
<br />
Now, >>= is suppose to return Raise if "m" is matched against Raise<br />
(second version commented out).<br />
If "m" matches SOIE it must return a SOIE only if "f a" does not<br />
returns a Raise (output must be concatenated).<br />
<br />
I seem not to be able to find a way out. Moreover, I cannot understand<br />
if a way out can be possibly found. Something suggests me it could be<br />
related to that Raise "something".<br />
But my feeling is that functional programming could be something out<br />
of the reach of my mind... by the way, I teach Law, so perhaps you'll<br />
forgive me...;-)<br />
<br />
If you can help me to understand this problem all I can promise is<br />
that I'll mention your help in the tutorial I'm trying to write on<br />
"the monadic way"... that seems to lead me nowhere.<br />
<br />
Thanks for your kind attention.<br />
<br />
Andrea<br />
</pre><br />
<br />
This was the code:<br />
<br />
<haskell><br />
data Eval_SOI a = Raise { unPackMSOIandRun :: State -> (a, State, Output) }<br />
| SOIE { unPackMSOIandRun :: State -> (a, State, Output) }<br />
<br />
instance Monad Eval_SOI where<br />
return a = SOIE (\s -> (a, s, ""))<br />
m >>= f = SOIE (\x -><br />
let (a, y, s1) = unPackMSOIandRun m x in<br />
case f a of<br />
SOIE nextRun -> let (b, z, s2) = nextRun y in <br />
(b, z, s1 ++ s2)<br />
Raise e1 -> e1 y --only this happens<br />
<br />
)<br />
-- (>>=) m f = case m of<br />
-- Raise e -> error "ciao" -- why this is not going to happen?<br />
-- SOIE a -> SOIE (\x -><br />
-- let (a, y, s1) = unPackMSOIandRun m x in<br />
-- let (b, z, s2) = unPackMSOIandRun (f a) y in <br />
-- (b, z, s1 ++ s2)) <br />
<br />
<br />
incSOIstate :: Eval_SOI ()<br />
incSOIstate = SOIE (\s -> ((), s + 1, ""))<br />
<br />
print_SOI :: Output -> Eval_SOI ()<br />
print_SOI x = SOIE (\s -> ((),s, x))<br />
<br />
raise x e = Raise (\s -> (x,s,e))<br />
<br />
eval_SOI :: Term -> Eval_SOI Int<br />
eval_SOI (Con a) = do incSOIstate<br />
print_SOI (formatLine (Con a) a)<br />
return a<br />
eval_SOI (Add t u) = do a <- eval_SOI t<br />
b <- eval_SOI u<br />
incSOIstate<br />
print_SOI (formatLine (Add t u) (a + b))<br />
if (a + b) == 42 <br />
then raise (a+b) " = The Ultimate Answer!!"<br />
else return (a + b)<br />
<br />
runEval exp = case eval_SOI exp of<br />
Raise a -> a 0<br />
SOIE p -> let (result, state, output) = p 0 in<br />
(result,state,output)<br />
<br />
<br />
<br />
--runEval (Add (Con 10) (Add (Con 28) (Add (Con 40) (Con 2))))<br />
</haskell><br />
<br />
This code will produce <br />
<br />
eval (Con 10) <= 10 -<br />
eval (Con 28) <= 28 -<br />
eval (Con 40) <= 40 -<br />
eval (Con 2) <= 2 - = The Ultimate Answer!!<br />
eval (Add (Con 28) (Add (Con 40) (Con 2))) <= 70 -<br />
eval (Add (Con 10) (Add (Con 28) (Add (Con 40) (Con 2)))) <= 80 -<br />
<br />
The exception appears in the output, but executioon is not stopped.<br />
<br />
===Monadic evaluator with output, counter and exception, in do-notation===<br />
<br />
Brian Hulley [http://www.haskell.org/pipermail/haskell-cafe/2006-August/017680.html came up with this solution]:<br />
<br />
<haskell><br />
<br />
> -- thanks to Brian Hulley<br />
> data Result a<br />
> = Good a State Output<br />
> | Bad State Output Exception<br />
> deriving Show<br />
<br />
> newtype Eval_SIOE a = SIOE {runSIOE :: State -> Result a}<br />
<br />
> instance Monad Eval_SIOE where<br />
> return a = SIOE (\s -> Good a s "")<br />
> m >>= f = SIOE $ \x -><br />
> case runSIOE m x of<br />
> Good a y o1 -><br />
> case runSIOE (f a) y of<br />
> Good b z o2 -> Good b z (o1 ++ o2)<br />
> Bad z o2 e -> Bad z (o1 ++ o2) e<br />
> Bad z o2 e -> Bad z o2 e<br />
<br />
> raise_SIOE e = SIOE (\s -> Bad s "" e)<br />
<br />
> incSIOEstate :: Eval_SIOE ()<br />
> incSIOEstate = SIOE (\s -> Good () (s + 1) "")<br />
<br />
> print_SIOE :: Output -> Eval_SIOE ()<br />
> print_SIOE x = SIOE (\s -> Good () s x)<br />
<br />
<br />
> eval_SIOE :: Term -> Eval_SIOE Int<br />
> eval_SIOE (Con a) = do incSIOEstate<br />
> print_SIOE (formatLine (Con a) a)<br />
> return a<br />
> eval_SIOE (Add t u) = do a <- eval_SIOE t<br />
> b <- eval_SIOE u<br />
> incSIOEstate<br />
> let out = formatLine (Add t u) (a + b)<br />
> print_SIOE out<br />
> if (a+b) == 42<br />
> then raise_SIOE $ out ++ "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else return (a + b)<br />
<br />
> runEval exp = case runSIOE (eval_SIOE exp) 0 of<br />
> Bad s o e -> "Error at iteration n. " ++ show s ++ <br />
> " - Output stack = " ++ o ++ <br />
> " - Exception = " ++ e<br />
> Good a s o -> "Result = " ++ show a ++ <br />
> " - Iterations = " ++ show s ++ " - Output = " ++ o<br />
<br />
</haskell><br />
<br />
Run with runEval (Add (Con 18) (Add (Con 12) (Add (Con 10) (Con 2))))<br />
<br />
==Suggested Readings==<br />
<br />
Cale Gibbard, [http://haskell.org/haskellwiki/Monads_as_Containers Monads as Containers]<br />
<br />
Jeff Newbern, [http://www.nomaware.com/monads/html/index.html All About Monads]<br />
<br />
[http://haskell.org/haskellwiki/IO_inside IO Inside]<br />
<br />
[http://sigfpe.blogspot.com/2006/08/you-could-have-invented-monads-and.html You Could Have Invented Monads! (And Maybe You Already Have.) by sigfpe]<br />
<br />
<br />
==Acknowledgments==<br />
<br />
Thanks to Neil Mitchell, Daniel Fisher, Bulat Ziganzhin, Brian Hulley<br />
and Udo Stenzel for the invaluable help they gave, in the<br />
[http://www.haskell.org/mailman/listinfo/haskell-cafe haskell-cafe mailing list], <br />
in understanding this topic.<br />
<br />
I couldn't do it without their help.<br />
<br />
Obviously errors are totally mine. But this is a wiki so, please,<br />
correct them!<br />
<br />
- [[User:AndreaRossato|Andrea Rossato]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=The_Monadic_Way/Part_I&diff=5871The Monadic Way/Part I2006-09-08T12:25:20Z<p>AndreaRossato: added a summary of the operations that can be done within a do block</p>
<hr />
<div>'''Note: this is the first part of [[The Monadic Way]]'''<br />
==An evaluation of Philip Wadler's "Monads for functional programming"==<br />
<br />
This tutorial is a "translation" of Philip Wadler's [http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf "Monads for functional programming"].<br />
(avail. from [http://homepages.inf.ed.ac.uk/wadler/topics/monads.html here])<br />
<br />
I'm a Haskell newbie trying to grasp such a difficult concept as the<br />
one of Monad and monadic computation.<br />
<br />
While [http://www.cs.utah.edu/~hal/htut/ "Yet Another Haskell Tutorial"] <br />
gave me a good understanding of the type system when it<br />
comes to monads I find it almost unreadable.<br />
<br />
But I had also Wadler's paper, and started reading it. Well, just<br />
wonderful! It explains how to ''create'' a monad!<br />
<br />
So I decided to "translate it", in order to clarify to myself the<br />
topic. And I'm now sharing this traslation ('''not completed yet'')<br />
with the hope it will be useful to someone else.<br />
<br />
Moreover, that's a wiki, so please improve it. And, specifically,<br />
correct my poor English. I'm Italian, after all.<br />
<br />
'''Note: The source of this page can be used as a Literate Haskell<br />
file and can be run with ghci or hugs: so cut paste change and run (in<br />
emacs for instance) while reading it...'''<br />
<br />
==A Simple Evaluator==<br />
<br />
Let's start with something simple: suppose we want to implement a new<br />
programming language. We just finished with<br />
[http://swiss.csail.mit.edu/classes/6.001/abelson-sussman-lectures/ Abelson and Sussman's Structure and Interpretation of Computer Programs] <br />
and we want to test what we have learned.<br />
<br />
Our programming language will be very simple: it will just compute the<br />
sum of two terms.<br />
<br />
So we have just one primitive operation (Add) that takes two constants<br />
and calculates their sum.<br />
<br />
Moreover we have just one kind of data type: Con a, which is an Int.<br />
<br />
For instance, something like:<br />
<br />
(Add (Con 5) (Con 6))<br />
<br />
should yeld:<br />
<br />
11<br />
<br />
===The basic evaluator===<br />
<br />
We will implement our language with the help of a data type<br />
constructor such as:<br />
<br />
<div id="BasicEval"><br />
<haskell><br />
<br />
> module TheMonadicWay where<br />
> data Term = Con Int<br />
> | Add Term Term<br />
> deriving (Show)<br />
<br />
</haskell><br />
<br />
After that we build our interpreter:<br />
<br />
<haskell><br />
<br />
> eval :: Term -> Int<br />
> eval (Con a) = a<br />
> eval (Add a b) = eval a + eval b<br />
<br />
</haskell><br />
<br />
That's it. Just an example:<br />
<br />
*TheMonadicWay> eval (Add (Con 5) (Con 6))<br />
11<br />
*TheMonadicWay><br />
<br />
Very very simple. The evaluator checks if its argument is of type Con<br />
Int: if it is it just returns the Int.<br />
<br />
If the argument is not of type Con, but it is of type Term, it<br />
evaluates the first Term and sums the result with the result of the<br />
evaluation of the second Term.<br />
<br />
As you may understand, our evaluator uses some of the powerful<br />
features of Haskell type system. Instead of writing a parser that<br />
takes a string (the user input) and transforms that string into an<br />
expression to be evaluated, we use the two type constructors defined<br />
for our data type Term (Con and Add) to build the expression - such as<br />
(Add (Con 5) (Con 6)) - and to match the expression's elements in our<br />
"eval" function.<br />
<br />
<br />
== Some Output, Please!==<br />
<br />
Now, that's fine, but we'd like to add some features, like providing<br />
some output, to show how the computation was carried out.<br />
<br />
Well, but Haskell is a pure functional language, with no side effects,<br />
we were told.<br />
<br />
Now we seem to be wanting to create a side effect of the computation,<br />
its output, and be able to stare at it...<br />
<br />
If we had some global variable to store the out that would be<br />
simple...<br />
<br />
But we can create the output and carry it along the computation,<br />
concatenating it with the old one, and present it at the end of the<br />
evaluation together with the evaluation of the expression given to our<br />
evaluator/interpreter!<br />
<br />
===The basic evaluator with output===<br />
<br />
Simple and neat:<br />
<div id="BasivEvalO"><br />
<haskell><br />
<br />
> type MOut a = (a, Output)<br />
> type Output = String<br />
> <br />
> formatLine :: Term -> Int -> Output<br />
> formatLine t a = "eval (" ++ show t ++ ") <= " ++ show a ++ " - " <br />
> <br />
> evalO :: Term -> MOut Int<br />
> evalO (Con a) = (a, formatLine (Con a) a)<br />
> evalO (Add t u) = ((a + b),(x ++ y ++ formatLine (Add t u) (a + b)))<br />
> where (a, x) = evalO t<br />
> (b, y) = evalO u<br />
<br />
</haskell><br />
<br />
Now we have what we want. But we had to change our evaluator quite a<br />
bit. <br />
<br />
First we added a function, formatLine, that takes an argument of type<br />
Term (the expression to be evaluated), one of type Int (the result of<br />
the evaluation of Term) and gives back an output of type Output (that<br />
is a synonymous of String). This is just a helper function to format<br />
the string to output. Not very interesting at all.<br />
<br />
The evaluator itself changed quite a lot! Now it has a different type<br />
signature: it takes an argument of type Term and produces a new type,<br />
we called it MOut, that is actually a compound pair of a variable type<br />
a (an Int in our evaluator) and a type Output, a string.<br />
<br />
So our evaluator, now, will take a Term (the type of the expressions<br />
in our new programming language) and will produce a pair, composed of<br />
the result of the evaluation (an Int) and the Output, a string.<br />
<br />
So far so good. But what's happening inside the evaluator?<br />
<br />
The first part will just return a pair with the number evaluated ("a")<br />
and the output formatted by formatLine.<br />
<br />
The second part does something more complicated: it returns a pair<br />
composed by <br />
1. the result of the evaluation of the right Term summed to the result<br />
of the evaluation of the second Term<br />
2. the output: the concatenation of the output produced by the<br />
evaluation of the right Term, the output produced by the evaluation of<br />
the left Term (each this evaluation returns a pair with the number and<br />
the output), and the formatted output of the evaluation.<br />
<br />
Let's try it:<br />
*TheMonadicWay> evalO (Add (Con 5) (Con 6))<br />
(11,"eval (Con 5) <= 5 - eval (Con 6) <= 6 - eval (Add (Con 5) (Con 6)) <= 11 - ")<br />
*TheMonadicWay><br />
<br />
It works! Let's put the output this way:<br />
eval (Con 5) <= 5 - <br />
eval (Con 6) <= 6 - <br />
eval (Add (Con 5) (Con 6)) <= 11 -<br />
<br />
Great! We are able to produce a side effect of our evaluation and<br />
present it at the end of the computation, after all.<br />
<br />
Let's have a closer look at this expression:<br />
<haskell><br />
<br />
evalO (Add t u) = ((a + b),(x ++ y ++ formatLine (Add t u) (a + b)))<br />
where (a, x) = evalO t<br />
(b, y) = evalO u<br />
<br />
</haskell><br />
<br />
Why all that? The problem is that we need:<br />
* "a" and "b" to calculate their sum (a + b), that will be the first element of the compund pair rapresenting the type (MOut) our evaluator will return <br />
* "x and "y" (the output of each evaluation) to be concatenated with the ourput of formatLine by the expression (x ++ y ++ formatLine(...)): this will be the second element of the compound pair MOut, the string part.<br />
<br />
So we need to separate the pairs produced by "evalO t" and "evalO u".<br />
<br />
We do that within the where clause (remember: evalO now produces a value of type<br />
MOut Int, i.e. a pair of an Int and a String).<br />
<br />
Then we use the single element, "extraded" within the where clause, to<br />
return a new MOut composed by <br />
<br />
((a + b),(x ++ y ++ formatLine (Add t u) (a + b))).<br />
------ -------------------------------------<br />
Int Output = String<br />
<br />
== Let's Go Monadic==<br />
<br />
Is there a more general way of doing so?<br />
<br />
Let's analyze the evaluator from another perspective. From the type<br />
perspective.<br />
<br />
We solved our problem by creating a new type, a pair of an Int (the<br />
result of the evaluation) and a String (the output of the process of<br />
evaluation).<br />
<br />
The first part of the evaluator does nothing else but creating, from a<br />
value of type Int, an object of type MOut Int (Int,Output). It does so<br />
by creating a pair with that Int and some text produced by formatLine.<br />
<br />
The second part evaluates the two Term(s) and "stores" the values thus<br />
produced in some variables to be use later to compute the output.<br />
<br />
Let's focus on the "stores" action. The correct term should be<br />
"binds".<br />
<br />
Take a function:<br />
<haskell><br />
f x = x + x<br />
</haskell><br />
"x" appears on both sides of the expression. We say that on the right<br />
side "x" is bound to the value of x given on the left side.<br />
<br />
So<br />
<haskell><br />
f 3<br />
</haskell><br />
binds x to 3 for the evaluation of the expression "x + x".<br />
<br />
Our evaluator binds "a" and "x" / "b" and "y" with the evaluation of<br />
"evalO t" and "evalO u" respectively. <br />
<br />
Then "a","b","x" and "y" will be used in the evaluation of<br />
((a+b),(x++y++formatLine)), that will produce a value of type MOut Int:<br />
<br />
<pre><br />
<br />
((a + b),(x ++ y ++ formatLine (Add t u) (a + b))).<br />
------ -------------------------------------<br />
\ / \ /<br />
Int Output = String<br />
---------------------------------<br />
\ /<br />
MOut Int <br />
</pre><br />
<br />
The binding happens in the "where" clause:<br />
<haskell><br />
where (a, x) = evalO t<br />
(b, y) = evalO u<br />
</haskell><br />
<br />
We know that there is an ad hoc operator for binding variables to a<br />
value: lambda, or \.<br />
<br />
Indeed f x = x + x is syntactic sugar for:<br />
<haskell><br />
f = \x -> x + x<br />
</haskell><br />
When we write f 3 we are actually binding "x" to 3 within what's next<br />
"->", that will be used (substituted) for evaluating f 3.<br />
<br />
So we can try to abstract this phenomenon.<br />
<br />
===Monadic evaluator with output===<br />
What we need is a function that takes our composed type MOut Int and a<br />
function in order to produce a new MOut Int, concatenating the<br />
output of the computation of the first with the output of the<br />
computation of the second.<br />
<br />
This is what bindM does:<br />
<br />
<haskell><br />
<br />
> bindM :: MOut a -> (a -> MOut b) -> MOut b<br />
> bindM m f = (b, x ++ y)<br />
> where (a, x) = m<br />
> (b, y) = f a<br />
<br />
</haskell><br />
<br />
It takes:<br />
* "m": the compound type MOut Int carrying the result of an "eval Term",<br />
* a function "f". This function will take the Int ("a") extracted by the evaluation of "m" ((a,x)=m). This function will produce a new pair: a new Int produced by a new evaluation; some new output.<br />
<br />
bindM will return the new Int in pair with the concatenated outputs<br />
resulting from the evaluation of "m" and "f a".<br />
<br />
As you see, we took the binding part out from evalO and put it in this new function.<br />
<br />
So let's write the new version of the evaluator, that we will call evalM_1:<br />
<br />
<haskell><br />
<br />
> evalM_1 :: Term -> MOut Int<br />
> evalM_1 (Con a) = (a, formatLine (Con a) a)<br />
> evalM_1 (Add t u) = bindM (evalM_1 t) (\a -> <br />
> bindM (evalM_1 u) (\b -> <br />
> ((a + b), formatLine (Add t u) (a + b))<br />
> )<br />
> )<br />
<br />
</haskell><br />
<br />
Ugly, isn't it?<br />
<br />
Let's start from the outside:<br />
<br />
<haskell><br />
bindM (evalM_1 u) (\b -> ((a + b), formatLine (Add t u) (a + b)))<br />
</haskell><br />
<br />
bindM takes the result of the evaluation "evalM_1 u", a type Mout Int,<br />
and a function. It will extract the Int from that type and use it to<br />
bind "b".<br />
<br />
So in bindM (evalM_1 u) (\b ->) "b" will be bound to the value<br />
returned by evalM_1 u, and this bound variable will be available in<br />
what comes after "->" as a bound variable (not free).<br />
<br />
Then the outer part (bindM (evalM_1 t) (\a...) will bind "a" to the<br />
value returned "evalM_1 t", the result of the evaluatuion of the first<br />
Term. This value is needed to evaluate "((a+b), formatLine...) and<br />
produce our final MOut Int.<br />
<br />
We can try to explain "bindM" in a different way by using more descriptive names.<br />
<br />
As we have seen, "bindM" extracts the Int part from our type. The Int<br />
part will be used for further computations and the Output part will be<br />
concatenated. As a result we will have a new pair with a new Int and<br />
an accumulated Output.<br />
<br />
The new version of "bindM":<br />
<haskell><br />
<br />
> getIntFromType typeMOut doSomething = (newInt,oldOutput ++ newOutput)<br />
> where (oldInt,oldOutput) = typeMOut<br />
> (newInt,newOutput) = (doSomething oldInt)<br />
<br />
</haskell><br />
<br />
As you can see it does the very same things that "bindM" does: it<br />
takes something of type MOut and a function to perform some<br />
computation with the Int part. <br />
<br />
In the "where" clause, the old Int and the old output<br />
will be extracted from our type MOut (first line of the "where"<br />
clause). <br />
<br />
A new Int and a new output will be extracted from evaluating<br />
(doSomething oldInt) in the second line.<br />
<br />
Our function will return the new Int and the concatenated outputs.<br />
<br />
We do not need to define our doSomething function, because it will be<br />
an anonymous function:<br />
<br />
<haskell><br />
<br />
> evaluator (Con a) = (a, "output-")<br />
> evaluator (Add t u) = <br />
> getIntFromType (evaluator t) <br />
> (\firstInt -> getIntFromType (evaluator u) <br />
> (\secondInt -> ((firstInt + secondInt),("-newoutput"))))<br />
<br />
</haskell><br />
<br />
As you can see we are feeding our "getIntFromType" with the evaluation<br />
of an expression ("evaluator t" and "evaluator u"). The second<br />
argument of "getIntFromType" is an anonymous function that takes the<br />
"oldInt" and does something with it.<br />
<br />
So we have a series of nested anonymous functions. Their arguments<br />
("\firstInt" and "\secondInt") will be used to produce the computation<br />
we need ("(firstInt + secondInt). Moreover "getIntFromType" will take<br />
care of concatenating the outputs.<br />
<br />
This is the result:<br />
<br />
*TheMonadicWay> evaluator (Add (Con 5) (Con 6))<br />
(11,"output-output--newoutput")<br />
*TheMonadicWay> <br />
<br />
Going back to our "bindM", we can now use lambda notation to write our<br />
evaluator in a more convinient way:<br />
<br />
<haskell><br />
<br />
> evalM_2 :: Term -> MOut Int<br />
> evalM_2 (Con a) = (a, formatLine (Con a) a)<br />
> evalM_2 (Add t u) = evalM_2 t `bindM` \a -><br />
> evalM_2 u `bindM` \b -><br />
> ((a + b), (formatLine (Add t u) (a + b)))<br />
<br />
</haskell><br />
<br />
Now, look at the first part:<br />
<br />
<haskell><br />
evalM_2 (Con a) = (a, formatLine (Con a) a)<br />
</haskell><br />
<br />
We could use a more general way of creating some output. <br />
<br />
We can create a function that takes an Int and returns the type MOut<br />
Int. We do that by pairing the received Int with an empty string "".<br />
<br />
This will be a general way of creating an object with type MOut Int starting from an Int.<br />
<br />
Or, more generaly, a function that takes something of a variable type<br />
a, and return an object of type MOut a, a coumpunt object made up of<br />
an element of type a, and one of type String.<br />
<br />
There it is:<br />
<br />
<haskell><br />
<br />
> mkM :: a -> MOut a<br />
> mkM a = (a, "")<br />
<br />
</haskell><br />
<br />
As you can see, this function will just push an Int and an empty<br />
string ("") inside our type MOut.<br />
<br />
Then we need a method of inserting some text in our object of type<br />
MOut. So we will take a string and return it paired with a void<br />
element "()":<br />
<br />
<haskell><br />
<br />
> outPut :: Output -> MOut ()<br />
> outPut x = ((), x)<br />
<br />
</haskell><br />
<br />
Very simple: we have a string "x" (Output) and create a pair with a ()<br />
instead of an Int, and the output.<br />
<br />
You can see this function as one that pushes a string, paired with a<br />
void int, inside our type MOut.<br />
<br />
Now we can rewrite:<br />
<haskell><br />
evalM_2 (Con a) = (a, formatLine (Con a) a)<br />
</haskell><br />
using the bindM function:<br />
<haskell><br />
evalM_2 (Con a) = outPut (formatLine (Con a) a) `bindM` \_ -> mkM a<br />
</haskell><br />
<br />
First we create an object of type MOut with the Int part (). As you<br />
see bindM will not use it ("\_"), but will concatenate the String part<br />
with the result of mkM, which in turn is the empry string "".<br />
<br />
In other words, first we insert the Output part (a string) in our<br />
MOut, and then we insert the Int paired with an empty string: "bindM"<br />
will not use the void int (the anonymous function will not use it's<br />
argument: "\_"), but will take care of concatenating the non empty<br />
string inserted by "outPut" with the empty one inserted by "mkM".<br />
<br />
Let's rewrite the evaluator:<br />
<br />
<haskell><br />
<br />
> evalM_3 :: Term -> MOut Int<br />
> evalM_3 (Con a) = outPut (formatLine (Con a) a) `bindM` \_ -> <br />
> mkM a<br />
> evalM_3 (Add t u) = evalM_3 t `bindM` \a -><br />
> evalM_3 u `bindM` \b -><br />
> outPut (formatLine (Add t u) (a + b)) `bindM` \_ -> <br />
> mkM (a + b)<br />
<br />
</haskell><br />
<br />
Well, this is fine, definetly better then before, anyway.<br />
<br />
Still we use `bindM` \_ -> that binds something we do not use (_). We<br />
could write a function for this specific case, when we concatenate<br />
computations without the need of binding variables for later uses.<br />
Let's call it `combineM`:<br />
<br />
<haskell><br />
<br />
> combineM :: MOut a -> MOut b -> MOut b<br />
> combineM m f = m `bindM` \_ -> f<br />
<br />
</haskell><br />
<br />
This is just something that will allow us to write the evaluator in a<br />
more concise way. <br />
<br />
So the new evaluator:<br />
<br />
<haskell><br />
<br />
> evalM :: Term -> MOut Int<br />
> evalM (Con a) = outPut (formatLine (Con a) a) `combineM` <br />
> mkM a<br />
> evalM (Add t u) = evalM t `bindM` \a -><br />
> evalM u `bindM` \b -><br />
> outPut (formatLine (Add t u) (a + b)) `combineM` <br />
> mkM (a + b)<br />
<br />
</haskell><br />
<br />
Let's put everything together (changing M into MO, so that this file<br />
will be still usable as a Literate Haskell file):<br />
<br />
<haskell><br />
<br />
> type MO a = (a, Out)<br />
> type Out = String<br />
<br />
> mkMO :: a -> MO a<br />
> mkMO a = (a, "")<br />
<br />
> bindMO :: MO a -> (a -> MO b) -> MO b<br />
> bindMO m f = (b, x ++ y)<br />
> where (a, x) = m<br />
> (b, y) = f a<br />
<br />
> combineMO :: MO a -> MO b -> MO b<br />
> combineMO m f = m `bindM` \_ -> f<br />
<br />
> outMO :: Out -> MO ()<br />
> outMO x = ((), x)<br />
<br />
> evalMO :: Term -> MO Int<br />
> evalMO (Con a) = outMO (formatLine (Con a) a) `combineMO`<br />
> mkMO a<br />
> evalMO (Add t u) = evalMO t `bindMO` \a -><br />
> evalMO u `bindMO` \b -><br />
> outMO (formatLine (Add t u) (a + b)) `combineMO` <br />
> mkMO (a + b)<br />
<br />
</haskell><br />
<br />
==What Does Bind Bind?==<br />
<br />
<div id="Bind"><br />
The evaluator looks like:<br />
<haskell><br />
evalM t >>= \a -> evalM u >>= \b -> outPut "something" >>= \_ -> mkM (a +b)<br />
</haskell><br />
where >>= is bindMO, obviously.<br />
<br />
Let's do some substitution, writing the type of their output of each function:<br />
* evalMO t => (a,Out) - where a is Int<br />
* evalMO u => (b,Out) - where b is the same of a, an Int, but with a different value<br />
* outMO Out = ((),Out)<br />
* mkMO (a+b) => ((a+b),Out) - where (a+b) is the same of a and b, but with a different value from either a and b<br />
<br />
<pre><br />
B | (a,Out) >>= \a -> (b,Out) >>= \b -> ((),Out) >>= \_ >>= ((a + b), Out)---\<br />
i | V V V V V V V V ^ ^ ^ ^ |\<br />
n | |__|________^ | | ^ | | | | | | | MOut Int <=> ((a+b), Out)<br />
d |_____|__(++)__|_Out_|__|__(++)__V_Out_|___|___(++)_|_(++)__|___|____|_____|/<br />
i | | |______(b)__|_____|_____(b)____|__(b)__|___|<br />
n | |_________(a)___________|____________|__(a)__|<br />
g | |_____()_____|<br />
<br />
</pre><br />
<br />
Clear, isn't it?<br />
<br />
"bindMO" is just a function that takes care of gluing together, inside<br />
a data type, a sequence of computations!<br />
<br />
== Some Sugar, Please!==<br />
Now our evaluator has been completely transformed into a monadic<br />
evaluator. That's what it is: a monad.<br />
<br />
We have a function that constructs an object of type MO Int, formed by<br />
a pair: the result of the evaluation and the accumulated<br />
(concatenated) output.<br />
<br />
The process of accumulation and the act of parting the MO Int into its<br />
component is buried into bindMO, now, that can also preserve some<br />
value for later uses.<br />
<br />
So we have:<br />
* MO a type constructor for a type carrying a pair composed by an Int and a String;<br />
* bindMO, that gives a direction to the process of evaluation: it concatenates computations and captures some side effects we created (the direction is given by the changes in the Out part: there's a "before" when Out was something and there's a "later" when Out is something else).<br />
* mkMO lets us create an object of type MO Int starting from an Int.<br />
<br />
As you see this is all we need to create a monad. In other words<br />
monads arise from the type system and the lambda calculus. Everything<br />
else is just syntactic sugar.<br />
<br />
So, let's have a look at that sugar: the famous do-notation!<br />
<br />
===Monadic evaluator with output in do-notation===<br />
<br />
In order to be able to use the "do-notation" we need to define a new<br />
type and make it an instance of the Monad class. To make a new type an<br />
instance of the Monad class we will have to define the two methods of<br />
this class: (>>=) and "return".<br />
<br />
This is not going to be difficult, because we already created these<br />
two methods: "bindM" and "mkM". Now we will have to rewrite them in<br />
order to reflect the fact that we are not going to use a type, for our<br />
evaluator, that is a synonymous of other types, as we did before.<br />
Indeed our MOut was defined with the "type" keyword. Now we will have<br />
to define a "real" new type with either "newtype" or "data". Since we<br />
are not going to need multiple constructors, we will use "newtype".<br />
<br />
<div id="MonadicEvalIO"><br />
<haskell><br />
<br />
> newtype Eval_IO a = Eval_IO (a, O)<br />
> deriving (Show)<br />
> type O = String<br />
<br />
</haskell><br />
<br />
This is our new type: it will have a single type constructor, whose<br />
name is the same of the type name ("Eval_IO"). The type constructor<br />
takes a parameter ("a"), a variable type, and will build a type formed<br />
by a type "a" (an Int in our case) and a String (O is indeed<br />
synonymous of String).<br />
<br />
We now need to define our "bind" function to reflect the fact that we<br />
are now using a "real" type, and, to unpack its content, we need to do<br />
pattern-matching we the type constructor "Eval_IO". Moreover, since we<br />
must return an Eval_IO type, we will use the type constructor also for<br />
building the new type with the new int and the concatenated output.<br />
<br />
For the rest our "bind" function will be identical to the one we<br />
defined before.<br />
<br />
We are going to use very descriptive names:<br />
<br />
<haskell><br />
<br />
> getInt monad doSomething = Eval_IO (newInt,oldOutput ++ newOutput)<br />
> where Eval_IO (oldInt,oldOutput) = monad<br />
> Eval_IO (newInt,newOutput) = (doSomething oldInt)<br />
<br />
</haskell><br />
<br />
As you can see, we are using Eval_IO to build the result of the<br />
computation to be returned by getInt: "Eval_IO (newInt,oldOutput ++<br />
newOutput)". And we are using it to match the internal components of<br />
our type in the "where" clause.<br />
<br />
We also need to create a function that, like mkO, will take an Int and,<br />
using the type constructor "Eval_IO", will create an object of type<br />
Eval_IO with that Int and an empty string:<br />
<br />
<haskell><br />
<br />
> createEval_IO :: a -> Eval_IO a<br />
> createEval_IO int = Eval_IO (int,"")<br />
<br />
</haskell><br />
<br />
And, finally, we need a function that will insert, in our type, a<br />
string and a void ():<br />
<br />
<haskell><br />
<br />
> print_IO :: O -> Eval_IO ()<br />
> print_IO string = Eval_IO ((), string)<br />
<br />
</haskell> <br />
<br />
With these functions we could write our monadic evaluator without the<br />
"do-notation" like this:<br />
<br />
<haskell><br />
<br />
> evalM_4 :: Term -> Eval_IO Int<br />
> evalM_4 (Con a) = createEval_IO a<br />
> evalM_4 (Add t u) = evalM_4 t `getInt` \a -><br />
> evalM_4 u `getInt` \b -><br />
> print_IO (formatLine (Add t u) (a + b)) `getInt` \_ -><br />
> createEval_IO (a + b)<br />
<br />
</haskell><br />
<br />
It is very similar to the previous evaluator, as you can see. The only<br />
differences are related to the fact that we are now using a "real"<br />
type and not a type synonymous: this requires the use of the type<br />
constructor to match the type and its internal part (as we do in the<br />
"where" clause of our "bind" function: "getInt") or to build the type<br />
(as we do in the "bind" function to return the new Int with the<br />
concatenated output).<br />
<br />
Running this evaluator will produce:<br />
<br />
*TheMonadicWay> evalM_4 (Add (Con 6) (Con 12))<br />
Eval_IO (18,"eval (Add (Con 6) (Con 12)) <= 18 - ")<br />
*TheMonadicWay> <br />
<br />
Now we have everything we need to declare our type, Eval_IO, as an<br />
instance of the Monad class:<br />
<haskell><br />
<br />
> instance Monad Eval_IO where<br />
> return a = createEval_IO a<br />
> (>>=) m f = getInt m f<br />
<br />
</haskell><br />
<br />
As you see we are just using our defined functions as methods for our<br />
instance of the Monad class.<br />
<br />
This is all we need to do. Notice that we do not have to define the<br />
"combineM" function, for chaining computation, as we do with "getInt",<br />
without binding variables for later use within the series of nested<br />
anonymous functions (the "doSomething" part) that will form the "do"<br />
block.<br />
<br />
This function comes for free by just defining our type as an instance<br />
of the Monad class. Indeed, if you look at the definition of the Monad<br />
class in the Prelude you see that "combineM", or (>>) is deduced by<br />
the definition of (>>=):<br />
<br />
<haskell><br />
class Monad m where<br />
return :: a -> m a<br />
(>>=) :: m a -> (a -> m b) -> m b<br />
(>>) :: m a -> m b -> m b<br />
fail :: String -> m a<br />
<br />
-- Minimal complete definition: (>>=), return<br />
p >> q = p >>= \ _ -> q<br />
fail s = error s<br />
</haskell><br />
<br />
You can see that the "combineM"" method (or (>>)) is automatically<br />
derived by the "bindMO" (or >>=) method:<br />
<br />
<haskell><br />
p >> q = p >>= \ _ -> q<br />
</haskell><br />
<br />
We can now write our evaluator using the do-notation:<br />
<br />
<haskell><br />
<br />
> eval_IO :: Term -> Eval_IO Int<br />
> eval_IO (Con a) = do print_IO (formatLine (Con a) a)<br />
> return a<br />
> eval_IO (Add t u) = do a <- eval_IO t<br />
> b <- eval_IO u<br />
> print_IO (formatLine (Add t u) (a + b))<br />
> return (a + b)<br />
<br />
</haskell><br />
<br />
As you can see the anonymous functions are gone. Instead we use this:<br />
a <- eval_IO t<br />
<br />
This seems like an assignment, that cannot be possible in Haskell. In<br />
fact it is just the way our anonymous function's arguments is bound<br />
within a do block.<br />
<br />
Even if it does not seem like a series of nested anonymous functions,<br />
this is what actually a do block is.<br />
<br />
Our monad is defined by three elements: <br />
* a type, with its type constructor(s);<br />
* a bind method: it will bind an unwritten anonymous function's argument to the value of the Int part of our type, or more generally, of the variable type "a". It will also create a series of anonymous functions: a line for each function;<br />
* a "return" function, to insert, into out type, a value of type Int, or, more generally, a value of variable type "a".<br />
<br />
Additionally, we need a function to insert a string in our type,<br />
string that the derived "bind" (>>) will concatenate ignoring the void<br />
(), our "print_IO".<br />
<br />
Within a do block we can thus perform only tree kind of operations:<br />
* a computation that produces a new Int, packed inside our monad's type, to be extracted and bound to a variable (an anonymous function's argument really):<br />
** this operation requires a binding (">>= \varName ->"), translated into "varName <- computation"<br />
** example: a <- eval_IO t<br />
* a computation that inserts a string into our monad, a string to be concatenated, without the need of binding a variable (an anonymous function's argument really):<br />
** this operation does not require a binding: it will be ">>= \_ ->", i.e. ">>", translated into a simple new line<br />
** example: print_IO (formatLine (Add t u) (a + b))<br />
* a computation that inserts an Int into our monad without the need of binding a variable (an anonymous function's argument really):<br />
** this operation is carried out by the <hask>return</hask> method (usually at the end of a do block, useless in the middle)<br />
** example <hask>return (a + b)</hask><br />
<br />
Let's see the evaluator with output in action:<br />
*TheMonadicWay> eval_IO (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) <br />
Eval_IO (54,"eval (Con 6) <= 6 - eval (Con 16) <= 16 - eval (Con 20) <= 20 - eval (Con 12) <= 12 - \<br />
eval (Add (Con 20) (Con 12)) <= 32 - eval (Add (Con 16) (Add (Con 20) (Con 12))) <= 48 - \<br />
eval (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) <= 54 - ")<br />
*TheMonadicWay> <br />
<br />
Let's format the output part:<br />
eval (Con 6) <= 6 <br />
eval (Con 16) <= 16 <br />
eval (Con 20) <= 20 <br />
eval (Con 12) <= 12 <br />
eval (Add (Con 20) (Con 12)) <= 32 <br />
eval (Add (Con 16) (Add (Con 20) (Con 12))) <= 48 <br />
eval (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) <= 54 <br />
<br />
==Type and Newtype: What Happened to Our Output?==<br />
<br />
Well, actually something happened to the output. Let's compare the<br />
output of evalMO (the monadic evaluator written without the<br />
do-notation) and eval_IO:<br />
<br />
*TheMonadicWay> evalMO (Con 6)<br />
(6,"eval (Con 6) <= 6 - ")<br />
*TheMonadicWay> eval_IO (Con 6)<br />
Eval_IO (6,"eval (Con 6) <= 6 - ")<br />
*TheMonadicWay> <br />
<br />
They look almost the same, but they are not the same: the output of<br />
eval_IO has the Eval_IO stuff. It must be related to the changes we<br />
had to do to our evaluator in order to use the do-conation, obviously.<br />
<br />
We can now review some of our basic knowledge of Haskell's type<br />
system.<br />
<br />
What's changed? First the type definition. We have now:<br />
<br />
<haskell><br />
newtype Eval_IO a = Eval_IO (a, O)<br />
deriving (Show)<br />
</haskell><br />
<br />
instead of <br />
<br />
<haskell><br />
type MO a = (a, Out)<br />
</haskell><br />
<br />
Now <hask>return a</hask> is the product of the application of the<br />
type constructor Eval_IO to the pair that are going to form our monad.<br />
<br />
"return" takes an Int and inserts it into our monad. It will also<br />
insert an empty String "" that (>>=) or (>>) will then concatenate in<br />
the sequence of computations they glue together.<br />
<br />
The same for (>>=). It will now return something constructed by<br />
Eval_IO: <br />
<br />
* "newInt", the result of the application of "doSomething" to "oldInt" (better, the binding of "oldInt" in "doSomething");<br />
* the concatenation of "oldOutput" (matched by <hask>Eval_IO (oldInt, oldOutput)</hask> with the evaluation of "monad" - "eval_IO t") and "newOutput", (matched by "Eval_IO(newInt,newOutput)" with the evaluation of "(doSomething monad)" - "eval_IO u").<br />
<br />
That is to say: in the "where" clause, we are matching for the<br />
elements paired in a type Eval_IO: this is indeed the type of "monad"<br />
(corresponding to "eval_IO t" in the body of the evaluator) and<br />
"(doSomething monad)" (where "doSomething" correspond to the<br />
evaluation of "eval_IO u" within an anonymous function with \oldInt as<br />
its argument, argument bound to the result of the previous evaluation<br />
of "monad", that is to say "eval_IO t").<br />
<br />
And so, "Eval_IO (oldInt,oldOutput) = monad" means: match "oldInt" and<br />
"oldOutput", paired in a type Eval_IO, and that are produced by the<br />
evaluation of "monad" (that is to say: "eval_IO t"). The same for<br />
Eval_IO (newInt,newOutput): match "newInt" and "newOutput" produced by<br />
the evaluation of "(doSomething monad)".<br />
<br />
So the output of the evaluator is now not simply a pair made of and<br />
Int and a String. It is a specific type (Eval_IO) that happens to<br />
carry a pair of an Int and a String. But, if we want the Int and the<br />
string, we have to extract them from the Eval_IO type, as we do in the<br />
"where" clause: we ''unpack'' our type object (let's call it with its<br />
name: our monad!) and take out the Int and the String to feed the next<br />
function application and the output generation.<br />
<br />
The same to insert something in our monad: if we want to create a pair<br />
of an Int and a String, pair of type Eval_IO, we now have to ''pack''<br />
them together by using our type constructor, feeding it with a pair<br />
composed by and Int and a String. This is what we do with the "return"<br />
method of out monad and with "print_IO" function, where:<br />
* return insert into the monad an Int;<br />
* print_IO insert into the monad a String.<br />
<br />
So, why cannot we use the old <hask>type MO a = (a, Out)</hask> that<br />
did not required all this additional work (apart the need to<br />
specifically define (>>)?<br />
<br />
Type MO is just a synonymous for (a,Out): the two can be substituted<br />
one for the other. That's it.<br />
<br />
We did not have to pack "a" and "Out" together with a type constructor<br />
to have a new type MO.<br />
<br />
As a consequence, we cannot use MO as an instance of Monad, and so, we<br />
cannot use with it the syntactic sugar we needed: the do-notation.<br />
<br />
That is to say: a type created with the "type" keyword cannot be an<br />
instance of a class, and cannot inherits its methods (in our case<br />
(>>=, >> and return). And without those methods the do-notation is not<br />
usable.<br />
<br />
==Errare Monadicum Est==<br />
<br />
Now that we have a basic understanding of what a monad is, and does,<br />
we will further explore it by making some changes to our evaluator.<br />
<br />
In this section we will se how to handle exceptions in our monadic<br />
evaluator.<br />
<br />
Suppose that we want to stop the execution of our monad if some<br />
conditions occurs. If our evaluator was to compute divisions, instead<br />
of sums, then we would like to stop the evaluator when a division by<br />
zero occurs, possibly producing some output, instead of the result of<br />
the evaluation of the expression, that explains what happened.<br />
<br />
Basic error handling.<br />
<br />
We will do so starting from the beginning once again...<br />
<br />
===The basic evaluator, non monadic, with exception===<br />
<br />
We just take our basic evaluator, without any output, and write a<br />
method to stop execution if a condition occurs: <br />
<br />
<haskell><br />
<br />
> data M a = Raise Exception<br />
> | Return a<br />
> deriving (Show)<br />
> type Exception = String<br />
<br />
</haskell><br />
<br />
Now, our monad is of datatype "M a" which can either be constructed<br />
with the "Raise" constructor, that takes a String (Exception is a<br />
synonymous of String), or by the "Return" constructor, that takes a<br />
variable type ("a"), an Int in our case.<br />
<br />
<haskell><br />
<br />
> evalE :: Term -> M Int<br />
> evalE (Con a) = Return a<br />
<br />
</haskell><br />
<br />
If evalE matches a Con it will construct a type Return with, inside, the content of the Con.<br />
<br />
<haskell><br />
<br />
> evalE (Add a b) = <br />
> case evalE a of<br />
> Raise e -> Raise e<br />
> Return a -><br />
> case evalE b of <br />
> Raise e -> Raise e<br />
> Return b -><br />
> if (a+b) == 42<br />
> then Raise "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else Return (a+b)<br />
<br />
</haskell><br />
<br />
If evalE matches an Add it will check if evaluating the first part<br />
produces a "Raise" or a "Return": in the first case it will return a<br />
"Raise" whose content is the same received. <br />
<br />
If instead the evaluation produces a value of a type matched by<br />
"Return", the evaluator will evaluate the second term of Add.<br />
<br />
If this returns a "Raise", a "Raise" will be returned all the way up<br />
the recursion, otherwise the evaluator will check whether a condition<br />
for raising a "Raise" exists. If not, it will return a "Return" with<br />
the sum inside.<br />
<br />
Test it with:<br />
<br />
evalE (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2)))<br />
<br />
<br />
===The basic evaluator, monadic, with exceptions===<br />
<br />
In order to produce a monadic version of the previous evaluator, the<br />
one that raises exceptions, we just need to abstract out from the<br />
evaluator all that case analysis.<br />
<br />
<haskell><br />
<br />
> data M1 a = Except Exception<br />
> | Ok {showM :: a }<br />
> deriving (Show)<br />
<br />
</haskell><br />
<br />
The data type didn't change at all. Well, we changed the name of the<br />
Return type constructor (now Ok) so that this constructor can coexist<br />
with the previous one in the same Literate Haskell file.<br />
<br />
<div id="MonadicEvalE"><br />
<haskell><br />
<br />
> instance Monad M1 where<br />
> return a = Ok a<br />
> m >>= f = case m of<br />
> Except e -> Except e<br />
> Ok a -> f a<br />
<br />
</haskell><br />
<br />
Binding operations are now very easy. Basically we check:<br />
* if the result of the evaluation of "m" produces an exception (first match: Except e ->...), in which case we return its content by constructing our M1 Int with the "Raise" constructor".<br />
* if the result of the evaluation of "m" is matched with the "Ok" constructor, we get its content and use it to bind the argument of "f" to its value.<br />
<br />
<hask>return a</hask> will just use the Ok type constructor for<br />
inserting "a" (in our case an Int) into M1 Int, the type of our monad.<br />
<br />
<haskell><br />
<br />
> raise :: Exception -> M1 a<br />
> raise e = Except e<br />
<br />
</haskell><br />
<br />
This is just a helper function to construct our "M1 a" type with the<br />
Raise constructor. It takes a string and returns a type (M1 a) to be<br />
matched with the "Raise" constructor.<br />
<br />
<haskell><br />
<br />
> eval_ME :: Term -> M1 Int<br />
> eval_ME (Con a) = do return a<br />
> eval_ME (Add t u) = do a <- eval_ME t<br />
> b <- eval_ME u<br />
> if (a+b) == 42<br />
> then raise "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else return (a + b)<br />
<br />
</haskell><br />
<br />
The evaluator itself is very simple. We bind "a" with the result of<br />
"eval_ME t", "b" with the result of "eval_ME u", and we check for a<br />
condition: <br />
* if the condition is met we raise an exception, that is to say: we return a value constructed with the "Raise" constructor. This value will be matched by ">>=" in the next recursion. And >>= will just return it all the way up the recursion.<br />
* if the condition is not met, we return a value constructed with the "Return" type constructor and go on with the recursion.<br />
<br />
Run with:<br />
<br />
eval_ME (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2)))<br />
<br />
It is noteworthy the fact that in our datatype definition we used a<br />
label field with a label selector (we called it showM), even though it<br />
was not used in our code. We will use this methodology later on.<br />
<br />
So, just to refresh your memory:<br />
<br />
<haskell><br />
<br />
> data Person = Person {name :: String,<br />
> age :: Int,<br />
> hobby :: String<br />
> } deriving (Show)<br />
<br />
> andreaRossato = Person "Andrea" 37 "Haskell The Monadic Way"<br />
> personName (Person a b c) = a<br />
<br />
</haskell><br />
<br />
will produce:<br />
*TheMonadicWay> andreaRossato<br />
Person {name = "Andrea", age = 37, hobby = "Haskell The Monadic Way"}<br />
*TheMonadicWay> personName andreaRossato<br />
"Andrea"<br />
*TheMonadicWay> name andreaRossato<br />
"Andrea"<br />
*TheMonadicWay> age andreaRossato<br />
37<br />
*TheMonadicWay> hobby andreaRossato<br />
"Haskell The Monadic Way"<br />
*TheMonadicWay> <br />
<br />
<br />
===Monadic evaluator with output and exceptions===<br />
<br />
We will now try to combine the [[The Monadic Way Part I#MonadicEvalIO|output-producing monadic evaluator]] <br />
with [[The Monadic Way Part I#MonadicEvalE| exception producing one]].<br />
<br />
<br />
<haskell><br />
<br />
> data M2 a = Ex Exception<br />
> | Done {unpack :: (a,O) }<br />
> deriving (Show)<br />
<br />
</haskell><br />
<br />
Now we need a datatype with two constructor: one to produce a value<br />
type "M2 a" using "Ex String" and one for value type "M2 a" (Int in<br />
this case) using "Done a".<br />
<br />
'''Note''' that we changed the name of the exception type constructor<br />
from "Raise" to "Ex" just to make the two coexist in the same Literate<br />
Haskell file.<br />
<br />
The constructor "Done a" is defined with a label sector: <hask>Done {unpack :: (a,O)}</hask> <br />
and is equivalent to <hask>Done (a,O)</hask>. <br />
<br />
The only difference is that, this way, we are also defining a method<br />
to retrieve the pair (a,O) (in our case "O" is a synonymous for<br />
String, whereas "a" is a variable type) from an object of type "Done<br />
a".<br />
<br />
<haskell><br />
<br />
> instance Monad M2 where<br />
> return a = Done (a, "")<br />
> m >>= f = case m of<br />
> Ex e -> Ex e<br />
> Done (a, x) -> case (f a) of<br />
> Ex e1 -> Ex e1<br />
> Done (b, y) -> Done (b, x ++ y)<br />
<br />
</haskell><br />
<br />
Now our binding operations gets more complicated by the fact that we<br />
have to concatenate the output, as we did before, '''and''' check for<br />
exceptions.<br />
<br />
It is not possible to do has we did in the [[The Monadic Way Part I#MonadicEvalE| exception producing evaluator]], <br />
where we could check just for "m" (remember the "m" in the first run<br />
stands for "eval t").<br />
<br />
Since at the end we must return the output produced by the evaluation<br />
of "m" ''concatenated'' with the output produced by the evaluation of<br />
"f a" (where "a" is returned by "m", paired with "x" by "Done"), now<br />
we must check if we '''do have''' an output from "f a" produced by<br />
"Done".<br />
<br />
Indeed, now, "f a" can also produce a value constructed by "Ex", and<br />
this value does not contain the pair as the value produced by "Done".<br />
<br />
So, we evaluate "m": <br />
* if we match a value produced by type constructor "Ex" we return a value produced by type constructor "Ex" whose content is the one we extracted in the matching;<br />
* if we match a value produced by "Done" we match the pair it carries "(a,x)" and we analyze what "f a" returns:<br />
** if "f a" returns a value produced by "Ex" we extract the exception and we return it, constructing a value with "Ex"<br />
** if "f a" returns a value produced by "Done" we return "b" and the concatenated "x" and "y". <br />
<br />
And now the evaluator:<br />
<br />
<haskell><br />
<br />
> raise_IOE :: Exception -> M2 a<br />
> raise_IOE e = Ex e<br />
<br />
</haskell><br />
<br />
This is the function to insert in our monad (M2) an exception: we take<br />
a String and produce a value applying the type constructor for<br />
exception "Ex" to its value.<br />
<br />
<haskell><br />
<br />
> print_IOE :: O -> M2 ()<br />
> print_IOE x = Done ((), x)<br />
<br />
</haskell><br />
<br />
The function to produce output is the very same of the one of the [[The Monadic Way Part I#MonadicEvalIO|output-producing monadic evaluator]].<br />
<br />
<haskell><br />
<br />
> eval_IOE :: Term -> M2 Int<br />
> eval_IOE (Con a) = do print_IOE (formatLine (Con a) a)<br />
> return a<br />
> eval_IOE (Add t u) = do a <- eval_IOE t<br />
> b <- eval_IOE u<br />
> let out = formatLine (Add t u) (a + b)<br />
> print_IOE out<br />
> if (a+b) == 42<br />
> then raise_IOE $ out ++ "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else return (a + b)<br />
<br />
</haskell><br />
<br />
The evaluator procedure did not change very much from the one of the [[The Monadic Way Part I#MonadicEvalIO|output-producing monadic evaluator]].<br />
<br />
We just added the case analysis to see if the condition for raising an exception is met.<br />
<br />
Running with<br />
<br />
eval_IOE (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2)))<br />
<br />
will produce <br />
<br />
Ex "eval (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2))) <= 42 - <br />
The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
<br />
Look at the <hask>let</hask> clause within the do-notation. We do not<br />
need to use the "let ... in" construction: since all bound variables<br />
remain bound within a <hask>do</hask> procedure (see [[The Monadic Way Part I#Bind|here]]), <br />
we do not need the "in" to specify "where" the variable "out" will be bound in!<br />
<br />
==We Need A State==<br />
<br />
We will keep on adding complexity to our monadic evaluator and this<br />
time we will add a counter. We just want to count the number of<br />
iterations (the number of times "eval" will be called) needed to<br />
evaluate the expression.<br />
<br />
===The basic evaluator, non monadic, with a counter===<br />
<br />
As before we will start by adding this feature to our [[The Monadic Way Part I#BasicEval|basic evaluator]]. <br />
<br />
A method to count the number of iterations, since the lack of<br />
assignment and destructive updates (such as for i=0;i<10;i++;),<br />
is to add an argument to our function, the initial state, number that<br />
in each call of the function will be increased and passed to the next<br />
function call.<br />
<br />
And so, very simply:<br />
<br />
<haskell><br />
<br />
> -- non monadic<br />
> type St a = State -> (a, State)<br />
> type State = Int<br />
> evalNMS :: Term ->St Int<br />
> evalNMS (Con a) x = (a, x + 1)<br />
> evalNMS (Add t u) x = let (a, y) = evalNMS t x in<br />
> let (b, z) = evalNMS u y in<br />
> (a + b, z +1)<br />
<br />
</haskell><br />
<br />
Now evalNMS takes two arguments: the expression of type Term and an<br />
State (which is a synonymous for Int), and will produce a pair<br />
(a,State), that is to say a pair with a variable type "a" and an Int.<br />
<br />
The operations in the evaluator are very similar to the non monadic [[The Monadic Way Part I#BasivEvalO|output producing evaluator]].<br />
<br />
We are now using the "let ... in" clause, instead of the "where", and we are increasing the counter "z" the comes from the evaluation of the second term, but the basic operation are the same:<br />
* we evaluate "evalNMS t x" where "x" is the initial state, and we match and bind the result in "let (a, y) ... in"<br />
* we evaluate "evalNMS u y", where "y" was bound to the value returned by the previous evaluation, and we match and bind the result in "let (b, z) ... in"<br />
* we return a pair formed by the sum of the result (a+b) and the state z increased by 1. <br />
<br />
Let's try it:<br />
<br />
*TheMonadicWay> evalNMS (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2))) 0<br />
(42,7)<br />
*TheMonadicWay> <br />
<br />
As you see we must pass to "evalNMS"the initial state of our counter: 0.<br />
<br />
Look at the type signature of the function "evalNMS":<br />
<haskell><br />
evalNMS :: Term ->St Int<br />
</haskell><br />
<br />
From this signature you could argue that our function takes only<br />
'''one''' argument. But since our type St is defined with the "type"<br />
keyword, St can be substituted with what comes after the "=" sign. So,<br />
the real type signature of our function is:<br />
<br />
<haskell><br />
evalNMS :: Term -> State -> (Int,State)<br />
</haskell><br />
<br />
<div if="typeNewtype"><br />
Just to refresh your memory:<br />
<br />
<haskell><br />
<br />
> type IamAfunction a = (a -> a)<br />
> newtype IamNotAfunction a = NF (a -> a)<br />
> newtype IamNotAfunctionButYouCanUnPackAndRunMe a = F { unpackAndRun :: (a -> a) }<br />
<br />
> a = \x -> x * x<br />
<br />
> a1 :: IamAfunction Int<br />
> a1 = a<br />
<br />
> a2 :: IamNotAfunction Int<br />
> a2 = NF a<br />
<br />
> a3 :: IamNotAfunctionButYouCanUnPackAndRunMe Int<br />
> a3 = F a<br />
<br />
</haskell><br />
<br />
<br />
*TheMonadicWay> a 4<br />
16<br />
*TheMonadicWay> a1 4<br />
16<br />
*TheMonadicWay> a2 4<br />
<br />
<interactive>:1:0:<br />
The function `a2' is applied to one arguments,<br />
but its type `IamNotAfunction Int' has only 0<br />
In the definition of `it': it = a2 4<br />
*TheMonadicWay> a3 4<br />
<br />
<interactive>:1:0:<br />
The function `a3' is applied to one arguments,<br />
but its type `IamNotAfunctionButYouCanUnPackAndRunMe Int' has only 0<br />
In the definition of `it': it = a3 4<br />
*TheMonadicWay> unpackAndRun a3 4<br />
16<br />
*TheMonadicWay><br />
<br />
This means that "a1" is a partial application hidden by a type<br />
synonymous. <br />
<br />
"a2" and "a3" are not function types. They are types that<br />
have a functional value. <br />
<br />
Moreover, since we defined the type constructor of type<br />
"IamNotAfunctionButYouCanUnPackAndRunMe", F, with a label field, in<br />
that label field we defined a method (a label selector) to "extract"<br />
the function from the type "IamNotAfunctionButYouCanUnPackAndRunMe",<br />
and run it:<br />
<br />
<haskell><br />
unpackAndRun a3 4<br />
</haskell><br />
<br />
And what about "a2"? Is it lost forever?<br />
<br />
Obviously not! We need to write a function that unpacks a type<br />
"IamNotAfunction", using its type constructor NF to match the internal<br />
function:<br />
<br />
<haskell><br />
<br />
> unpackNF :: IamNotAfunction a -> a -> a<br />
> unpackNF (NF f) = f<br />
<br />
</haskell><br />
<br />
and run:<br />
<br />
*TheMonadicWay> unpackNF a2 4<br />
16<br />
*TheMonadicWay> <br />
<br />
As you see, "unpackNF" definition is a partial application: we specify<br />
one argument to get a function that gets another argument.<br />
<br />
A label selector does the same thing.<br />
<br />
Later we will see the importance of this distinction, quite obvious<br />
for haskell gurus, but not for us. Till now.<br />
<br />
===The evaluator, monadic, with a counter, without do-notation===<br />
<br />
<br />
'''(Text to be done yet: just a summary)'''<br />
<br />
The moadic version without do notation.<br />
<br />
<haskell><br />
<br />
> -- monadic<br />
> type MS a = State -> (a, State)<br />
<br />
> mkMS :: a -> MS a<br />
> mkMS a = \x -> (a, x)<br />
<br />
> bindMS :: MS a -> (a -> MS b) -> MS b<br />
> bindMS m f = \x -> <br />
> let (a, y) = m x in<br />
> let (b, z) = f a y in<br />
> (b, z)<br />
<br />
> combineMS :: MS a -> MS b -> MS b<br />
> combineMS m f = m `bindMS` \_ -> f<br />
<br />
> incState :: MS ()<br />
> incState = \s -> ((), s + 1)<br />
<br />
> evalMS :: Term -> MS Int<br />
> evalMS (Con a) = incState `combineMS` mkMS a<br />
> evalMS (Add t u) = evalMS t `bindMS` \a -><br />
> evalMS u `bindMS` \b -><br />
> incState `combineMS` mkMS (a + b)<br />
<br />
> --evalMS (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) 0<br />
<br />
</haskell><br />
<br />
===The evaluator, monadic, with counter and output, without do-notation===<br />
<br />
Now we'll add Output to the stateful evaluator:<br />
<br />
<haskell><br />
<br />
> -- state and output<br />
<br />
> type MSO a = State -> (a, State, Output)<br />
<br />
> mkMSO :: a -> MSO a<br />
> mkMSO a = \s -> (a, s, "")<br />
<br />
> bindMSO :: MSO a -> (a -> MSO b) -> MSO b<br />
> bindMSO m f = \x -> <br />
> let (a, y, s1) = m x in<br />
> let (b, z, s2) = f a y in<br />
> (b, z, s1 ++ s2)<br />
<br />
> combineMSO :: MSO a -> MSO b -> MSO b<br />
> combineMSO m f = m `bindMSO` \_ -> f<br />
<br />
> incMSOstate :: MSO ()<br />
> incMSOstate = \s -> ((), s + 1, "")<br />
<br />
> outMSO :: Output -> MSO ()<br />
> outMSO = \x s -> ((),s, x)<br />
<br />
> evalMSO :: Term -> MSO Int<br />
> evalMSO (Con a) = incMSOstate `combineMSO` <br />
> outMSO (formatLine (Con a) a) `combineMSO` <br />
> mkMSO a<br />
> evalMSO (Add t u) = evalMSO t `bindMSO` \a -><br />
> evalMSO u `bindMSO` \b -><br />
> incMSOstate `combineMSO` <br />
> outMSO (formatLine (Add t u) (a + b)) `combineMSO`<br />
> mkMSO (a + b)<br />
<br />
> --evalMSO (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) 0<br />
<br />
</haskell><br />
<br />
===The monadic evaluator with output and counter in do-notation=== <br />
<br />
State, Output in do-notation. Look at how much the complexity of our<br />
(>>=) founction is increasing:<br />
<br />
<haskell><br />
<br />
> -- thanks to Brian Hulley<br />
<br />
> newtype MSIO a = MSIO (State -> (a, State, Output))<br />
> instance Monad MSIO where<br />
> return a = MSIO (\s -> (a, s, ""))<br />
> (MSIO m) >>= f = MSIO $ \x -><br />
> let (a, y, s1) = m x in<br />
> let MSIO runNextStep = f a in<br />
> let (b, z, s2) = runNextStep y in<br />
> (b, z, s1 ++ s2)<br />
<br />
<br />
> incMSOIstate :: MSIO ()<br />
> incMSOIstate = MSIO (\s -> ((), s + 1, ""))<br />
<br />
> print_MSOI :: Output -> MSIO ()<br />
> print_MSOI x = MSIO (\s -> ((),s, x))<br />
<br />
> eval_MSOI :: Term -> MSIO Int<br />
> eval_MSOI (Con a) = do incMSOIstate<br />
> print_MSOI (formatLine (Con a) a)<br />
> return a<br />
<br />
> eval_MSOI (Add t u) = do a <- eval_MSOI t<br />
> b <- eval_MSOI u<br />
> incMSOIstate<br />
> print_MSOI (formatLine (Add t u) (a + b))<br />
> return (a + b)<br />
<br />
> run_MSOI :: MSIO a -> State -> (a, State, Output)<br />
> run_MSOI (MSIO f) s = f s<br />
<br />
> --run_MSOI (eval_MSOI (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12))))) 0<br />
<br />
</haskell><br />
<br />
===Another version of the monadic evaluator with output and counter, in do-notation===<br />
<br />
This is e second version that exploit label fields in datatype to<br />
decrease the complexity of the binding operations.<br />
<br />
<haskell><br />
<br />
> -- Thanks Udo Stenzel<br />
<br />
> newtype Eval_SIO a = Eval_SIO { unPackMSIOandRun :: State -> (a, State, Output) }<br />
> instance Monad Eval_SIO where<br />
> return a = Eval_SIO (\s -> (a, s, ""))<br />
> (>>=) m f = Eval_SIO (\x -><br />
> let (a, y, s1) = unPackMSIOandRun m x in<br />
> let (b, z, s2) = unPackMSIOandRun (f a) y in<br />
> (b, z, s1 ++ s2))<br />
<br />
> incSIOstate :: Eval_SIO ()<br />
> incSIOstate = Eval_SIO (\s -> ((), s + 1, ""))<br />
<br />
> print_SIO :: Output -> Eval_SIO ()<br />
> print_SIO x = Eval_SIO (\s -> ((),s, x))<br />
<br />
> eval_SIO :: Term -> Eval_SIO Int<br />
> eval_SIO (Con a) = do incSIOstate<br />
> print_SIO (formatLine (Con a) a)<br />
> return a<br />
> eval_SIO (Add t u) = do a <- eval_SIO t<br />
> b <- eval_SIO u<br />
> incSIOstate<br />
> print_SIO (formatLine (Add t u) (a + b))<br />
> return (a + b)<br />
<br />
> --unPackMSIOandRun (eval_SIO (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12))))) 0<br />
<br />
</haskell><br />
<br />
<br />
==If There's A State We Need Some Discipline: Dealing With Complexity==<br />
<br />
In order to increase the complexity of our monad now we will try to<br />
mix State (counter), Exceptions and Output.<br />
<br />
This is an email [http://www.haskell.org/pipermail/haskell-cafe/2006-August/017672.html I send to the haskell-cafe mailing list]:<br />
<br />
<pre><br />
Now I'm trying to create a statefull evaluator, with output and<br />
exception, but I'm facing a problem I seem not to be able to<br />
conceptually solve.<br />
<br />
Take the code below.<br />
Now, in order to get it run (and try to debug) the Eval_SOI type has a<br />
Raise constructor that produces the same type of SOIE. Suppose instead it<br />
should be constructing something like Raise "something". <br />
Moreover, I wrote a second version of >>=, commented out.<br />
This is just to help me illustrate to problem I'm facing.<br />
<br />
Now, >>= is suppose to return Raise if "m" is matched against Raise<br />
(second version commented out).<br />
If "m" matches SOIE it must return a SOIE only if "f a" does not<br />
returns a Raise (output must be concatenated).<br />
<br />
I seem not to be able to find a way out. Moreover, I cannot understand<br />
if a way out can be possibly found. Something suggests me it could be<br />
related to that Raise "something".<br />
But my feeling is that functional programming could be something out<br />
of the reach of my mind... by the way, I teach Law, so perhaps you'll<br />
forgive me...;-)<br />
<br />
If you can help me to understand this problem all I can promise is<br />
that I'll mention your help in the tutorial I'm trying to write on<br />
"the monadic way"... that seems to lead me nowhere.<br />
<br />
Thanks for your kind attention.<br />
<br />
Andrea<br />
</pre><br />
<br />
This was the code:<br />
<br />
<haskell><br />
data Eval_SOI a = Raise { unPackMSOIandRun :: State -> (a, State, Output) }<br />
| SOIE { unPackMSOIandRun :: State -> (a, State, Output) }<br />
<br />
instance Monad Eval_SOI where<br />
return a = SOIE (\s -> (a, s, ""))<br />
m >>= f = SOIE (\x -><br />
let (a, y, s1) = unPackMSOIandRun m x in<br />
case f a of<br />
SOIE nextRun -> let (b, z, s2) = nextRun y in <br />
(b, z, s1 ++ s2)<br />
Raise e1 -> e1 y --only this happens<br />
<br />
)<br />
-- (>>=) m f = case m of<br />
-- Raise e -> error "ciao" -- why this is not going to happen?<br />
-- SOIE a -> SOIE (\x -><br />
-- let (a, y, s1) = unPackMSOIandRun m x in<br />
-- let (b, z, s2) = unPackMSOIandRun (f a) y in <br />
-- (b, z, s1 ++ s2)) <br />
<br />
<br />
incSOIstate :: Eval_SOI ()<br />
incSOIstate = SOIE (\s -> ((), s + 1, ""))<br />
<br />
print_SOI :: Output -> Eval_SOI ()<br />
print_SOI x = SOIE (\s -> ((),s, x))<br />
<br />
raise x e = Raise (\s -> (x,s,e))<br />
<br />
eval_SOI :: Term -> Eval_SOI Int<br />
eval_SOI (Con a) = do incSOIstate<br />
print_SOI (formatLine (Con a) a)<br />
return a<br />
eval_SOI (Add t u) = do a <- eval_SOI t<br />
b <- eval_SOI u<br />
incSOIstate<br />
print_SOI (formatLine (Add t u) (a + b))<br />
if (a + b) == 42 <br />
then raise (a+b) " = The Ultimate Answer!!"<br />
else return (a + b)<br />
<br />
runEval exp = case eval_SOI exp of<br />
Raise a -> a 0<br />
SOIE p -> let (result, state, output) = p 0 in<br />
(result,state,output)<br />
<br />
<br />
<br />
--runEval (Add (Con 10) (Add (Con 28) (Add (Con 40) (Con 2))))<br />
</haskell><br />
<br />
This code will produce <br />
<br />
eval (Con 10) <= 10 -<br />
eval (Con 28) <= 28 -<br />
eval (Con 40) <= 40 -<br />
eval (Con 2) <= 2 - = The Ultimate Answer!!<br />
eval (Add (Con 28) (Add (Con 40) (Con 2))) <= 70 -<br />
eval (Add (Con 10) (Add (Con 28) (Add (Con 40) (Con 2)))) <= 80 -<br />
<br />
The exception appears in the output, but executioon is not stopped.<br />
<br />
===Monadic evaluator with output, counter and exception, in do-notation===<br />
<br />
Brian Hulley [http://www.haskell.org/pipermail/haskell-cafe/2006-August/017680.html came up with this solution]:<br />
<br />
<haskell><br />
<br />
> -- thanks to Brian Hulley<br />
> data Result a<br />
> = Good a State Output<br />
> | Bad State Output Exception<br />
> deriving Show<br />
<br />
> newtype Eval_SIOE a = SIOE {runSIOE :: State -> Result a}<br />
<br />
> instance Monad Eval_SIOE where<br />
> return a = SIOE (\s -> Good a s "")<br />
> m >>= f = SIOE $ \x -><br />
> case runSIOE m x of<br />
> Good a y o1 -><br />
> case runSIOE (f a) y of<br />
> Good b z o2 -> Good b z (o1 ++ o2)<br />
> Bad z o2 e -> Bad z (o1 ++ o2) e<br />
> Bad z o2 e -> Bad z o2 e<br />
<br />
> raise_SIOE e = SIOE (\s -> Bad s "" e)<br />
<br />
> incSIOEstate :: Eval_SIOE ()<br />
> incSIOEstate = SIOE (\s -> Good () (s + 1) "")<br />
<br />
> print_SIOE :: Output -> Eval_SIOE ()<br />
> print_SIOE x = SIOE (\s -> Good () s x)<br />
<br />
<br />
> eval_SIOE :: Term -> Eval_SIOE Int<br />
> eval_SIOE (Con a) = do incSIOEstate<br />
> print_SIOE (formatLine (Con a) a)<br />
> return a<br />
> eval_SIOE (Add t u) = do a <- eval_SIOE t<br />
> b <- eval_SIOE u<br />
> incSIOEstate<br />
> let out = formatLine (Add t u) (a + b)<br />
> print_SIOE out<br />
> if (a+b) == 42<br />
> then raise_SIOE $ out ++ "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else return (a + b)<br />
<br />
> runEval exp = case runSIOE (eval_SIOE exp) 0 of<br />
> Bad s o e -> "Error at iteration n. " ++ show s ++ <br />
> " - Output stack = " ++ o ++ <br />
> " - Exception = " ++ e<br />
> Good a s o -> "Result = " ++ show a ++ <br />
> " - Iterations = " ++ show s ++ " - Output = " ++ o<br />
<br />
</haskell><br />
<br />
Run with runEval (Add (Con 18) (Add (Con 12) (Add (Con 10) (Con 2))))<br />
<br />
==Suggested Readings==<br />
<br />
Cale Gibbard, [http://haskell.org/haskellwiki/Monads_as_Containers Monads as Containers]<br />
<br />
Jeff Newbern, [http://www.nomaware.com/monads/html/index.html All About Monads]<br />
<br />
[http://haskell.org/haskellwiki/IO_inside IO Inside]<br />
<br />
[http://sigfpe.blogspot.com/2006/08/you-could-have-invented-monads-and.html You Could Have Invented Monads! (And Maybe You Already Have.) by sigfpe]<br />
<br />
<br />
==Acknowledgments==<br />
<br />
Thanks to Neil Mitchell, Daniel Fisher, Bulat Ziganzhin, Brian Hulley<br />
and Udo Stenzel for the invaluable help they gave, in the<br />
[http://www.haskell.org/mailman/listinfo/haskell-cafe haskell-cafe mailing list], <br />
in understanding this topic.<br />
<br />
I couldn't do it without their help.<br />
<br />
Obviously errors are totally mine. But this is a wiki so, please,<br />
correct them!<br />
<br />
- [[User:AndreaRossato|Andrea Rossato]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=The_Monadic_Way/Part_I&diff=5870The Monadic Way/Part I2006-09-08T11:57:52Z<p>AndreaRossato: minor corrections</p>
<hr />
<div>'''Note: this is the first part of [[The Monadic Way]]'''<br />
==An evaluation of Philip Wadler's "Monads for functional programming"==<br />
<br />
This tutorial is a "translation" of Philip Wadler's [http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf "Monads for functional programming"].<br />
(avail. from [http://homepages.inf.ed.ac.uk/wadler/topics/monads.html here])<br />
<br />
I'm a Haskell newbie trying to grasp such a difficult concept as the<br />
one of Monad and monadic computation.<br />
<br />
While [http://www.cs.utah.edu/~hal/htut/ "Yet Another Haskell Tutorial"] <br />
gave me a good understanding of the type system when it<br />
comes to monads I find it almost unreadable.<br />
<br />
But I had also Wadler's paper, and started reading it. Well, just<br />
wonderful! It explains how to ''create'' a monad!<br />
<br />
So I decided to "translate it", in order to clarify to myself the<br />
topic. And I'm now sharing this traslation ('''not completed yet'')<br />
with the hope it will be useful to someone else.<br />
<br />
Moreover, that's a wiki, so please improve it. And, specifically,<br />
correct my poor English. I'm Italian, after all.<br />
<br />
'''Note: The source of this page can be used as a Literate Haskell<br />
file and can be run with ghci or hugs: so cut paste change and run (in<br />
emacs for instance) while reading it...'''<br />
<br />
==A Simple Evaluator==<br />
<br />
Let's start with something simple: suppose we want to implement a new<br />
programming language. We just finished with<br />
[http://swiss.csail.mit.edu/classes/6.001/abelson-sussman-lectures/ Abelson and Sussman's Structure and Interpretation of Computer Programs] <br />
and we want to test what we have learned.<br />
<br />
Our programming language will be very simple: it will just compute the<br />
sum of two terms.<br />
<br />
So we have just one primitive operation (Add) that takes two constants<br />
and calculates their sum.<br />
<br />
Moreover we have just one kind of data type: Con a, which is an Int.<br />
<br />
For instance, something like:<br />
<br />
(Add (Con 5) (Con 6))<br />
<br />
should yeld:<br />
<br />
11<br />
<br />
===The basic evaluator===<br />
<br />
We will implement our language with the help of a data type<br />
constructor such as:<br />
<br />
<div id="BasicEval"><br />
<haskell><br />
<br />
> module TheMonadicWay where<br />
> data Term = Con Int<br />
> | Add Term Term<br />
> deriving (Show)<br />
<br />
</haskell><br />
<br />
After that we build our interpreter:<br />
<br />
<haskell><br />
<br />
> eval :: Term -> Int<br />
> eval (Con a) = a<br />
> eval (Add a b) = eval a + eval b<br />
<br />
</haskell><br />
<br />
That's it. Just an example:<br />
<br />
*TheMonadicWay> eval (Add (Con 5) (Con 6))<br />
11<br />
*TheMonadicWay><br />
<br />
Very very simple. The evaluator checks if its argument is of type Con<br />
Int: if it is it just returns the Int.<br />
<br />
If the argument is not of type Con, but it is of type Term, it<br />
evaluates the first Term and sums the result with the result of the<br />
evaluation of the second Term.<br />
<br />
As you may understand, our evaluator uses some of the powerful<br />
features of Haskell type system. Instead of writing a parser that<br />
takes a string (the user input) and transforms that string into an<br />
expression to be evaluated, we use the two type constructors defined<br />
for our data type Term (Con and Add) to build the expression - such as<br />
(Add (Con 5) (Con 6)) - and to match the expression's elements in our<br />
"eval" function.<br />
<br />
<br />
== Some Output, Please!==<br />
<br />
Now, that's fine, but we'd like to add some features, like providing<br />
some output, to show how the computation was carried out.<br />
<br />
Well, but Haskell is a pure functional language, with no side effects,<br />
we were told.<br />
<br />
Now we seem to be wanting to create a side effect of the computation,<br />
its output, and be able to stare at it...<br />
<br />
If we had some global variable to store the out that would be<br />
simple...<br />
<br />
But we can create the output and carry it along the computation,<br />
concatenating it with the old one, and present it at the end of the<br />
evaluation together with the evaluation of the expression given to our<br />
evaluator/interpreter!<br />
<br />
===The basic evaluator with output===<br />
<br />
Simple and neat:<br />
<div id="BasivEvalO"><br />
<haskell><br />
<br />
> type MOut a = (a, Output)<br />
> type Output = String<br />
> <br />
> formatLine :: Term -> Int -> Output<br />
> formatLine t a = "eval (" ++ show t ++ ") <= " ++ show a ++ " - " <br />
> <br />
> evalO :: Term -> MOut Int<br />
> evalO (Con a) = (a, formatLine (Con a) a)<br />
> evalO (Add t u) = ((a + b),(x ++ y ++ formatLine (Add t u) (a + b)))<br />
> where (a, x) = evalO t<br />
> (b, y) = evalO u<br />
<br />
</haskell><br />
<br />
Now we have what we want. But we had to change our evaluator quite a<br />
bit. <br />
<br />
First we added a function, formatLine, that takes an argument of type<br />
Term (the expression to be evaluated), one of type Int (the result of<br />
the evaluation of Term) and gives back an output of type Output (that<br />
is a synonymous of String). This is just a helper function to format<br />
the string to output. Not very interesting at all.<br />
<br />
The evaluator itself changed quite a lot! Now it has a different type<br />
signature: it takes an argument of type Term and produces a new type,<br />
we called it MOut, that is actually a compound pair of a variable type<br />
a (an Int in our evaluator) and a type Output, a string.<br />
<br />
So our evaluator, now, will take a Term (the type of the expressions<br />
in our new programming language) and will produce a pair, composed of<br />
the result of the evaluation (an Int) and the Output, a string.<br />
<br />
So far so good. But what's happening inside the evaluator?<br />
<br />
The first part will just return a pair with the number evaluated ("a")<br />
and the output formatted by formatLine.<br />
<br />
The second part does something more complicated: it returns a pair<br />
composed by <br />
1. the result of the evaluation of the right Term summed to the result<br />
of the evaluation of the second Term<br />
2. the output: the concatenation of the output produced by the<br />
evaluation of the right Term, the output produced by the evaluation of<br />
the left Term (each this evaluation returns a pair with the number and<br />
the output), and the formatted output of the evaluation.<br />
<br />
Let's try it:<br />
*TheMonadicWay> evalO (Add (Con 5) (Con 6))<br />
(11,"eval (Con 5) <= 5 - eval (Con 6) <= 6 - eval (Add (Con 5) (Con 6)) <= 11 - ")<br />
*TheMonadicWay><br />
<br />
It works! Let's put the output this way:<br />
eval (Con 5) <= 5 - <br />
eval (Con 6) <= 6 - <br />
eval (Add (Con 5) (Con 6)) <= 11 -<br />
<br />
Great! We are able to produce a side effect of our evaluation and<br />
present it at the end of the computation, after all.<br />
<br />
Let's have a closer look at this expression:<br />
<haskell><br />
<br />
evalO (Add t u) = ((a + b),(x ++ y ++ formatLine (Add t u) (a + b)))<br />
where (a, x) = evalO t<br />
(b, y) = evalO u<br />
<br />
</haskell><br />
<br />
Why all that? The problem is that we need:<br />
* "a" and "b" to calculate their sum (a + b), that will be the first element of the compund pair rapresenting the type (MOut) our evaluator will return <br />
* "x and "y" (the output of each evaluation) to be concatenated with the ourput of formatLine by the expression (x ++ y ++ formatLine(...)): this will be the second element of the compound pair MOut, the string part.<br />
<br />
So we need to separate the pairs produced by "evalO t" and "evalO u".<br />
<br />
We do that within the where clause (remember: evalO now produces a value of type<br />
MOut Int, i.e. a pair of an Int and a String).<br />
<br />
Then we use the single element, "extraded" within the where clause, to<br />
return a new MOut composed by <br />
<br />
((a + b),(x ++ y ++ formatLine (Add t u) (a + b))).<br />
------ -------------------------------------<br />
Int Output = String<br />
<br />
== Let's Go Monadic==<br />
<br />
Is there a more general way of doing so?<br />
<br />
Let's analyze the evaluator from another perspective. From the type<br />
perspective.<br />
<br />
We solved our problem by creating a new type, a pair of an Int (the<br />
result of the evaluation) and a String (the output of the process of<br />
evaluation).<br />
<br />
The first part of the evaluator does nothing else but creating, from a<br />
value of type Int, an object of type MOut Int (Int,Output). It does so<br />
by creating a pair with that Int and some text produced by formatLine.<br />
<br />
The second part evaluates the two Term(s) and "stores" the values thus<br />
produced in some variables to be use later to compute the output.<br />
<br />
Let's focus on the "stores" action. The correct term should be<br />
"binds".<br />
<br />
Take a function:<br />
<haskell><br />
f x = x + x<br />
</haskell><br />
"x" appears on both sides of the expression. We say that on the right<br />
side "x" is bound to the value of x given on the left side.<br />
<br />
So<br />
<haskell><br />
f 3<br />
</haskell><br />
binds x to 3 for the evaluation of the expression "x + x".<br />
<br />
Our evaluator binds "a" and "x" / "b" and "y" with the evaluation of<br />
"evalO t" and "evalO u" respectively. <br />
<br />
Then "a","b","x" and "y" will be used in the evaluation of<br />
((a+b),(x++y++formatLine)), that will produce a value of type MOut Int:<br />
<br />
<pre><br />
<br />
((a + b),(x ++ y ++ formatLine (Add t u) (a + b))).<br />
------ -------------------------------------<br />
\ / \ /<br />
Int Output = String<br />
---------------------------------<br />
\ /<br />
MOut Int <br />
</pre><br />
<br />
The binding happens in the "where" clause:<br />
<haskell><br />
where (a, x) = evalO t<br />
(b, y) = evalO u<br />
</haskell><br />
<br />
We know that there is an ad hoc operator for binding variables to a<br />
value: lambda, or \.<br />
<br />
Indeed f x = x + x is syntactic sugar for:<br />
<haskell><br />
f = \x -> x + x<br />
</haskell><br />
When we write f 3 we are actually binding "x" to 3 within what's next<br />
"->", that will be used (substituted) for evaluating f 3.<br />
<br />
So we can try to abstract this phenomenon.<br />
<br />
===Monadic evaluator with output===<br />
What we need is a function that takes our composed type MOut Int and a<br />
function in order to produce a new MOut Int, concatenating the<br />
output of the computation of the first with the output of the<br />
computation of the second.<br />
<br />
This is what bindM does:<br />
<br />
<haskell><br />
<br />
> bindM :: MOut a -> (a -> MOut b) -> MOut b<br />
> bindM m f = (b, x ++ y)<br />
> where (a, x) = m<br />
> (b, y) = f a<br />
<br />
</haskell><br />
<br />
It takes:<br />
* "m": the compound type MOut Int carrying the result of an "eval Term",<br />
* a function "f". This function will take the Int ("a") extracted by the evaluation of "m" ((a,x)=m). This function will produce a new pair: a new Int produced by a new evaluation; some new output.<br />
<br />
bindM will return the new Int in pair with the concatenated outputs<br />
resulting from the evaluation of "m" and "f a".<br />
<br />
As you see, we took the binding part out from evalO and put it in this new function.<br />
<br />
So let's write the new version of the evaluator, that we will call evalM_1:<br />
<br />
<haskell><br />
<br />
> evalM_1 :: Term -> MOut Int<br />
> evalM_1 (Con a) = (a, formatLine (Con a) a)<br />
> evalM_1 (Add t u) = bindM (evalM_1 t) (\a -> <br />
> bindM (evalM_1 u) (\b -> <br />
> ((a + b), formatLine (Add t u) (a + b))<br />
> )<br />
> )<br />
<br />
</haskell><br />
<br />
Ugly, isn't it?<br />
<br />
Let's start from the outside:<br />
<br />
<haskell><br />
bindM (evalM_1 u) (\b -> ((a + b), formatLine (Add t u) (a + b)))<br />
</haskell><br />
<br />
bindM takes the result of the evaluation "evalM_1 u", a type Mout Int,<br />
and a function. It will extract the Int from that type and use it to<br />
bind "b".<br />
<br />
So in bindM (evalM_1 u) (\b ->) "b" will be bound to the value<br />
returned by evalM_1 u, and this bound variable will be available in<br />
what comes after "->" as a bound variable (not free).<br />
<br />
Then the outer part (bindM (evalM_1 t) (\a...) will bind "a" to the<br />
value returned "evalM_1 t", the result of the evaluatuion of the first<br />
Term. This value is needed to evaluate "((a+b), formatLine...) and<br />
produce our final MOut Int.<br />
<br />
We can try to explain "bindM" in a different way by using more descriptive names.<br />
<br />
As we have seen, "bindM" extracts the Int part from our type. The Int<br />
part will be used for further computations and the Output part will be<br />
concatenated. As a result we will have a new pair with a new Int and<br />
an accumulated Output.<br />
<br />
The new version of "bindM":<br />
<haskell><br />
<br />
> getIntFromType typeMOut doSomething = (newInt,oldOutput ++ newOutput)<br />
> where (oldInt,oldOutput) = typeMOut<br />
> (newInt,newOutput) = (doSomething oldInt)<br />
<br />
</haskell><br />
<br />
As you can see it does the very same things that "bindM" does: it<br />
takes something of type MOut and a function to perform some<br />
computation with the Int part. <br />
<br />
In the "where" clause, the old Int and the old output<br />
will be extracted from our type MOut (first line of the "where"<br />
clause). <br />
<br />
A new Int and a new output will be extracted from evaluating<br />
(doSomething oldInt) in the second line.<br />
<br />
Our function will return the new Int and the concatenated outputs.<br />
<br />
We do not need to define our doSomething function, because it will be<br />
an anonymous function:<br />
<br />
<haskell><br />
<br />
> evaluator (Con a) = (a, "output-")<br />
> evaluator (Add t u) = <br />
> getIntFromType (evaluator t) <br />
> (\firstInt -> getIntFromType (evaluator u) <br />
> (\secondInt -> ((firstInt + secondInt),("-newoutput"))))<br />
<br />
</haskell><br />
<br />
As you can see we are feeding our "getIntFromType" with the evaluation<br />
of an expression ("evaluator t" and "evaluator u"). The second<br />
argument of "getIntFromType" is an anonymous function that takes the<br />
"oldInt" and does something with it.<br />
<br />
So we have a series of nested anonymous functions. Their arguments<br />
("\firstInt" and "\secondInt") will be used to produce the computation<br />
we need ("(firstInt + secondInt). Moreover "getIntFromType" will take<br />
care of concatenating the outputs.<br />
<br />
This is the result:<br />
<br />
*TheMonadicWay> evaluator (Add (Con 5) (Con 6))<br />
(11,"output-output--newoutput")<br />
*TheMonadicWay> <br />
<br />
Going back to our "bindM", we can now use lambda notation to write our<br />
evaluator in a more convinient way:<br />
<br />
<haskell><br />
<br />
> evalM_2 :: Term -> MOut Int<br />
> evalM_2 (Con a) = (a, formatLine (Con a) a)<br />
> evalM_2 (Add t u) = evalM_2 t `bindM` \a -><br />
> evalM_2 u `bindM` \b -><br />
> ((a + b), (formatLine (Add t u) (a + b)))<br />
<br />
</haskell><br />
<br />
Now, look at the first part:<br />
<br />
<haskell><br />
evalM_2 (Con a) = (a, formatLine (Con a) a)<br />
</haskell><br />
<br />
We could use a more general way of creating some output. <br />
<br />
We can create a function that takes an Int and returns the type MOut<br />
Int. We do that by pairing the received Int with an empty string "".<br />
<br />
This will be a general way of creating an object with type MOut Int starting from an Int.<br />
<br />
Or, more generaly, a function that takes something of a variable type<br />
a, and return an object of type MOut a, a coumpunt object made up of<br />
an element of type a, and one of type String.<br />
<br />
There it is:<br />
<br />
<haskell><br />
<br />
> mkM :: a -> MOut a<br />
> mkM a = (a, "")<br />
<br />
</haskell><br />
<br />
As you can see, this function will just push an Int and an empty<br />
string ("") inside our type MOut.<br />
<br />
Then we need a method of inserting some text in our object of type<br />
MOut. So we will take a string and return it paired with a void<br />
element "()":<br />
<br />
<haskell><br />
<br />
> outPut :: Output -> MOut ()<br />
> outPut x = ((), x)<br />
<br />
</haskell><br />
<br />
Very simple: we have a string "x" (Output) and create a pair with a ()<br />
instead of an Int, and the output.<br />
<br />
You can see this function as one that pushes a string, paired with a<br />
void int, inside our type MOut.<br />
<br />
Now we can rewrite:<br />
<haskell><br />
evalM_2 (Con a) = (a, formatLine (Con a) a)<br />
</haskell><br />
using the bindM function:<br />
<haskell><br />
evalM_2 (Con a) = outPut (formatLine (Con a) a) `bindM` \_ -> mkM a<br />
</haskell><br />
<br />
First we create an object of type MOut with the Int part (). As you<br />
see bindM will not use it ("\_"), but will concatenate the String part<br />
with the result of mkM, which in turn is the empry string "".<br />
<br />
In other words, first we insert the Output part (a string) in our<br />
MOut, and then we insert the Int paired with an empty string: "bindM"<br />
will not use the void int (the anonymous function will not use it's<br />
argument: "\_"), but will take care of concatenating the non empty<br />
string inserted by "outPut" with the empty one inserted by "mkM".<br />
<br />
Let's rewrite the evaluator:<br />
<br />
<haskell><br />
<br />
> evalM_3 :: Term -> MOut Int<br />
> evalM_3 (Con a) = outPut (formatLine (Con a) a) `bindM` \_ -> <br />
> mkM a<br />
> evalM_3 (Add t u) = evalM_3 t `bindM` \a -><br />
> evalM_3 u `bindM` \b -><br />
> outPut (formatLine (Add t u) (a + b)) `bindM` \_ -> <br />
> mkM (a + b)<br />
<br />
</haskell><br />
<br />
Well, this is fine, definetly better then before, anyway.<br />
<br />
Still we use `bindM` \_ -> that binds something we do not use (_). We<br />
could write a function for this specific case, when we concatenate<br />
computations without the need of binding variables for later uses.<br />
Let's call it `combineM`:<br />
<br />
<haskell><br />
<br />
> combineM :: MOut a -> MOut b -> MOut b<br />
> combineM m f = m `bindM` \_ -> f<br />
<br />
</haskell><br />
<br />
This is just something that will allow us to write the evaluator in a<br />
more concise way. <br />
<br />
So the new evaluator:<br />
<br />
<haskell><br />
<br />
> evalM :: Term -> MOut Int<br />
> evalM (Con a) = outPut (formatLine (Con a) a) `combineM` <br />
> mkM a<br />
> evalM (Add t u) = evalM t `bindM` \a -><br />
> evalM u `bindM` \b -><br />
> outPut (formatLine (Add t u) (a + b)) `combineM` <br />
> mkM (a + b)<br />
<br />
</haskell><br />
<br />
Let's put everything together (changing M into MO, so that this file<br />
will be still usable as a Literate Haskell file):<br />
<br />
<haskell><br />
<br />
> type MO a = (a, Out)<br />
> type Out = String<br />
<br />
> mkMO :: a -> MO a<br />
> mkMO a = (a, "")<br />
<br />
> bindMO :: MO a -> (a -> MO b) -> MO b<br />
> bindMO m f = (b, x ++ y)<br />
> where (a, x) = m<br />
> (b, y) = f a<br />
<br />
> combineMO :: MO a -> MO b -> MO b<br />
> combineMO m f = m `bindM` \_ -> f<br />
<br />
> outMO :: Out -> MO ()<br />
> outMO x = ((), x)<br />
<br />
> evalMO :: Term -> MO Int<br />
> evalMO (Con a) = outMO (formatLine (Con a) a) `combineMO`<br />
> mkMO a<br />
> evalMO (Add t u) = evalMO t `bindMO` \a -><br />
> evalMO u `bindMO` \b -><br />
> outMO (formatLine (Add t u) (a + b)) `combineMO` <br />
> mkMO (a + b)<br />
<br />
</haskell><br />
<br />
==What Does Bind Bind?==<br />
<br />
<div id="Bind"><br />
The evaluator looks like:<br />
<haskell><br />
evalM t >>= \a -> evalM u >>= \b -> outPut "something" >>= \_ -> mkM (a +b)<br />
</haskell><br />
where >>= is bindMO, obviously.<br />
<br />
Let's do some substitution, writing the type of their output of each function:<br />
* evalMO t => (a,Out) - where a is Int<br />
* evalMO u => (b,Out) - where b is the same of a, an Int, but with a different value<br />
* outMO Out = ((),Out)<br />
* mkMO (a+b) => ((a+b),Out) - where (a+b) is the same of a and b, but with a different value from either a and b<br />
<br />
<pre><br />
B | (a,Out) >>= \a -> (b,Out) >>= \b -> ((),Out) >>= \_ >>= ((a + b), Out)---\<br />
i | V V V V V V V V ^ ^ ^ ^ |\<br />
n | |__|________^ | | ^ | | | | | | | MOut Int <=> ((a+b), Out)<br />
d |_____|__(++)__|_Out_|__|__(++)__V_Out_|___|___(++)_|_(++)__|___|____|_____|/<br />
i | | |______(b)__|_____|_____(b)____|__(b)__|___|<br />
n | |_________(a)___________|____________|__(a)__|<br />
g | |_____()_____|<br />
<br />
</pre><br />
<br />
Clear, isn't it?<br />
<br />
"bindMO" is just a function that takes care of gluing together, inside<br />
a data type, a sequence of computations!<br />
<br />
== Some Sugar, Please!==<br />
Now our evaluator has been completely transformed into a monadic<br />
evaluator. That's what it is: a monad.<br />
<br />
We have a function that constructs an object of type MO Int, formed by<br />
a pair: the result of the evaluation and the accumulated<br />
(concatenated) output.<br />
<br />
The process of accumulation and the act of parting the MO Int into its<br />
component is buried into bindMO, now, that can also preserve some<br />
value for later uses.<br />
<br />
So we have:<br />
* MO a type constructor for a type carrying a pair composed by an Int and a String;<br />
* bindMO, that gives a direction to the process of evaluation: it concatenates computations and captures some side effects we created (the direction is given by the changes in the Out part: there's a "before" when Out was something and there's a "later" when Out is something else).<br />
* mkMO lets us create an object of type MO Int starting from an Int.<br />
<br />
As you see this is all we need to create a monad. In other words<br />
monads arise from the type system and the lambda calculus. Everything<br />
else is just syntactic sugar.<br />
<br />
So, let's have a look at that sugar: the famous do-notation!<br />
<br />
===Monadic evaluator with output in do-notation===<br />
<br />
In order to be able to use the "do-notation" we need to define a new<br />
type and make it an instance of the Monad class. To make a new type an<br />
instance of the Monad class we will have to define the two methods of<br />
this class: (>>=) and "return".<br />
<br />
This is not going to be difficult, because we already created these<br />
two methods: "bindM" and "mkM". Now we will have to rewrite them in<br />
order to reflect the fact that we are not going to use a type, for our<br />
evaluator, that is a synonymous of other types, as we did before.<br />
Indeed our MOut was defined with the "type" keyword. Now we will have<br />
to define a "real" new type with either "newtype" or "data". Since we<br />
are not going to need multiple constructors, we will use "newtype".<br />
<br />
<div id="MonadicEvalIO"><br />
<haskell><br />
<br />
> newtype Eval_IO a = Eval_IO (a, O)<br />
> deriving (Show)<br />
> type O = String<br />
<br />
</haskell><br />
<br />
This is our new type: it will have a single type constructor, whose<br />
name is the same of the type name ("Eval_IO"). The type constructor<br />
takes a parameter ("a"), a variable type, and will build a type formed<br />
by a type "a" (an Int in our case) and a String (O is indeed<br />
synonymous of String).<br />
<br />
We now need to define our "bind" function to reflect the fact that we<br />
are now using a "real" type, and, to unpack its content, we need to do<br />
pattern-matching we the type constructor "Eval_IO". Moreover, since we<br />
must return an Eval_IO type, we will use the type constructor also for<br />
building the new type with the new int and the concatenated output.<br />
<br />
For the rest our "bind" function will be identical to the one we<br />
defined before.<br />
<br />
We are going to use very descriptive names:<br />
<br />
<haskell><br />
<br />
> getInt monad doSomething = Eval_IO (newInt,oldOutput ++ newOutput)<br />
> where Eval_IO (oldInt,oldOutput) = monad<br />
> Eval_IO (newInt,newOutput) = (doSomething oldInt)<br />
<br />
</haskell><br />
<br />
As you can see, we are using Eval_IO to build the result of the<br />
computation to be returned by getInt: "Eval_IO (newInt,oldOutput ++<br />
newOutput)". And we are using it to match the internal components of<br />
our type in the "where" clause.<br />
<br />
We also need to create a function that, like mkO, will take an Int and,<br />
using the type constructor "Eval_IO", will create an object of type<br />
Eval_IO with that Int and an empty string:<br />
<br />
<haskell><br />
<br />
> createEval_IO :: a -> Eval_IO a<br />
> createEval_IO int = Eval_IO (int,"")<br />
<br />
</haskell><br />
<br />
And, finally, we need a function that will insert, in our type, a<br />
string and a void ():<br />
<br />
<haskell><br />
<br />
> print_IO :: O -> Eval_IO ()<br />
> print_IO string = Eval_IO ((), string)<br />
<br />
</haskell> <br />
<br />
With these functions we could write our monadic evaluator without the<br />
"do-notation" like this:<br />
<br />
<haskell><br />
<br />
> evalM_4 :: Term -> Eval_IO Int<br />
> evalM_4 (Con a) = createEval_IO a<br />
> evalM_4 (Add t u) = evalM_4 t `getInt` \a -><br />
> evalM_4 u `getInt` \b -><br />
> print_IO (formatLine (Add t u) (a + b)) `getInt` \_ -><br />
> createEval_IO (a + b)<br />
<br />
</haskell><br />
<br />
It is very similar to the previous evaluator, as you can see. The only<br />
differences are related to the fact that we are now using a "real"<br />
type and not a type synonymous: this requires the use of the type<br />
constructor to match the type and its internal part (as we do in the<br />
"where" clause of our "bind" function: "getInt") or to build the type<br />
(as we do in the "bind" function to return the new Int with the<br />
concatenated output).<br />
<br />
Running this evaluator will produce:<br />
<br />
*TheMonadicWay> evalM_4 (Add (Con 6) (Con 12))<br />
Eval_IO (18,"eval (Add (Con 6) (Con 12)) <= 18 - ")<br />
*TheMonadicWay> <br />
<br />
Now we have everything we need to declare our type, Eval_IO, as an<br />
instance of the Monad class:<br />
<haskell><br />
<br />
> instance Monad Eval_IO where<br />
> return a = createEval_IO a<br />
> (>>=) m f = getInt m f<br />
<br />
</haskell><br />
<br />
As you see we are just using our defined functions as methods for our<br />
instance of the Monad class.<br />
<br />
This is all we need to do. Notice that we do not have to define the<br />
"combineM" function, for chaining computation, as we do with "getInt",<br />
without binding variables for later use within the series of nested<br />
anonymous functions (the "doSomething" part) that will form the "do"<br />
block.<br />
<br />
This function comes for free by just defining our type as an instance<br />
of the Monad class. Indeed, if you look at the definition of the Monad<br />
class in the Prelude you see that "combineM", or (>>) is deduced by<br />
the definition of (>>=):<br />
<br />
<haskell><br />
class Monad m where<br />
return :: a -> m a<br />
(>>=) :: m a -> (a -> m b) -> m b<br />
(>>) :: m a -> m b -> m b<br />
fail :: String -> m a<br />
<br />
-- Minimal complete definition: (>>=), return<br />
p >> q = p >>= \ _ -> q<br />
fail s = error s<br />
</haskell><br />
<br />
You can see that the "combineM"" method (or (>>)) is automatically<br />
derived by the "bindMO" (or >>=) method:<br />
<br />
<haskell><br />
p >> q = p >>= \ _ -> q<br />
</haskell><br />
<br />
We can now write our evaluator using the do-notation:<br />
<br />
<haskell><br />
<br />
> eval_IO :: Term -> Eval_IO Int<br />
> eval_IO (Con a) = do print_IO (formatLine (Con a) a)<br />
> return a<br />
> eval_IO (Add t u) = do a <- eval_IO t<br />
> b <- eval_IO u<br />
> print_IO (formatLine (Add t u) (a + b))<br />
> return (a + b)<br />
<br />
</haskell><br />
<br />
As you can see the anonymous functions are gone. Instead we use this:<br />
a <- eval_IO t<br />
<br />
This seems like an assignment, that cannot be possible in Haskell. In<br />
fact it is just the way our anonymous function's arguments is bound<br />
within a do block.<br />
<br />
Even if it does not seem like a series of nested anonymous functions,<br />
this is what actually a do block is.<br />
<br />
Our monad is defined by three elements: <br />
* a type, with its type constructor(s);<br />
* a bind method: it will bind an unwritten anonymous function's argument to the value of the Int part of our type, or more generally, of the variable type "a". It will also create a series of anonymous functions: a line for each function;<br />
* a "return" function, to insert, into out type, a value of type Int, or, more generally, a value of variable type "a".<br />
<br />
Additionally, we need a function to insert a string in our type,<br />
string that the derived "bind" (>>) will concatenate ignoring the void<br />
(), our "print_IO".<br />
<br />
Let's see the evaluator with output in action:<br />
*TheMonadicWay> eval_IO (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) <br />
Eval_IO (54,"eval (Con 6) <= 6 - eval (Con 16) <= 16 - eval (Con 20) <= 20 - eval (Con 12) <= 12 - \<br />
eval (Add (Con 20) (Con 12)) <= 32 - eval (Add (Con 16) (Add (Con 20) (Con 12))) <= 48 - \<br />
eval (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) <= 54 - ")<br />
*TheMonadicWay> <br />
<br />
Let's format the output part:<br />
eval (Con 6) <= 6 <br />
eval (Con 16) <= 16 <br />
eval (Con 20) <= 20 <br />
eval (Con 12) <= 12 <br />
eval (Add (Con 20) (Con 12)) <= 32 <br />
eval (Add (Con 16) (Add (Con 20) (Con 12))) <= 48 <br />
eval (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) <= 54 <br />
<br />
==Type and Newtype: What Happened to Our Output?==<br />
<br />
Well, actually something happened to the output. Let's compare the<br />
output of evalMO (the monadic evaluator written without the<br />
do-notation) and eval_IO:<br />
<br />
*TheMonadicWay> evalMO (Con 6)<br />
(6,"eval (Con 6) <= 6 - ")<br />
*TheMonadicWay> eval_IO (Con 6)<br />
Eval_IO (6,"eval (Con 6) <= 6 - ")<br />
*TheMonadicWay> <br />
<br />
They look almost the same, but they are not the same: the output of<br />
eval_IO has the Eval_IO stuff. It must be related to the changes we<br />
had to do to our evaluator in order to use the do-conation, obviously.<br />
<br />
We can now review some of our basic knowledge of Haskell's type<br />
system.<br />
<br />
What's changed? First the type definition. We have now:<br />
<br />
<haskell><br />
newtype Eval_IO a = Eval_IO (a, O)<br />
deriving (Show)<br />
</haskell><br />
<br />
instead of <br />
<br />
<haskell><br />
type MO a = (a, Out)<br />
</haskell><br />
<br />
Now <hask>return a</hask> is the product of the application of the<br />
type constructor Eval_IO to the pair that are going to form our monad.<br />
<br />
"return" takes an Int and inserts it into our monad. It will also<br />
insert an empty String "" that (>>=) or (>>) will then concatenate in<br />
the sequence of computations they glue together.<br />
<br />
The same for (>>=). It will now return something constructed by<br />
Eval_IO: <br />
<br />
* "newInt", the result of the application of "doSomething" to "oldInt" (better, the binding of "oldInt" in "doSomething");<br />
* the concatenation of "oldOutput" (matched by <hask>Eval_IO (oldInt, oldOutput)</hask> with the evaluation of "monad" - "eval_IO t") and "newOutput", (matched by "Eval_IO(newInt,newOutput)" with the evaluation of "(doSomething monad)" - "eval_IO u").<br />
<br />
That is to say: in the "where" clause, we are matching for the<br />
elements paired in a type Eval_IO: this is indeed the type of "monad"<br />
(corresponding to "eval_IO t" in the body of the evaluator) and<br />
"(doSomething monad)" (where "doSomething" correspond to the<br />
evaluation of "eval_IO u" within an anonymous function with \oldInt as<br />
its argument, argument bound to the result of the previous evaluation<br />
of "monad", that is to say "eval_IO t").<br />
<br />
And so, "Eval_IO (oldInt,oldOutput) = monad" means: match "oldInt" and<br />
"oldOutput", paired in a type Eval_IO, and that are produced by the<br />
evaluation of "monad" (that is to say: "eval_IO t"). The same for<br />
Eval_IO (newInt,newOutput): match "newInt" and "newOutput" produced by<br />
the evaluation of "(doSomething monad)".<br />
<br />
So the output of the evaluator is now not simply a pair made of and<br />
Int and a String. It is a specific type (Eval_IO) that happens to<br />
carry a pair of an Int and a String. But, if we want the Int and the<br />
string, we have to extract them from the Eval_IO type, as we do in the<br />
"where" clause: we ''unpack'' our type object (let's call it with its<br />
name: our monad!) and take out the Int and the String to feed the next<br />
function application and the output generation.<br />
<br />
The same to insert something in our monad: if we want to create a pair<br />
of an Int and a String, pair of type Eval_IO, we now have to ''pack''<br />
them together by using our type constructor, feeding it with a pair<br />
composed by and Int and a String. This is what we do with the "return"<br />
method of out monad and with "print_IO" function, where:<br />
* return insert into the monad an Int;<br />
* print_IO insert into the monad a String.<br />
<br />
So, why cannot we use the old <hask>type MO a = (a, Out)</hask> that<br />
did not required all this additional work (apart the need to<br />
specifically define (>>)?<br />
<br />
Type MO is just a synonymous for (a,Out): the two can be substituted<br />
one for the other. That's it.<br />
<br />
We did not have to pack "a" and "Out" together with a type constructor<br />
to have a new type MO.<br />
<br />
As a consequence, we cannot use MO as an instance of Monad, and so, we<br />
cannot use with it the syntactic sugar we needed: the do-notation.<br />
<br />
That is to say: a type created with the "type" keyword cannot be an<br />
instance of a class, and cannot inherits its methods (in our case<br />
(>>=, >> and return). And without those methods the do-notation is not<br />
usable.<br />
<br />
==Errare Monadicum Est==<br />
<br />
Now that we have a basic understanding of what a monad is, and does,<br />
we will further explore it by making some changes to our evaluator.<br />
<br />
In this section we will se how to handle exceptions in our monadic<br />
evaluator.<br />
<br />
Suppose that we want to stop the execution of our monad if some<br />
conditions occurs. If our evaluator was to compute divisions, instead<br />
of sums, then we would like to stop the evaluator when a division by<br />
zero occurs, possibly producing some output, instead of the result of<br />
the evaluation of the expression, that explains what happened.<br />
<br />
Basic error handling.<br />
<br />
We will do so starting from the beginning once again...<br />
<br />
===The basic evaluator, non monadic, with exception===<br />
<br />
We just take our basic evaluator, without any output, and write a<br />
method to stop execution if a condition occurs: <br />
<br />
<haskell><br />
<br />
> data M a = Raise Exception<br />
> | Return a<br />
> deriving (Show)<br />
> type Exception = String<br />
<br />
</haskell><br />
<br />
Now, our monad is of datatype "M a" which can either be constructed<br />
with the "Raise" constructor, that takes a String (Exception is a<br />
synonymous of String), or by the "Return" constructor, that takes a<br />
variable type ("a"), an Int in our case.<br />
<br />
<haskell><br />
<br />
> evalE :: Term -> M Int<br />
> evalE (Con a) = Return a<br />
<br />
</haskell><br />
<br />
If evalE matches a Con it will construct a type Return with, inside, the content of the Con.<br />
<br />
<haskell><br />
<br />
> evalE (Add a b) = <br />
> case evalE a of<br />
> Raise e -> Raise e<br />
> Return a -><br />
> case evalE b of <br />
> Raise e -> Raise e<br />
> Return b -><br />
> if (a+b) == 42<br />
> then Raise "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else Return (a+b)<br />
<br />
</haskell><br />
<br />
If evalE matches an Add it will check if evaluating the first part<br />
produces a "Raise" or a "Return": in the first case it will return a<br />
"Raise" whose content is the same received. <br />
<br />
If instead the evaluation produces a value of a type matched by<br />
"Return", the evaluator will evaluate the second term of Add.<br />
<br />
If this returns a "Raise", a "Raise" will be returned all the way up<br />
the recursion, otherwise the evaluator will check whether a condition<br />
for raising a "Raise" exists. If not, it will return a "Return" with<br />
the sum inside.<br />
<br />
Test it with:<br />
<br />
evalE (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2)))<br />
<br />
<br />
===The basic evaluator, monadic, with exceptions===<br />
<br />
In order to produce a monadic version of the previous evaluator, the<br />
one that raises exceptions, we just need to abstract out from the<br />
evaluator all that case analysis.<br />
<br />
<haskell><br />
<br />
> data M1 a = Except Exception<br />
> | Ok {showM :: a }<br />
> deriving (Show)<br />
<br />
</haskell><br />
<br />
The data type didn't change at all. Well, we changed the name of the<br />
Return type constructor (now Ok) so that this constructor can coexist<br />
with the previous one in the same Literate Haskell file.<br />
<br />
<div id="MonadicEvalE"><br />
<haskell><br />
<br />
> instance Monad M1 where<br />
> return a = Ok a<br />
> m >>= f = case m of<br />
> Except e -> Except e<br />
> Ok a -> f a<br />
<br />
</haskell><br />
<br />
Binding operations are now very easy. Basically we check:<br />
* if the result of the evaluation of "m" produces an exception (first match: Except e ->...), in which case we return its content by constructing our M1 Int with the "Raise" constructor".<br />
* if the result of the evaluation of "m" is matched with the "Ok" constructor, we get its content and use it to bind the argument of "f" to its value.<br />
<br />
<hask>return a</hask> will just use the Ok type constructor for<br />
inserting "a" (in our case an Int) into M1 Int, the type of our monad.<br />
<br />
<haskell><br />
<br />
> raise :: Exception -> M1 a<br />
> raise e = Except e<br />
<br />
</haskell><br />
<br />
This is just a helper function to construct our "M1 a" type with the<br />
Raise constructor. It takes a string and returns a type (M1 a) to be<br />
matched with the "Raise" constructor.<br />
<br />
<haskell><br />
<br />
> eval_ME :: Term -> M1 Int<br />
> eval_ME (Con a) = do return a<br />
> eval_ME (Add t u) = do a <- eval_ME t<br />
> b <- eval_ME u<br />
> if (a+b) == 42<br />
> then raise "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else return (a + b)<br />
<br />
</haskell><br />
<br />
The evaluator itself is very simple. We bind "a" with the result of<br />
"eval_ME t", "b" with the result of "eval_ME u", and we check for a<br />
condition: <br />
* if the condition is met we raise an exception, that is to say: we return a value constructed with the "Raise" constructor. This value will be matched by ">>=" in the next recursion. And >>= will just return it all the way up the recursion.<br />
* if the condition is not met, we return a value constructed with the "Return" type constructor and go on with the recursion.<br />
<br />
Run with:<br />
<br />
eval_ME (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2)))<br />
<br />
It is noteworthy the fact that in our datatype definition we used a<br />
label field with a label selector (we called it showM), even though it<br />
was not used in our code. We will use this methodology later on.<br />
<br />
So, just to refresh your memory:<br />
<br />
<haskell><br />
<br />
> data Person = Person {name :: String,<br />
> age :: Int,<br />
> hobby :: String<br />
> } deriving (Show)<br />
<br />
> andreaRossato = Person "Andrea" 37 "Haskell The Monadic Way"<br />
> personName (Person a b c) = a<br />
<br />
</haskell><br />
<br />
will produce:<br />
*TheMonadicWay> andreaRossato<br />
Person {name = "Andrea", age = 37, hobby = "Haskell The Monadic Way"}<br />
*TheMonadicWay> personName andreaRossato<br />
"Andrea"<br />
*TheMonadicWay> name andreaRossato<br />
"Andrea"<br />
*TheMonadicWay> age andreaRossato<br />
37<br />
*TheMonadicWay> hobby andreaRossato<br />
"Haskell The Monadic Way"<br />
*TheMonadicWay> <br />
<br />
<br />
===Monadic evaluator with output and exceptions===<br />
<br />
We will now try to combine the [[The Monadic Way Part I#MonadicEvalIO|output-producing monadic evaluator]] <br />
with [[The Monadic Way Part I#MonadicEvalE| exception producing one]].<br />
<br />
<br />
<haskell><br />
<br />
> data M2 a = Ex Exception<br />
> | Done {unpack :: (a,O) }<br />
> deriving (Show)<br />
<br />
</haskell><br />
<br />
Now we need a datatype with two constructor: one to produce a value<br />
type "M2 a" using "Ex String" and one for value type "M2 a" (Int in<br />
this case) using "Done a".<br />
<br />
'''Note''' that we changed the name of the exception type constructor<br />
from "Raise" to "Ex" just to make the two coexist in the same Literate<br />
Haskell file.<br />
<br />
The constructor "Done a" is defined with a label sector: <hask>Done {unpack :: (a,O)}</hask> <br />
and is equivalent to <hask>Done (a,O)</hask>. <br />
<br />
The only difference is that, this way, we are also defining a method<br />
to retrieve the pair (a,O) (in our case "O" is a synonymous for<br />
String, whereas "a" is a variable type) from an object of type "Done<br />
a".<br />
<br />
<haskell><br />
<br />
> instance Monad M2 where<br />
> return a = Done (a, "")<br />
> m >>= f = case m of<br />
> Ex e -> Ex e<br />
> Done (a, x) -> case (f a) of<br />
> Ex e1 -> Ex e1<br />
> Done (b, y) -> Done (b, x ++ y)<br />
<br />
</haskell><br />
<br />
Now our binding operations gets more complicated by the fact that we<br />
have to concatenate the output, as we did before, '''and''' check for<br />
exceptions.<br />
<br />
It is not possible to do has we did in the [[The Monadic Way Part I#MonadicEvalE| exception producing evaluator]], <br />
where we could check just for "m" (remember the "m" in the first run<br />
stands for "eval t").<br />
<br />
Since at the end we must return the output produced by the evaluation<br />
of "m" ''concatenated'' with the output produced by the evaluation of<br />
"f a" (where "a" is returned by "m", paired with "x" by "Done"), now<br />
we must check if we '''do have''' an output from "f a" produced by<br />
"Done".<br />
<br />
Indeed, now, "f a" can also produce a value constructed by "Ex", and<br />
this value does not contain the pair as the value produced by "Done".<br />
<br />
So, we evaluate "m": <br />
* if we match a value produced by type constructor "Ex" we return a value produced by type constructor "Ex" whose content is the one we extracted in the matching;<br />
* if we match a value produced by "Done" we match the pair it carries "(a,x)" and we analyze what "f a" returns:<br />
** if "f a" returns a value produced by "Ex" we extract the exception and we return it, constructing a value with "Ex"<br />
** if "f a" returns a value produced by "Done" we return "b" and the concatenated "x" and "y". <br />
<br />
And now the evaluator:<br />
<br />
<haskell><br />
<br />
> raise_IOE :: Exception -> M2 a<br />
> raise_IOE e = Ex e<br />
<br />
</haskell><br />
<br />
This is the function to insert in our monad (M2) an exception: we take<br />
a String and produce a value applying the type constructor for<br />
exception "Ex" to its value.<br />
<br />
<haskell><br />
<br />
> print_IOE :: O -> M2 ()<br />
> print_IOE x = Done ((), x)<br />
<br />
</haskell><br />
<br />
The function to produce output is the very same of the one of the [[The Monadic Way Part I#MonadicEvalIO|output-producing monadic evaluator]].<br />
<br />
<haskell><br />
<br />
> eval_IOE :: Term -> M2 Int<br />
> eval_IOE (Con a) = do print_IOE (formatLine (Con a) a)<br />
> return a<br />
> eval_IOE (Add t u) = do a <- eval_IOE t<br />
> b <- eval_IOE u<br />
> let out = formatLine (Add t u) (a + b)<br />
> print_IOE out<br />
> if (a+b) == 42<br />
> then raise_IOE $ out ++ "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else return (a + b)<br />
<br />
</haskell><br />
<br />
The evaluator procedure did not change very much from the one of the [[The Monadic Way Part I#MonadicEvalIO|output-producing monadic evaluator]].<br />
<br />
We just added the case analysis to see if the condition for raising an exception is met.<br />
<br />
Running with<br />
<br />
eval_IOE (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2)))<br />
<br />
will produce <br />
<br />
Ex "eval (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2))) <= 42 - <br />
The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
<br />
Look at the <hask>let</hask> clause within the do-notation. We do not<br />
need to use the "let ... in" construction: since all bound variables<br />
remain bound within a <hask>do</hask> procedure (see [[The Monadic Way Part I#Bind|here]]), <br />
we do not need the "in" to specify "where" the variable "out" will be bound in!<br />
<br />
==We Need A State==<br />
<br />
We will keep on adding complexity to our monadic evaluator and this<br />
time we will add a counter. We just want to count the number of<br />
iterations (the number of times "eval" will be called) needed to<br />
evaluate the expression.<br />
<br />
===The basic evaluator, non monadic, with a counter===<br />
<br />
As before we will start by adding this feature to our [[The Monadic Way Part I#BasicEval|basic evaluator]]. <br />
<br />
A method to count the number of iterations, since the lack of<br />
assignment and destructive updates (such as for i=0;i<10;i++;),<br />
is to add an argument to our function, the initial state, number that<br />
in each call of the function will be increased and passed to the next<br />
function call.<br />
<br />
And so, very simply:<br />
<br />
<haskell><br />
<br />
> -- non monadic<br />
> type St a = State -> (a, State)<br />
> type State = Int<br />
> evalNMS :: Term ->St Int<br />
> evalNMS (Con a) x = (a, x + 1)<br />
> evalNMS (Add t u) x = let (a, y) = evalNMS t x in<br />
> let (b, z) = evalNMS u y in<br />
> (a + b, z +1)<br />
<br />
</haskell><br />
<br />
Now evalNMS takes two arguments: the expression of type Term and an<br />
State (which is a synonymous for Int), and will produce a pair<br />
(a,State), that is to say a pair with a variable type "a" and an Int.<br />
<br />
The operations in the evaluator are very similar to the non monadic [[The Monadic Way Part I#BasivEvalO|output producing evaluator]].<br />
<br />
We are now using the "let ... in" clause, instead of the "where", and we are increasing the counter "z" the comes from the evaluation of the second term, but the basic operation are the same:<br />
* we evaluate "evalNMS t x" where "x" is the initial state, and we match and bind the result in "let (a, y) ... in"<br />
* we evaluate "evalNMS u y", where "y" was bound to the value returned by the previous evaluation, and we match and bind the result in "let (b, z) ... in"<br />
* we return a pair formed by the sum of the result (a+b) and the state z increased by 1. <br />
<br />
Let's try it:<br />
<br />
*TheMonadicWay> evalNMS (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2))) 0<br />
(42,7)<br />
*TheMonadicWay> <br />
<br />
As you see we must pass to "evalNMS"the initial state of our counter: 0.<br />
<br />
Look at the type signature of the function "evalNMS":<br />
<haskell><br />
evalNMS :: Term ->St Int<br />
</haskell><br />
<br />
From this signature you could argue that our function takes only<br />
'''one''' argument. But since our type St is defined with the "type"<br />
keyword, St can be substituted with what comes after the "=" sign. So,<br />
the real type signature of our function is:<br />
<br />
<haskell><br />
evalNMS :: Term -> State -> (Int,State)<br />
</haskell><br />
<br />
<div if="typeNewtype"><br />
Just to refresh your memory:<br />
<br />
<haskell><br />
<br />
> type IamAfunction a = (a -> a)<br />
> newtype IamNotAfunction a = NF (a -> a)<br />
> newtype IamNotAfunctionButYouCanUnPackAndRunMe a = F { unpackAndRun :: (a -> a) }<br />
<br />
> a = \x -> x * x<br />
<br />
> a1 :: IamAfunction Int<br />
> a1 = a<br />
<br />
> a2 :: IamNotAfunction Int<br />
> a2 = NF a<br />
<br />
> a3 :: IamNotAfunctionButYouCanUnPackAndRunMe Int<br />
> a3 = F a<br />
<br />
</haskell><br />
<br />
<br />
*TheMonadicWay> a 4<br />
16<br />
*TheMonadicWay> a1 4<br />
16<br />
*TheMonadicWay> a2 4<br />
<br />
<interactive>:1:0:<br />
The function `a2' is applied to one arguments,<br />
but its type `IamNotAfunction Int' has only 0<br />
In the definition of `it': it = a2 4<br />
*TheMonadicWay> a3 4<br />
<br />
<interactive>:1:0:<br />
The function `a3' is applied to one arguments,<br />
but its type `IamNotAfunctionButYouCanUnPackAndRunMe Int' has only 0<br />
In the definition of `it': it = a3 4<br />
*TheMonadicWay> unpackAndRun a3 4<br />
16<br />
*TheMonadicWay><br />
<br />
This means that "a1" is a partial application hidden by a type<br />
synonymous. <br />
<br />
"a2" and "a3" are not function types. They are types that<br />
have a functional value. <br />
<br />
Moreover, since we defined the type constructor of type<br />
"IamNotAfunctionButYouCanUnPackAndRunMe", F, with a label field, in<br />
that label field we defined a method (a label selector) to "extract"<br />
the function from the type "IamNotAfunctionButYouCanUnPackAndRunMe",<br />
and run it:<br />
<br />
<haskell><br />
unpackAndRun a3 4<br />
</haskell><br />
<br />
And what about "a2"? Is it lost forever?<br />
<br />
Obviously not! We need to write a function that unpacks a type<br />
"IamNotAfunction", using its type constructor NF to match the internal<br />
function:<br />
<br />
<haskell><br />
<br />
> unpackNF :: IamNotAfunction a -> a -> a<br />
> unpackNF (NF f) = f<br />
<br />
</haskell><br />
<br />
and run:<br />
<br />
*TheMonadicWay> unpackNF a2 4<br />
16<br />
*TheMonadicWay> <br />
<br />
As you see, "unpackNF" definition is a partial application: we specify<br />
one argument to get a function that gets another argument.<br />
<br />
A label selector does the same thing.<br />
<br />
Later we will see the importance of this distinction, quite obvious<br />
for haskell gurus, but not for us. Till now.<br />
<br />
===The evaluator, monadic, with a counter, without do-notation===<br />
<br />
<br />
'''(Text to be done yet: just a summary)'''<br />
<br />
The moadic version without do notation.<br />
<br />
<haskell><br />
<br />
> -- monadic<br />
> type MS a = State -> (a, State)<br />
<br />
> mkMS :: a -> MS a<br />
> mkMS a = \x -> (a, x)<br />
<br />
> bindMS :: MS a -> (a -> MS b) -> MS b<br />
> bindMS m f = \x -> <br />
> let (a, y) = m x in<br />
> let (b, z) = f a y in<br />
> (b, z)<br />
<br />
> combineMS :: MS a -> MS b -> MS b<br />
> combineMS m f = m `bindMS` \_ -> f<br />
<br />
> incState :: MS ()<br />
> incState = \s -> ((), s + 1)<br />
<br />
> evalMS :: Term -> MS Int<br />
> evalMS (Con a) = incState `combineMS` mkMS a<br />
> evalMS (Add t u) = evalMS t `bindMS` \a -><br />
> evalMS u `bindMS` \b -><br />
> incState `combineMS` mkMS (a + b)<br />
<br />
> --evalMS (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) 0<br />
<br />
</haskell><br />
<br />
===The evaluator, monadic, with counter and output, without do-notation===<br />
<br />
Now we'll add Output to the stateful evaluator:<br />
<br />
<haskell><br />
<br />
> -- state and output<br />
<br />
> type MSO a = State -> (a, State, Output)<br />
<br />
> mkMSO :: a -> MSO a<br />
> mkMSO a = \s -> (a, s, "")<br />
<br />
> bindMSO :: MSO a -> (a -> MSO b) -> MSO b<br />
> bindMSO m f = \x -> <br />
> let (a, y, s1) = m x in<br />
> let (b, z, s2) = f a y in<br />
> (b, z, s1 ++ s2)<br />
<br />
> combineMSO :: MSO a -> MSO b -> MSO b<br />
> combineMSO m f = m `bindMSO` \_ -> f<br />
<br />
> incMSOstate :: MSO ()<br />
> incMSOstate = \s -> ((), s + 1, "")<br />
<br />
> outMSO :: Output -> MSO ()<br />
> outMSO = \x s -> ((),s, x)<br />
<br />
> evalMSO :: Term -> MSO Int<br />
> evalMSO (Con a) = incMSOstate `combineMSO` <br />
> outMSO (formatLine (Con a) a) `combineMSO` <br />
> mkMSO a<br />
> evalMSO (Add t u) = evalMSO t `bindMSO` \a -><br />
> evalMSO u `bindMSO` \b -><br />
> incMSOstate `combineMSO` <br />
> outMSO (formatLine (Add t u) (a + b)) `combineMSO`<br />
> mkMSO (a + b)<br />
<br />
> --evalMSO (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) 0<br />
<br />
</haskell><br />
<br />
===The monadic evaluator with output and counter in do-notation=== <br />
<br />
State, Output in do-notation. Look at how much the complexity of our<br />
(>>=) founction is increasing:<br />
<br />
<haskell><br />
<br />
> -- thanks to Brian Hulley<br />
<br />
> newtype MSIO a = MSIO (State -> (a, State, Output))<br />
> instance Monad MSIO where<br />
> return a = MSIO (\s -> (a, s, ""))<br />
> (MSIO m) >>= f = MSIO $ \x -><br />
> let (a, y, s1) = m x in<br />
> let MSIO runNextStep = f a in<br />
> let (b, z, s2) = runNextStep y in<br />
> (b, z, s1 ++ s2)<br />
<br />
<br />
> incMSOIstate :: MSIO ()<br />
> incMSOIstate = MSIO (\s -> ((), s + 1, ""))<br />
<br />
> print_MSOI :: Output -> MSIO ()<br />
> print_MSOI x = MSIO (\s -> ((),s, x))<br />
<br />
> eval_MSOI :: Term -> MSIO Int<br />
> eval_MSOI (Con a) = do incMSOIstate<br />
> print_MSOI (formatLine (Con a) a)<br />
> return a<br />
<br />
> eval_MSOI (Add t u) = do a <- eval_MSOI t<br />
> b <- eval_MSOI u<br />
> incMSOIstate<br />
> print_MSOI (formatLine (Add t u) (a + b))<br />
> return (a + b)<br />
<br />
> run_MSOI :: MSIO a -> State -> (a, State, Output)<br />
> run_MSOI (MSIO f) s = f s<br />
<br />
> --run_MSOI (eval_MSOI (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12))))) 0<br />
<br />
</haskell><br />
<br />
===Another version of the monadic evaluator with output and counter, in do-notation===<br />
<br />
This is e second version that exploit label fields in datatype to<br />
decrease the complexity of the binding operations.<br />
<br />
<haskell><br />
<br />
> -- Thanks Udo Stenzel<br />
<br />
> newtype Eval_SIO a = Eval_SIO { unPackMSIOandRun :: State -> (a, State, Output) }<br />
> instance Monad Eval_SIO where<br />
> return a = Eval_SIO (\s -> (a, s, ""))<br />
> (>>=) m f = Eval_SIO (\x -><br />
> let (a, y, s1) = unPackMSIOandRun m x in<br />
> let (b, z, s2) = unPackMSIOandRun (f a) y in<br />
> (b, z, s1 ++ s2))<br />
<br />
> incSIOstate :: Eval_SIO ()<br />
> incSIOstate = Eval_SIO (\s -> ((), s + 1, ""))<br />
<br />
> print_SIO :: Output -> Eval_SIO ()<br />
> print_SIO x = Eval_SIO (\s -> ((),s, x))<br />
<br />
> eval_SIO :: Term -> Eval_SIO Int<br />
> eval_SIO (Con a) = do incSIOstate<br />
> print_SIO (formatLine (Con a) a)<br />
> return a<br />
> eval_SIO (Add t u) = do a <- eval_SIO t<br />
> b <- eval_SIO u<br />
> incSIOstate<br />
> print_SIO (formatLine (Add t u) (a + b))<br />
> return (a + b)<br />
<br />
> --unPackMSIOandRun (eval_SIO (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12))))) 0<br />
<br />
</haskell><br />
<br />
<br />
==If There's A State We Need Some Discipline: Dealing With Complexity==<br />
<br />
In order to increase the complexity of our monad now we will try to<br />
mix State (counter), Exceptions and Output.<br />
<br />
This is an email [http://www.haskell.org/pipermail/haskell-cafe/2006-August/017672.html I send to the haskell-cafe mailing list]:<br />
<br />
<pre><br />
Now I'm trying to create a statefull evaluator, with output and<br />
exception, but I'm facing a problem I seem not to be able to<br />
conceptually solve.<br />
<br />
Take the code below.<br />
Now, in order to get it run (and try to debug) the Eval_SOI type has a<br />
Raise constructor that produces the same type of SOIE. Suppose instead it<br />
should be constructing something like Raise "something". <br />
Moreover, I wrote a second version of >>=, commented out.<br />
This is just to help me illustrate to problem I'm facing.<br />
<br />
Now, >>= is suppose to return Raise if "m" is matched against Raise<br />
(second version commented out).<br />
If "m" matches SOIE it must return a SOIE only if "f a" does not<br />
returns a Raise (output must be concatenated).<br />
<br />
I seem not to be able to find a way out. Moreover, I cannot understand<br />
if a way out can be possibly found. Something suggests me it could be<br />
related to that Raise "something".<br />
But my feeling is that functional programming could be something out<br />
of the reach of my mind... by the way, I teach Law, so perhaps you'll<br />
forgive me...;-)<br />
<br />
If you can help me to understand this problem all I can promise is<br />
that I'll mention your help in the tutorial I'm trying to write on<br />
"the monadic way"... that seems to lead me nowhere.<br />
<br />
Thanks for your kind attention.<br />
<br />
Andrea<br />
</pre><br />
<br />
This was the code:<br />
<br />
<haskell><br />
data Eval_SOI a = Raise { unPackMSOIandRun :: State -> (a, State, Output) }<br />
| SOIE { unPackMSOIandRun :: State -> (a, State, Output) }<br />
<br />
instance Monad Eval_SOI where<br />
return a = SOIE (\s -> (a, s, ""))<br />
m >>= f = SOIE (\x -><br />
let (a, y, s1) = unPackMSOIandRun m x in<br />
case f a of<br />
SOIE nextRun -> let (b, z, s2) = nextRun y in <br />
(b, z, s1 ++ s2)<br />
Raise e1 -> e1 y --only this happens<br />
<br />
)<br />
-- (>>=) m f = case m of<br />
-- Raise e -> error "ciao" -- why this is not going to happen?<br />
-- SOIE a -> SOIE (\x -><br />
-- let (a, y, s1) = unPackMSOIandRun m x in<br />
-- let (b, z, s2) = unPackMSOIandRun (f a) y in <br />
-- (b, z, s1 ++ s2)) <br />
<br />
<br />
incSOIstate :: Eval_SOI ()<br />
incSOIstate = SOIE (\s -> ((), s + 1, ""))<br />
<br />
print_SOI :: Output -> Eval_SOI ()<br />
print_SOI x = SOIE (\s -> ((),s, x))<br />
<br />
raise x e = Raise (\s -> (x,s,e))<br />
<br />
eval_SOI :: Term -> Eval_SOI Int<br />
eval_SOI (Con a) = do incSOIstate<br />
print_SOI (formatLine (Con a) a)<br />
return a<br />
eval_SOI (Add t u) = do a <- eval_SOI t<br />
b <- eval_SOI u<br />
incSOIstate<br />
print_SOI (formatLine (Add t u) (a + b))<br />
if (a + b) == 42 <br />
then raise (a+b) " = The Ultimate Answer!!"<br />
else return (a + b)<br />
<br />
runEval exp = case eval_SOI exp of<br />
Raise a -> a 0<br />
SOIE p -> let (result, state, output) = p 0 in<br />
(result,state,output)<br />
<br />
<br />
<br />
--runEval (Add (Con 10) (Add (Con 28) (Add (Con 40) (Con 2))))<br />
</haskell><br />
<br />
This code will produce <br />
<br />
eval (Con 10) <= 10 -<br />
eval (Con 28) <= 28 -<br />
eval (Con 40) <= 40 -<br />
eval (Con 2) <= 2 - = The Ultimate Answer!!<br />
eval (Add (Con 28) (Add (Con 40) (Con 2))) <= 70 -<br />
eval (Add (Con 10) (Add (Con 28) (Add (Con 40) (Con 2)))) <= 80 -<br />
<br />
The exception appears in the output, but executioon is not stopped.<br />
<br />
===Monadic evaluator with output, counter and exception, in do-notation===<br />
<br />
Brian Hulley [http://www.haskell.org/pipermail/haskell-cafe/2006-August/017680.html came up with this solution]:<br />
<br />
<haskell><br />
<br />
> -- thanks to Brian Hulley<br />
> data Result a<br />
> = Good a State Output<br />
> | Bad State Output Exception<br />
> deriving Show<br />
<br />
> newtype Eval_SIOE a = SIOE {runSIOE :: State -> Result a}<br />
<br />
> instance Monad Eval_SIOE where<br />
> return a = SIOE (\s -> Good a s "")<br />
> m >>= f = SIOE $ \x -><br />
> case runSIOE m x of<br />
> Good a y o1 -><br />
> case runSIOE (f a) y of<br />
> Good b z o2 -> Good b z (o1 ++ o2)<br />
> Bad z o2 e -> Bad z (o1 ++ o2) e<br />
> Bad z o2 e -> Bad z o2 e<br />
<br />
> raise_SIOE e = SIOE (\s -> Bad s "" e)<br />
<br />
> incSIOEstate :: Eval_SIOE ()<br />
> incSIOEstate = SIOE (\s -> Good () (s + 1) "")<br />
<br />
> print_SIOE :: Output -> Eval_SIOE ()<br />
> print_SIOE x = SIOE (\s -> Good () s x)<br />
<br />
<br />
> eval_SIOE :: Term -> Eval_SIOE Int<br />
> eval_SIOE (Con a) = do incSIOEstate<br />
> print_SIOE (formatLine (Con a) a)<br />
> return a<br />
> eval_SIOE (Add t u) = do a <- eval_SIOE t<br />
> b <- eval_SIOE u<br />
> incSIOEstate<br />
> let out = formatLine (Add t u) (a + b)<br />
> print_SIOE out<br />
> if (a+b) == 42<br />
> then raise_SIOE $ out ++ "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else return (a + b)<br />
<br />
> runEval exp = case runSIOE (eval_SIOE exp) 0 of<br />
> Bad s o e -> "Error at iteration n. " ++ show s ++ <br />
> " - Output stack = " ++ o ++ <br />
> " - Exception = " ++ e<br />
> Good a s o -> "Result = " ++ show a ++ <br />
> " - Iterations = " ++ show s ++ " - Output = " ++ o<br />
<br />
</haskell><br />
<br />
Run with runEval (Add (Con 18) (Add (Con 12) (Add (Con 10) (Con 2))))<br />
<br />
==Suggested Readings==<br />
<br />
Cale Gibbard, [http://haskell.org/haskellwiki/Monads_as_Containers Monads as Containers]<br />
<br />
Jeff Newbern, [http://www.nomaware.com/monads/html/index.html All About Monads]<br />
<br />
[http://haskell.org/haskellwiki/IO_inside IO Inside]<br />
<br />
[http://sigfpe.blogspot.com/2006/08/you-could-have-invented-monads-and.html You Could Have Invented Monads! (And Maybe You Already Have.) by sigfpe]<br />
<br />
<br />
==Acknowledgments==<br />
<br />
Thanks to Neil Mitchell, Daniel Fisher, Bulat Ziganzhin, Brian Hulley<br />
and Udo Stenzel for the invaluable help they gave, in the<br />
[http://www.haskell.org/mailman/listinfo/haskell-cafe haskell-cafe mailing list], <br />
in understanding this topic.<br />
<br />
I couldn't do it without their help.<br />
<br />
Obviously errors are totally mine. But this is a wiki so, please,<br />
correct them!<br />
<br />
- [[User:AndreaRossato|Andrea Rossato]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=The_Monadic_Way/Part_I&diff=5868The Monadic Way/Part I2006-09-08T10:55:42Z<p>AndreaRossato: a new and hopefully better explanation of the do-notation</p>
<hr />
<div>'''Note: this is the first part of [[The Monadic Way]]'''<br />
==An evaluation of Philip Wadler's "Monads for functional programming"==<br />
<br />
This tutorial is a "translation" of Philip Wadler's [http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf "Monads for functional programming"].<br />
(avail. from [http://homepages.inf.ed.ac.uk/wadler/topics/monads.html here])<br />
<br />
I'm a Haskell newbie trying to grasp such a difficult concept as the<br />
one of Monad and monadic computation.<br />
<br />
While [http://www.cs.utah.edu/~hal/htut/ "Yet Another Haskell Tutorial"] <br />
gave me a good understanding of the type system when it<br />
comes to monads I find it almost unreadable.<br />
<br />
But I had also Wadler's paper, and started reading it. Well, just<br />
wonderful! It explains how to ''create'' a monad!<br />
<br />
So I decided to "translate it", in order to clarify to myself the<br />
topic. And I'm now sharing this traslation ('''not completed yet'')<br />
with the hope it will be useful to someone else.<br />
<br />
Moreover, that's a wiki, so please improve it. And, specifically,<br />
correct my poor English. I'm Italian, after all.<br />
<br />
'''Note: The source of this page can be used as a Literate Haskell<br />
file and can be run with ghci or hugs: so cut paste change and run (in<br />
emacs for instance) while reading it...'''<br />
<br />
==A Simple Evaluator==<br />
<br />
Let's start with something simple: suppose we want to implement a new<br />
programming language. We just finished with<br />
[http://swiss.csail.mit.edu/classes/6.001/abelson-sussman-lectures/ Abelson and Sussman's Structure and Interpretation of Computer Programs] <br />
and we want to test what we have learned.<br />
<br />
Our programming language will be very simple: it will just compute the<br />
sum of two terms.<br />
<br />
So we have just one primitive operation (Add) that takes two constants<br />
and calculates their sum.<br />
<br />
Moreover we have just one kind of data type: Con a, which is an Int.<br />
<br />
For instance, something like:<br />
<br />
(Add (Con 5) (Con 6))<br />
<br />
should yeld:<br />
<br />
11<br />
<br />
===The basic evaluator===<br />
<br />
We will implement our language with the help of a data type<br />
constructor such as:<br />
<br />
<div id="BasicEval"><br />
<haskell><br />
<br />
> module TheMonadicWay where<br />
> data Term = Con Int<br />
> | Add Term Term<br />
> deriving (Show)<br />
<br />
</haskell><br />
<br />
After that we build our interpreter:<br />
<br />
<haskell><br />
<br />
> eval :: Term -> Int<br />
> eval (Con a) = a<br />
> eval (Add a b) = eval a + eval b<br />
<br />
</haskell><br />
<br />
That's it. Just an example:<br />
<br />
*TheMonadicWay> eval (Add (Con 5) (Con 6))<br />
11<br />
*TheMonadicWay><br />
<br />
Very very simple. The evaluator checks if its argument is of type Con<br />
Int: if it is it just returns the Int.<br />
<br />
If the argument is not of type Con, but it is of type Term, it<br />
evaluates the first Term and sums the result with the result of the<br />
evaluation of the second Term.<br />
<br />
As you may understand, our evaluator uses some of the powerful<br />
features of Haskell type system. Instead of writing a parser that<br />
takes a string (the user input) and transforms that string into an<br />
expression to be evaluated, we use the two type constructors defined<br />
for our data type Term (Con and Add) to build the expression - such as<br />
(Add (Con 5) (Con 6)) - and to match the expression's elements in our<br />
"eval" function.<br />
<br />
<br />
== Some Output, Please!==<br />
<br />
Now, that's fine, but we'd like to add some features, like providing<br />
some output, to show how the computation was carried out.<br />
<br />
Well, but Haskell is a pure functional language, with no side effects,<br />
we were told.<br />
<br />
Now we seem to be wanting to create a side effect of the computation,<br />
its output, and be able to stare at it...<br />
<br />
If we had some global variable to store the out that would be<br />
simple...<br />
<br />
But we can create the output and carry it along the computation,<br />
concatenating it with the old one, and present it at the end of the<br />
evaluation together with the evaluation of the expression given to our<br />
evaluator/interpreter!<br />
<br />
===The basic evaluator with output===<br />
<br />
Simple and neat:<br />
<div id="BasivEvalO"><br />
<haskell><br />
<br />
> type MOut a = (a, Output)<br />
> type Output = String<br />
> <br />
> formatLine :: Term -> Int -> Output<br />
> formatLine t a = "eval (" ++ show t ++ ") <= " ++ show a ++ " - " <br />
> <br />
> evalO :: Term -> MOut Int<br />
> evalO (Con a) = (a, formatLine (Con a) a)<br />
> evalO (Add t u) = ((a + b),(x ++ y ++ formatLine (Add t u) (a + b)))<br />
> where (a, x) = evalO t<br />
> (b, y) = evalO u<br />
<br />
</haskell><br />
<br />
Now we have what we want. But we had to change our evaluator quite a<br />
bit. <br />
<br />
First we added a function, formatLine, that takes an argument of type<br />
Term (the expression to be evaluated), one of type Int (the result of<br />
the evaluation of Term) and gives back an output of type Output (that<br />
is a synonymous of String). This is just a helper function to format<br />
the string to output. Not very interesting at all.<br />
<br />
The evaluator itself changed quite a lot! Now it has a different type<br />
signature: it takes an argument of type Term and produces a new type,<br />
we called it MOut, that is actually a compound pair of a variable type<br />
a (an Int in our evaluator) and a type Output, a string.<br />
<br />
So our evaluator, now, will take a Term (the type of the expressions<br />
in our new programming language) and will produce a pair, composed of<br />
the result of the evaluation (an Int) and the Output, a string.<br />
<br />
So far so good. But what's happening inside the evaluator?<br />
<br />
The first part will just return a pair with the number evaluated ("a")<br />
and the output formatted by formatLine.<br />
<br />
The second part does something more complicated: it returns a pair<br />
composed by <br />
1. the result of the evaluation of the right Term summed to the result<br />
of the evaluation of the second Term<br />
2. the output: the concatenation of the output produced by the<br />
evaluation of the right Term, the output produced by the evaluation of<br />
the left Term (each this evaluation returns a pair with the number and<br />
the output), and the formatted output of the evaluation.<br />
<br />
Let's try it:<br />
*TheMonadicWay> evalO (Add (Con 5) (Con 6))<br />
(11,"eval (Con 5) <= 5 - eval (Con 6) <= 6 - eval (Add (Con 5) (Con 6)) <= 11 - ")<br />
*TheMonadicWay><br />
<br />
It works! Let's put the output this way:<br />
eval (Con 5) <= 5 - <br />
eval (Con 6) <= 6 - <br />
eval (Add (Con 5) (Con 6)) <= 11 -<br />
<br />
Great! We are able to produce a side effect of our evaluation and<br />
present it at the end of the computation, after all.<br />
<br />
Let's have a closer look at this expression:<br />
<haskell><br />
<br />
evalO (Add t u) = ((a + b),(x ++ y ++ formatLine (Add t u) (a + b)))<br />
where (a, x) = evalO t<br />
(b, y) = evalO u<br />
<br />
</haskell><br />
<br />
Why all that? The problem is that we need:<br />
* "a" and "b" to calculate their sum (a + b), that will be the first element of the compund pair rapresenting the type (MOut) our evaluator will return <br />
* "x and "y" (the output of each evaluation) to be concatenated with the ourput of formatLine by the expression (x ++ y ++ formatLine(...)): this will be the second element of the compound pair MOut, the string part.<br />
<br />
So we need to separate the pairs produced by "evalO t" and "evalO u".<br />
<br />
We do that within the where clause (remember: evalO now produces a value of type<br />
MOut Int, i.e. a pair of an Int and a String).<br />
<br />
Then we use the single element, "extraded" within the where clause, to<br />
return a new MOut composed by <br />
<br />
((a + b),(x ++ y ++ formatLine (Add t u) (a + b))).<br />
------ -------------------------------------<br />
Int Output = String<br />
<br />
== Let's Go Monadic==<br />
<br />
Is there a more general way of doing so?<br />
<br />
Let's analyze the evaluator from another perspective. From the type<br />
perspective.<br />
<br />
We solved our problem by creating a new type, a pair of an Int (the<br />
result of the evaluation) and a String (the output of the process of<br />
evaluation).<br />
<br />
The first part of the evaluator does nothing else but creating, from a<br />
value of type Int, an object of type MOut Int (Int,Output). It does so<br />
by creating a pair with that Int and some text produced by formatLine.<br />
<br />
The second part evaluates the two Term(s) and "stores" the values thus<br />
produced in some variables to be use later to compute the output.<br />
<br />
Let's focus on the "stores" action. The correct term should be<br />
"binds".<br />
<br />
Take a function:<br />
<haskell><br />
f x = x + x<br />
</haskell><br />
"x" appears on both sides of the expression. We say that on the right<br />
side "x" is bound to the value of x given on the left side.<br />
<br />
So<br />
<haskell><br />
f 3<br />
</haskell><br />
binds x to 3 for the evaluation of the expression "x + x".<br />
<br />
Our evaluator binds "a" and "x" / "b" and "y" with the evaluation of<br />
"evalO t" and "evalO u" respectively. <br />
<br />
Then "a","b","x" and "y" will be used in the evaluation of<br />
((a+b),(x++y++formatLine)), that will produce a value of type MOut Int:<br />
<br />
<pre><br />
<br />
((a + b),(x ++ y ++ formatLine (Add t u) (a + b))).<br />
------ -------------------------------------<br />
\ / \ /<br />
Int Output = String<br />
---------------------------------<br />
\ /<br />
MOut Int <br />
</pre><br />
<br />
The binding happens in the "where" clause:<br />
<haskell><br />
where (a, x) = evalO t<br />
(b, y) = evalO u<br />
</haskell><br />
<br />
We know that there is an ad hoc operator for binding variables to a<br />
value: lambda, or \.<br />
<br />
Indeed f x = x + x is syntactic sugar for:<br />
<haskell><br />
f = \x -> x + x<br />
</haskell><br />
When we write f 3 we are actually binding "x" to 3 within what's next<br />
"->", that will be used (substituted) for evaluating f 3.<br />
<br />
So we can try to abstract this phenomenon.<br />
<br />
===Monadic evaluator with output===<br />
What we need is a function that takes our composed type MOut Int and a<br />
function in order to produce a new MOut Int, concatenating the<br />
output of the computation of the first with the output of the<br />
computation of the second.<br />
<br />
This is what bindM does:<br />
<br />
<haskell><br />
<br />
> bindM :: MOut a -> (a -> MOut b) -> MOut b<br />
> bindM m f = (b, x ++ y)<br />
> where (a, x) = m<br />
> (b, y) = f a<br />
<br />
</haskell><br />
<br />
It takes:<br />
* "m": the compound type MOut Int carrying the result of an "eval Term",<br />
* a function "f". This function will take the Int ("a") extracted by the evaluation of "m" ((a,x)=m). This function will produce a new pair: a new Int produced by a new evaluation; some new output.<br />
<br />
bindM will return the new Int in pair with the concatenated outputs<br />
resulting from the evaluation of "m" and "f a".<br />
<br />
As you see, we took the binding part out from evalO and put it in this new function.<br />
<br />
So let's write the new version of the evaluator, that we will call evalM_1:<br />
<br />
<haskell><br />
<br />
> evalM_1 :: Term -> MOut Int<br />
> evalM_1 (Con a) = (a, formatLine (Con a) a)<br />
> evalM_1 (Add t u) = bindM (evalM_1 t) (\a -> <br />
> bindM (evalM_1 u) (\b -> <br />
> ((a + b), formatLine (Add t u) (a + b))<br />
> )<br />
> )<br />
<br />
</haskell><br />
<br />
Ugly, isn't it?<br />
<br />
Let's start from the outside:<br />
<br />
<haskell><br />
bindM (evalM_1 u) (\b -> ((a + b), formatLine (Add t u) (a + b)))<br />
</haskell><br />
<br />
bindM takes the result of the evaluation "evalM_1 u", a type Mout Int,<br />
and a function. It will extract the Int from that type and use it to<br />
bind "b".<br />
<br />
So in bindM (evalM_1 u) (\b ->) "b" will be bound to the value<br />
returned by evalM_1 u, and this bound variable will be available in<br />
what comes after "->" as a bound variable (not free).<br />
<br />
Then the outer part (bindM (evalM_1 t) (\a...) will bind "a" to the<br />
value returned "evalM_1 t", the result of the evaluatuion of the first<br />
Term. This value is needed to evaluate "((a+b), formatLine...) and<br />
produce our final MOut Int.<br />
<br />
We can try to explain "bindM" in a different way by using more descriptive names.<br />
<br />
As we have seen, "bindM" extracts the Int part from our type. The Int<br />
part will be used for further computations and the Output part will be<br />
concatenated. As a result we will have a new pair with a new Int and<br />
an accumulated Output.<br />
<br />
The new version of "bindM":<br />
<haskell><br />
<br />
> getIntFromType typeMOut doSomething = (newInt,oldOutput ++ newOutput)<br />
> where (oldInt,oldOutput) = typeMOut<br />
> (newInt,newOutput) = (doSomething oldInt)<br />
<br />
</haskell><br />
<br />
As you can see it does the very same things that "bindM" does: it<br />
takes something of type MOut and a function to perform some<br />
computation with the Int part. <br />
<br />
In the "where" clause, the old Int and the old output<br />
will be extracted from our type MOut (first line of the "where"<br />
clause). <br />
<br />
A new Int and a new output will be extracted from evaluating<br />
(doSomething oldInt) in the second line.<br />
<br />
Our function will return the new Int and the concatenated outputs.<br />
<br />
We do not need to define our doSomething function, because it will be<br />
an anonymous function:<br />
<br />
<haskell><br />
<br />
> evaluator (Con a) = (a, "output-")<br />
> evaluator (Add t u) = <br />
> getIntFromType (evaluator t) <br />
> (\firstInt -> getIntFromType (evaluator u) <br />
> (\secondInt -> ((firstInt + secondInt),("-newoutput"))))<br />
<br />
</haskell><br />
<br />
As you can see we are feeding our "getIntFromType" with the evaluation<br />
of an expression ("evaluator t" and "evaluator u"). The second<br />
argument of "getIntFromType" is an anonymous function that takes the<br />
"oldInt" and does something with it.<br />
<br />
So we have a series of nested anonymous functions. Their arguments<br />
("\firstInt" and "\secondInt") will be used to produce the computation<br />
we need ("(firstInt + secondInt). Moreover "getIntFromType" will take<br />
care of concatenating the outputs.<br />
<br />
This is the result:<br />
<br />
*TheMonadicWay> evaluator (Add (Con 5) (Con 6))<br />
(11,"output-output--newoutput")<br />
*TheMonadicWay> <br />
<br />
Going back to our "bindM", we can now use lambda notation to write our<br />
evaluator in a more convinient way:<br />
<br />
<haskell><br />
<br />
> evalM_2 :: Term -> MOut Int<br />
> evalM_2 (Con a) = (a, formatLine (Con a) a)<br />
> evalM_2 (Add t u) = evalM_2 t `bindM` \a -><br />
> evalM_2 u `bindM` \b -><br />
> ((a + b), (formatLine (Add t u) (a + b)))<br />
<br />
</haskell><br />
<br />
Now, look at the first part:<br />
<br />
<haskell><br />
evalM_2 (Con a) = (a, formatLine (Con a) a)<br />
</haskell><br />
<br />
We could use a more general way of creating some output. <br />
<br />
We can create a function that takes an Int and returns the type MOut<br />
Int. We do that by pairing the received Int with an empty string "".<br />
<br />
This will be a general way of creating an object with type MOut Int starting from an Int.<br />
<br />
Or, more generaly, a function that takes something of a variable type<br />
a, and return an object of type MOut a, a coumpunt object made up of<br />
an element of type a, and one of type String.<br />
<br />
There it is:<br />
<br />
<haskell><br />
<br />
> mkM :: a -> MOut a<br />
> mkM a = (a, "")<br />
<br />
</haskell><br />
<br />
As you can see, this function will just push an Int and an empty<br />
string ("") inside our type MOut.<br />
<br />
Then we need a method of inserting some text in our object of type<br />
MOut. So we will take a string and return it paired with a void<br />
element "()":<br />
<br />
<haskell><br />
<br />
> outPut :: Output -> MOut ()<br />
> outPut x = ((), x)<br />
<br />
</haskell><br />
<br />
Very simple: we have a string "x" (Output) and create a pair with a ()<br />
instead of an Int, and the output.<br />
<br />
You can see this function as one that pushes a string, paired with a<br />
void int, inside our type MOut.<br />
<br />
Now we can rewrite:<br />
<haskell><br />
evalM_2 (Con a) = (a, formatLine (Con a) a)<br />
</haskell><br />
using the bindM function:<br />
<haskell><br />
evalM_2 (Con a) = outPut (formatLine (Con a) a) `bindM` \_ -> mkM a<br />
</haskell><br />
<br />
First we create an object of type MOut with the Int part (). As you<br />
see bindM will not use it ("\_"), but will concatenate the String part<br />
with the result of mkM, which in turn is the empry string "".<br />
<br />
In other words, first we insert the Output part (a string) in our<br />
MOut, and then we insert the Int paired with an empty string: "bindM"<br />
will not use the void int (the anonymous function will not use it's<br />
argument: "\_"), but will take care of concatenating the non empty<br />
string inserted by "outPut" with the empty one inserted by "mkM".<br />
<br />
Let's rewrite the evaluator:<br />
<br />
<haskell><br />
<br />
> evalM_3 :: Term -> MOut Int<br />
> evalM_3 (Con a) = outPut (formatLine (Con a) a) `bindM` \_ -> <br />
> mkM a<br />
> evalM_3 (Add t u) = evalM_3 t `bindM` \a -><br />
> evalM_3 u `bindM` \b -><br />
> outPut (formatLine (Add t u) (a + b)) `bindM` \_ -> <br />
> mkM (a + b)<br />
<br />
</haskell><br />
<br />
Well, this is fine, definetly better then before, anyway.<br />
<br />
Still we use `bindM` \_ -> that binds something we do not use (_). We<br />
could write a function for this specific case, when we concatenate<br />
computations without the need of binding variables for later uses.<br />
Let's call it `combineM`:<br />
<br />
<haskell><br />
<br />
> combineM :: MOut a -> MOut b -> MOut b<br />
> combineM m f = m `bindM` \_ -> f<br />
<br />
</haskell><br />
<br />
This is just something that will allow us to write the evaluator in a<br />
more concise way. <br />
<br />
So the new evaluator:<br />
<br />
<haskell><br />
<br />
> evalM :: Term -> MOut Int<br />
> evalM (Con a) = outPut (formatLine (Con a) a) `combineM` <br />
> mkM a<br />
> evalM (Add t u) = evalM t `bindM` \a -><br />
> evalM u `bindM` \b -><br />
> outPut (formatLine (Add t u) (a + b)) `combineM` <br />
> mkM (a + b)<br />
<br />
</haskell><br />
<br />
Let's put everything together (changing M into MO, so that this file<br />
will be still usable as a Literate Haskell file):<br />
<br />
<haskell><br />
<br />
> type MO a = (a, Out)<br />
> type Out = String<br />
<br />
> mkMO :: a -> MO a<br />
> mkMO a = (a, "")<br />
<br />
> bindMO :: MO a -> (a -> MO b) -> MO b<br />
> bindMO m f = (b, x ++ y)<br />
> where (a, x) = m<br />
> (b, y) = f a<br />
<br />
> combineMO :: MO a -> MO b -> MO b<br />
> combineMO m f = m `bindM` \_ -> f<br />
<br />
> outMO :: Out -> MO ()<br />
> outMO x = ((), x)<br />
<br />
> evalMO :: Term -> MO Int<br />
> evalMO (Con a) = outMO (formatLine (Con a) a) `combineMO`<br />
> mkMO a<br />
> evalMO (Add t u) = evalMO t `bindMO` \a -><br />
> evalMO u `bindMO` \b -><br />
> outMO (formatLine (Add t u) (a + b)) `combineMO` <br />
> mkMO (a + b)<br />
<br />
</haskell><br />
<br />
==What Does Bind Bind?==<br />
<br />
<div id="Bind"><br />
The evaluator looks like:<br />
<haskell><br />
evalM t >>= \a -> evalM u >>= \b -> outPut "something" >>= \_ -> mkM (a +b)<br />
</haskell><br />
where >>= is bindMO, obviously.<br />
<br />
Let's do some substitution, writing the type of their output of each function:<br />
* evalMO t => (a,Out) - where a is Int<br />
* evalMO u => (b,Out) - where b is the same of a, an Int, but with a different value<br />
* outMO Out = ((),Out)<br />
* mkMO (a+b) => ((a+b),Out) - where (a+b) is the same of a and b, but with a different value from either a and b<br />
<br />
<pre><br />
B | (a,Out) >>= \a -> (b,Out) >>= \b -> ((),Out) >>= \_ >>= ((a + b), Out)---\<br />
i | V V V V V V V V ^ ^ ^ ^ |\<br />
n | |__|________^ | | ^ | | | | | | | MOut Int <=> ((a+b), Out)<br />
d |_____|__(++)__|_Out_|__|__(++)__V_Out_|___|___(++)_|_(++)__|___|____|_____|/<br />
i | | |______(b)__|_____|_____(b)____|__(b)__|___|<br />
n | |_________(a)___________|____________|__(a)__|<br />
g | |_____()_____|<br />
<br />
</pre><br />
<br />
Clear, isn't it?<br />
<br />
"bindMO" is just a function that takes care of gluing together, inside<br />
a data type, a sequence of computations!<br />
<br />
== Some Sugar, Please!==<br />
Now our evaluator has been completely transformed into a monadic<br />
evaluator. That's what it is: a monad.<br />
<br />
We have a function that constructs an object of type MO Int, formed by<br />
a pair: the result of the evaluation and the accumulated<br />
(concatenated) output.<br />
<br />
The process of accumulation and the act of parting the MO Int into its<br />
component is buried into bindMO, now, that can also preserve some<br />
value for later uses.<br />
<br />
So we have:<br />
* MO a type constructor for a type carrying a pair composed by an Int and a String;<br />
* bindMO, that gives a direction to the process of evaluation: it concatenates computations and captures some side effects we created (the direction is given by the changes in the Out part: there's a "before" when Out was something and there's a "later" when Out is something else).<br />
* mkMO lets us create an object of type MO Int starting from an Int.<br />
<br />
As you see this is all we need to create a monad. In other words<br />
monads arise from the type system and the lambda calculus. Everything<br />
else is just syntactic sugar.<br />
<br />
So, let's have a look at that sugar: the famous do-notation!<br />
<br />
===Monadic evaluator with output in do-notation===<br />
<br />
In order to be able to use the "do-notation" we need to define a new<br />
type and make it an instance of the Monad class. To make a new type an<br />
instance of the Monad class we will have to define the two methods of<br />
this class: (>>=) and "return".<br />
<br />
This is not going to be difficult, because we already created these<br />
two methods: "bindM" and "mkM". Now we will have to rewrite them in<br />
order to reflect the fact that we are not going to use a type, for our<br />
evaluator, that is a synonymous of other types, as we did before.<br />
Indeed our MOut was defined with the "type" keyword. Now we will have<br />
to define a "real" new type with either "newtype" or "data". Since we<br />
are not going to need multiple constructors, we will use "newtype".<br />
<br />
<div id="MonadicEvalIO"><br />
<haskell><br />
<br />
> newtype Eval_IO a = Eval_IO (a, O)<br />
> deriving (Show)<br />
> type O = String<br />
<br />
</haskell><br />
<br />
This is our new type: it will have a single type constructor, whose<br />
name is the same of the type name ("Eval_IO"). The type constructor<br />
takes a parameter ("a"), a variable type, and will build a type formed<br />
by a type "a" (an Int in our case) and a String (O is indeed<br />
synonymous of String).<br />
<br />
We now need to define our "bind" function to reflect the fact that we<br />
are now using a "real" type, and, to unpack its content, we need to do<br />
pattern-matching we the type constructor "Eval_IO". Moreover, since we<br />
must return an Eval_IO type, we will use the type constructor also for<br />
building the new type with the new int and the concatenated output.<br />
<br />
For the rest our "bind" function will be identical to the one we<br />
defined before.<br />
<br />
We are going to use very descriptive names:<br />
<br />
<haskell><br />
<br />
> getInt monad doSomething = Eval_IO (newInt,oldOutput ++ newOutput)<br />
> where Eval_IO (oldInt,oldOutput) = monad<br />
> Eval_IO (newInt,newOutput) = (doSomething oldInt)<br />
<br />
</haskell><br />
<br />
As you can see, we are using Eval_IO to build the result of the<br />
computation to be returned by getInt: "Eval_IO (newInt,oldOutput ++<br />
newOutput)". And we are using it to match the internal components of<br />
our type in the "where" clause.<br />
<br />
We also need to create a function that, like mkO, will take an Int and,<br />
using the type constructor "Eval_IO", will create an object of type<br />
Eval_IO with that Int and an empty string:<br />
<br />
<haskell><br />
<br />
> createEval_IO :: a -> Eval_IO a<br />
> createEval_IO int = Eval_IO (int,"")<br />
<br />
</haskell><br />
<br />
And, finally, we need a function that will insert, in our type, a<br />
string and a void ():<br />
<br />
<haskell><br />
<br />
> print_IO :: O -> Eval_IO ()<br />
> print_IO string = Eval_IO ((), string)<br />
<br />
</haskell> <br />
<br />
With these functions we could write our monadic evaluator without the<br />
"do-notation" like this:<br />
<br />
<haskell><br />
<br />
> evalM_4 :: Term -> Eval_IO Int<br />
> evalM_4 (Con a) = createEval_IO a<br />
> evalM_4 (Add t u) = evalM_4 t `getInt` \a -><br />
> evalM_4 u `getInt` \b -><br />
> print_IO (formatLine (Add t u) (a + b)) `getInt` \_ -><br />
> createEval_IO (a + b)<br />
<br />
</haskell><br />
<br />
It is very similar to the previous evaluator, as you can see. The only<br />
differences are related to the fact that we are now using a "real"<br />
type and not a type synonymous: this requires the use of the type<br />
constructor to match the type and its internal part (as we do in the<br />
"where" clause of our "bind" function: "getInt") or to build the type<br />
(as we do in the "bind" function to return the new Int with the<br />
concatenated output).<br />
<br />
Running this evaluator will produce:<br />
<br />
*TheMonadicWay> evalM_4 (Add (Con 6) (Con 12))<br />
Eval_IO (18,"eval (Add (Con 6) (Con 12)) <= 18 - ")<br />
*TheMonadicWay> <br />
<br />
Now we have everything we need to declare our type, Eval_IO, as an<br />
instance of the Monad class:<br />
<haskell><br />
<br />
> instance Monad Eval_IO where<br />
> return a = createEval_IO a<br />
> (>>=) m f = getInt m f<br />
<br />
</haskell><br />
<br />
As you see we are just using our defined functions as methods for our<br />
instance of the Monad class.<br />
<br />
This is all we need to do. Notice that we do not have to define the<br />
"combineM" function, for chaining computation, as we do with "getInt",<br />
without binding variables for later use within the series of nested<br />
anonymous functions (the "doSomething" part) that will form the "do"<br />
block.<br />
<br />
This function comes for free by just defining our type as an instance<br />
of the Monad class. Indeed, if you look at the definition of the Monad<br />
class in the Prelude you see that "combineM", or (>>) is deduced by<br />
the definition of (>>=):<br />
<br />
<haskell><br />
class Monad m where<br />
return :: a -> m a<br />
(>>=) :: m a -> (a -> m b) -> m b<br />
(>>) :: m a -> m b -> m b<br />
fail :: String -> m a<br />
<br />
-- Minimal complete definition: (>>=), return<br />
p >> q = p >>= \ _ -> q<br />
fail s = error s<br />
</haskell><br />
<br />
You can see that the "combineM"" method (or (>>)) is automatically<br />
derived by the "bindMO" (or >>=) method:<br />
<br />
<haskell><br />
p >> q = p >>= \ _ -> q<br />
</haskell><br />
<br />
We can now write our evaluator using the do-notation:<br />
<br />
<haskell><br />
<br />
> eval_IO :: Term -> Eval_IO Int<br />
> eval_IO (Con a) = do print_IO (formatLine (Con a) a)<br />
> return a<br />
> eval_IO (Add t u) = do a <- eval_IO t<br />
> b <- eval_IO u<br />
> print_IO (formatLine (Add t u) (a + b))<br />
> return (a + b)<br />
<br />
</haskell><br />
<br />
As you can see the anonymous functions are gone. Instead we use this:<br />
a <- eval_IO t<br />
<br />
This seems like an assignment, that cannot be possible in Haskell. In<br />
fact it is just the way our anonymous function's arguments is bound<br />
within a do block.<br />
<br />
Even if it does not seem like a series of nested anonymous functions,<br />
this is what actually a do block is.<br />
<br />
Our monad is defined by three elements: <br />
* a type, with its type constructor(s);<br />
* a bind method: it will bind an unwritten anonymous function's argument to the value of the Int part of our type, or more generally, of the variable type "a". It will also create a series of anonymous functions: a line for each function;<br />
* a "return" function, to insert, into out type, a value of type Int, or, more generally, a value of variable type "a".<br />
<br />
Additionally, we need a function to insert a string in our type,<br />
string that the derived "bind" (>>) will concatenate ignoring the void<br />
(), our "print_IO".<br />
<br />
Let's see the evaluator with output in action:<br />
*TheMonadicWay> eval_IO (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) <br />
Eval_IO (54,"eval (Con 6) <= 6 - eval (Con 16) <= 16 - eval (Con 20) <= 20 - eval (Con 12) <= 12 - \<br />
eval (Add (Con 20) (Con 12)) <= 32 - eval (Add (Con 16) (Add (Con 20) (Con 12))) <= 48 - \<br />
eval (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) <= 54 - ")<br />
*TheMonadicWay> <br />
<br />
Let's format the output part:<br />
eval (Con 6) <= 6 <br />
eval (Con 16) <= 16 <br />
eval (Con 20) <= 20 <br />
eval (Con 12) <= 12 <br />
eval (Add (Con 20) (Con 12)) <= 32 <br />
eval (Add (Con 16) (Add (Con 20) (Con 12))) <= 48 <br />
eval (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) <= 54 <br />
<br />
==Type and Newtype: What Happened to Our Output?==<br />
<br />
Well, actually something happened to the output. Let's compare the<br />
output of evalMO (the monadic evaluator written without the<br />
do-notation) and eval_IO:<br />
<br />
*TheMonadicWay> evalMO (Con 6)<br />
(6,"eval (Con 6) <= 6 - ")<br />
*TheMonadicWay> eval_IO (Con 6)<br />
Eval_IO (6,"eval (Con 6) <= 6 - ")<br />
*TheMonadicWay> <br />
<br />
They look almost the same, but they are not the same: the output of<br />
eval_IO has the Eval_IO stuff. It must be related to the changes we<br />
had to do to our evaluator in order to use the do-conation, obviously.<br />
<br />
What's changed?<br />
<br />
First the type definition. We have now:<br />
<br />
<haskell><br />
newtype Eval_IO a = Eval_IO (a, O)<br />
deriving (Show)<br />
</haskell><br />
<br />
instead of <br />
<br />
<haskell><br />
type MO a = (a, Out)<br />
</haskell><br />
<br />
Now <hask>return a</hask> is the product of the application of the<br />
type constructor Eval_IO to the pair that are going to form our monad.<br />
<br />
"return" takes an Int and insert it into our monad. It will also<br />
insert an empty String "" that (>>=) or (>>) will then concatenate in<br />
the sequence of computations they glue together.<br />
<br />
The same for (>>=): it will now return something constructed by<br />
Eval_IO: "newInt", the result of the application of "doSomething" to<br />
"oldInt" (better, the binding of "newInt" in "doSomething");<br />
"oldOutput" (matched by <hask>Eval_IO (oldInt, oldOutput)</hask> with<br />
the evaluation of "monad") and "newOutput", (matched by "Eval_IO(newInt,newOutput)" with<br />
the evaluation of "(doSomething monad)".<br />
<br />
That is to say: in the "where" clause, we are matching for the<br />
elements paired in a type Eval_IO: this is indeed the type of "monad"<br />
(corresponding to "eval_IO t" in the body of the evaluator) and<br />
"(doSomething monad)" (where "doSomething" correspond to the<br />
evaluation of "eval_IO u" within an anonymous function with \oldInt as<br />
its argument, argument bound to the result of the previous evaluation<br />
of "monad", that is to say "eval_IO t").<br />
<br />
And so, "Eval_IO (oldInt,oldOutput) = monad" means: match "oldInt" and<br />
"oldOutput", paired in a type Eval_IO, and that are produced by the<br />
evaluation of "monad" (that is to say: "eval_IO t"). The same for<br />
Eval_IO (newInt,newOutput): match "newInt" and "newOutput" produced by<br />
the evaluation of "(doSomething monad)".<br />
<br />
So the output of the evaluator is now not simply a pair made of and<br />
Int and a String. It is a specific type (Eval_IO) that happens to<br />
carry a pair of an Int and a String. But, if we want the Int and the<br />
string, we have to extract them from the Eval_IO type, as we do in the<br />
"where" clause: we ''unpack'' our type object (let's call it with its<br />
name: our monad!) and take out the Int and the String to feed the next<br />
function application and the output generation.<br />
<br />
The same to insert something in our monad: if we want to create a pair<br />
of an Int and a String, pair of type Eval_IO, we now have to ''pack''<br />
them together by using our type constructor, feeding it with pair<br />
composed by and Int and a String. This is what we do with the "return"<br />
method of out monad and with "print_IO" function, where:<br />
* return insert into the monad an Int;<br />
* print_IO insert into the monad a String.<br />
<br />
So, why cannot we use the old <hask>type MO a = (a, Out)</hask> that<br />
did not required all this additional work (apart the need to<br />
specifically define (>>)?<br />
<br />
Type MO is just a synonymous for (a,Out): the two can be substituted<br />
one for the other. That's it.<br />
<br />
We did not have to pack "a" and "Out" together with a type constructor<br />
to have a new type MO.<br />
<br />
As a consequence, we cannot use MO as an instance of Monad, and so, we<br />
cannot use with it the syntactic sugar we needed: the do-notation.<br />
<br />
That is to say: a type created with the "type" keyword cannot be an<br />
instance of a class, and cannot inherits its methods (in our case<br />
(>>=, >> and return). And without those methods the do-notation is not<br />
usable.<br />
<br />
==Errare Monadicum Est==<br />
<br />
Now that we have a basic understanding of what a monad is, and does,<br />
we will further explore it by making some changes to our evaluator.<br />
<br />
In this section we will se how to handle exceptions in our monadic<br />
evaluator.<br />
<br />
Suppose that we want to stop the execution of our monad if some<br />
conditions occurs. If our evaluator was to compute divisions, instead<br />
of sums, then we would like to stop the evaluator when a division by<br />
zero occurs, possibly producing some output, instead of the result of<br />
the evaluation of the expression, that explains what happened.<br />
<br />
Basic error handling.<br />
<br />
We will do so starting from the beginning once again...<br />
<br />
===The basic evaluator, non monadic, with exception===<br />
<br />
We just take our basic evaluator, without any output, and write a<br />
method to stop execution if a condition occurs: <br />
<br />
<haskell><br />
<br />
> data M a = Raise Exception<br />
> | Return a<br />
> deriving (Show)<br />
> type Exception = String<br />
<br />
</haskell><br />
<br />
Now, our monad is of datatype "M a" which can either be constructed<br />
with the "Raise" constructor, that takes a String (Exception is a<br />
synonymous of String), or by the "Return" constructor, that takes a<br />
variable type ("a"), an Int in our case.<br />
<br />
<haskell><br />
<br />
> evalE :: Term -> M Int<br />
> evalE (Con a) = Return a<br />
<br />
</haskell><br />
<br />
If evalE matches a Con it will construct a type Return with, inside, the content of the Con.<br />
<br />
<haskell><br />
<br />
> evalE (Add a b) = <br />
> case evalE a of<br />
> Raise e -> Raise e<br />
> Return a -><br />
> case evalE b of <br />
> Raise e -> Raise e<br />
> Return b -><br />
> if (a+b) == 42<br />
> then Raise "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else Return (a+b)<br />
<br />
</haskell><br />
<br />
If evalE matches an Add it will check if evaluating the first part<br />
produces a "Raise" or a "Return": in the first case it will return a<br />
"Raise" whose content is the same received. <br />
<br />
If instead the evaluation produces a value of a type matched by<br />
"Return", the evaluator will evaluate the second term of Add.<br />
<br />
If this returns a "Raise", a "Raise" will be returned all the way up<br />
the recursion, otherwise the evaluator will check whether a condition<br />
for raising a "Raise" exists. If not, it will return a "Return" with<br />
the sum inside.<br />
<br />
Test it with:<br />
<br />
evalE (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2)))<br />
<br />
<br />
===The basic evaluator, monadic, with exceptions===<br />
<br />
In order to produce a monadic version of the previous evaluator, the<br />
one that raises exceptions, we just need to abstract out from the<br />
evaluator all that case analysis.<br />
<br />
<haskell><br />
<br />
> data M1 a = Except Exception<br />
> | Ok {showM :: a }<br />
> deriving (Show)<br />
<br />
</haskell><br />
<br />
The data type didn't change at all. Well, we changed the name of the<br />
Return type constructor (now Ok) so that this constructor can coexist<br />
with the previous one in the same Literate Haskell file.<br />
<br />
<div id="MonadicEvalE"><br />
<haskell><br />
<br />
> instance Monad M1 where<br />
> return a = Ok a<br />
> m >>= f = case m of<br />
> Except e -> Except e<br />
> Ok a -> f a<br />
<br />
</haskell><br />
<br />
Binding operations are now very easy. Basically we check:<br />
* if the result of the evaluation of "m" produces an exception (first match: Except e ->...), in which case we return its content by constructing our M1 Int with the "Raise" constructor".<br />
* if the result of the evaluation of "m" is matched with the "Ok" constructor, we get its content and use it to bind the argument of "f" to its value.<br />
<br />
<hask>return a</hask> will just use the Ok type constructor for<br />
inserting "a" (in our case an Int) into M1 Int, the type of our monad.<br />
<br />
<haskell><br />
<br />
> raise :: Exception -> M1 a<br />
> raise e = Except e<br />
<br />
</haskell><br />
<br />
This is just a helper function to construct our "M1 a" type with the<br />
Raise constructor. It takes a string and returns a type (M1 a) to be<br />
matched with the "Raise" constructor.<br />
<br />
<haskell><br />
<br />
> eval_ME :: Term -> M1 Int<br />
> eval_ME (Con a) = do return a<br />
> eval_ME (Add t u) = do a <- eval_ME t<br />
> b <- eval_ME u<br />
> if (a+b) == 42<br />
> then raise "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else return (a + b)<br />
<br />
</haskell><br />
<br />
The evaluator itself is very simple. We bind "a" with the result of<br />
"eval_ME t", "b" with the result of "eval_ME u", and we check for a<br />
condition: <br />
* if the condition is met we raise an exception, that is to say: we return a value constructed with the "Raise" constructor. This value will be matched by ">>=" in the next recursion. And >>= will just return it all the way up the recursion.<br />
* if the condition is not me, we return a value constructed with the "Return" type constructor and go on with the recursion...<br />
<br />
Run with:<br />
<br />
eval_ME (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2)))<br />
<br />
It is noteworthy the fact that in our datatype definition we used a<br />
label field with a label selector (we called it showM), even though it<br />
was not used in our code. We will use this methodology later on.<br />
<br />
So, just to refresh your memory:<br />
<br />
<haskell><br />
<br />
> data Person = Person {name :: String,<br />
> age :: Int,<br />
> hobby :: String<br />
> } deriving (Show)<br />
<br />
> andreaRossato = Person "Andrea" 37 "Haskell The Monadic Way"<br />
> personName (Person a b c) = a<br />
<br />
</haskell><br />
<br />
will produce:<br />
*TheMonadicWay> andreaRossato<br />
Person {name = "Andrea", age = 37, hobby = "Haskell The Monadic Way"}<br />
*TheMonadicWay> personName andreaRossato<br />
"Andrea"<br />
*TheMonadicWay> name andreaRossato<br />
"Andrea"<br />
*TheMonadicWay> age andreaRossato<br />
37<br />
*TheMonadicWay> hobby andreaRossato<br />
"Haskell The Monadic Way"<br />
*TheMonadicWay> <br />
<br />
<br />
===Monadic evaluator with output and exceptions===<br />
<br />
We will now try to combine the [[The Monadic Way Part I#MonadicEvalIO|output-producing monadic evaluator]] <br />
with [[The Monadic Way Part I#MonadicEvalE| exception producing one]].<br />
<br />
<br />
<haskell><br />
<br />
> data M2 a = Ex Exception<br />
> | Done {unpack :: (a,O) }<br />
> deriving (Show)<br />
<br />
</haskell><br />
<br />
Now we need a datatype with two constructor: one to produce a value<br />
type "M2 a" using "Ex String" and one for value type "M2 a" (Int in<br />
this case) using "Done a".<br />
<br />
'''Note''' that we changed the name of the exception type constructor<br />
from "Raise" to "Ex" just to make the two coexist in the same Literate<br />
Haskell file.<br />
<br />
The constructor "Done a" is defined with a label sector: <hask>Done {unpack :: (a,O)}</hask> <br />
is equivalent to <hask>Done (a,O)</hask>. <br />
<br />
The only difference is that, this way, we are also defining a method<br />
to retrieve the pair (a,O) (in our case "O" is a synonymous for<br />
String, whereas "a" is a variable type) from an object of type "Done<br />
a".<br />
<br />
<haskell><br />
<br />
> instance Monad M2 where<br />
> return a = Done (a, "")<br />
> m >>= f = case m of<br />
> Ex e -> Ex e<br />
> Done (a, x) -> case (f a) of<br />
> Ex e1 -> Ex e1<br />
> Done (b, y) -> Done (b, x ++ y)<br />
<br />
</haskell><br />
<br />
Now our binding operations gets more complicated by the fact that we<br />
have to concatenate the output, as we did before, '''and''' check for<br />
exceptions.<br />
<br />
It is not possible to do has we did in the [[The Monadic Way Part I#MonadicEvalE| exception producing evaluator]], <br />
where we could check just for "m" (remember the "m" in the first run<br />
stands for "eval t").<br />
<br />
Since at the end we must return the output produced by the evaluation<br />
of "m" ''concatenated'' with the output produced by the evaluation of<br />
"f a" (where "a" is returned by "m", paired with "x" by "Done"), now<br />
we must check if we '''do have''' an output from "f a" produced by<br />
"Done".<br />
<br />
Indeed, now, "f a" can also produce a value constructed by "Ex", and<br />
this value does not contain the pair as the value produced by "Done".<br />
<br />
So, we evaluate "m": <br />
* if we match a value produced by type constructor "Ex" we return a value produced by type constructor "Ex" whose content is the one we extracted in the matching;<br />
* if we match a value produced by "Done" we match the pair it carries "(a,x)" and we analyze what "f a" returns:<br />
** if "f a" returns a value produced by "Ex" we extract the exception and we return it, constructing a value with "Ex"<br />
** if "f a" returns a value produced by "Done" we return "b" and the concatenated "x" and "y". <br />
<br />
And now the evaluator:<br />
<br />
<haskell><br />
<br />
> raise_IOE :: Exception -> M2 a<br />
> raise_IOE e = Ex e<br />
<br />
</haskell><br />
<br />
This is the function to insert in our monad (M2) an exception: we take<br />
a String and produce a value applying the type constructor for<br />
exception "Ex" to its value.<br />
<br />
<haskell><br />
<br />
> print_IOE :: O -> M2 ()<br />
> print_IOE x = Done ((), x)<br />
<br />
</haskell><br />
<br />
The function to produce output is the very same of the one of the [[The Monadic Way Part I#MonadicEvalIO|output-producing monadic evaluator]].<br />
<br />
<haskell><br />
<br />
> eval_IOE :: Term -> M2 Int<br />
> eval_IOE (Con a) = do print_IOE (formatLine (Con a) a)<br />
> return a<br />
> eval_IOE (Add t u) = do a <- eval_IOE t<br />
> b <- eval_IOE u<br />
> let out = formatLine (Add t u) (a + b)<br />
> print_IOE out<br />
> if (a+b) == 42<br />
> then raise_IOE $ out ++ "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else return (a + b)<br />
<br />
</haskell><br />
<br />
The evaluator procedure did not change very much from the one of the [[The Monadic Way Part I#MonadicEvalIO|output-producing monadic evaluator]].<br />
<br />
We just added the case analysis to see if the condition for raising an exception is met.<br />
<br />
Running with<br />
<br />
eval_IOE (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2)))<br />
<br />
will produce <br />
<br />
Ex "eval (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2))) <= 42 - <br />
The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
<br />
Look at the <hask>let</hask> clause within the do-notation. We do not<br />
need to use the "let ... in" construction: since all bound variables<br />
remain bound within a <hask>do</hask> procedure (see [[The Monadic Way Part I#Bind|here]]), <br />
we do not need the "in" to specify "where" the variable "out" will be bound in!<br />
<br />
==We Need A State==<br />
<br />
We will keep on adding complexity to our monadic evaluator and this<br />
time we will add a counter. We just want to count the number of<br />
iterations (the number of times "eval" will be called) needed to<br />
evaluate the expression.<br />
<br />
===The basic evaluator, non monadic, with a counter===<br />
<br />
As before we will start by adding this feature to our [[The Monadic Way Part I#BasicEval|basic evaluator]]. <br />
<br />
A method to count the number of iterations, since the lack of<br />
assignment and destructive updates (such as for i=0;i<10;i++;),<br />
is to add an argument to our function, the initial state, number that<br />
in each call of the function will be increased and passed to the next<br />
function call.<br />
<br />
And so, very simply:<br />
<br />
<haskell><br />
<br />
> -- non monadic<br />
> type St a = State -> (a, State)<br />
> type State = Int<br />
> evalNMS :: Term ->St Int<br />
> evalNMS (Con a) x = (a, x + 1)<br />
> evalNMS (Add t u) x = let (a, y) = evalNMS t x in<br />
> let (b, z) = evalNMS u y in<br />
> (a + b, z +1)<br />
<br />
</haskell><br />
<br />
Now evalNMS takes two arguments: the expression of type Term and an<br />
State (which is a synonymous for Int), and will produce a pair<br />
(a,State), that is to say a pair with a variable type "a" and an Int.<br />
<br />
The operations in the evaluator are very similar to the non monadic [[The Monadic Way Part I#BasivEvalO|output producing evaluator]].<br />
<br />
We are now using the "let ... in" clause, instead of the "where", and we are increasing the counter "z" the comes from the evaluation of the second term, but the basic operation are the same:<br />
* we evaluate "evalNMS t x" where "x" is the initial state, and we match and bind the result in "let (a, y) ... in"<br />
* we evaluate "evalNMS u y", where "y" was bound to the value returned by the previous evaluation, and we match and bind the result in "let (b, z) ... in"<br />
* we return a pair formed by the sum of the result (a+b) and the state z increased by 1. <br />
<br />
Let's try it:<br />
<br />
*TheMonadicWay> evalNMS (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2))) 0<br />
(42,7)<br />
*TheMonadicWay> <br />
<br />
As you see we must pass to "evalNMS"the initial state of our counter: 0.<br />
<br />
Look at the type signature of the function "evalNMS":<br />
<haskell><br />
evalNMS :: Term ->St Int<br />
</haskell><br />
<br />
From this signature you could argue that our function takes only<br />
'''one''' argument. But since our type St is defined with the "type"<br />
keyword, St can be substituted with what comes after the "=" sign. So,<br />
the real type signature of our function is:<br />
<br />
<haskell><br />
evalNMS :: Term -> State -> (Int,State)<br />
</haskell><br />
<br />
<div if="typeNewtype"><br />
Just to refresh your memory:<br />
<br />
<haskell><br />
<br />
> type IamAfunction a = (a -> a)<br />
> newtype IamNotAfunction a = NF (a -> a)<br />
> newtype IamNotAfunctionButYouCanUnPackAndRunMe a = F { unpackAndRun :: (a -> a) }<br />
<br />
> a = \x -> x * x<br />
<br />
> a1 :: IamAfunction Int<br />
> a1 = a<br />
<br />
> a2 :: IamNotAfunction Int<br />
> a2 = NF a<br />
<br />
> a3 :: IamNotAfunctionButYouCanUnPackAndRunMe Int<br />
> a3 = F a<br />
<br />
</haskell><br />
<br />
<br />
*TheMonadicWay> a 4<br />
16<br />
*TheMonadicWay> a1 4<br />
16<br />
*TheMonadicWay> a2 4<br />
<br />
<interactive>:1:0:<br />
The function `a2' is applied to one arguments,<br />
but its type `IamNotAfunction Int' has only 0<br />
In the definition of `it': it = a2 4<br />
*TheMonadicWay> a3 4<br />
<br />
<interactive>:1:0:<br />
The function `a3' is applied to one arguments,<br />
but its type `IamNotAfunctionButYouCanUnPackAndRunMe Int' has only 0<br />
In the definition of `it': it = a3 4<br />
*TheMonadicWay> unpackAndRun a3 4<br />
16<br />
*TheMonadicWay><br />
<br />
This means that "a1" is a partial application hidden by a type<br />
synonymous. <br />
<br />
"a2" and "a3" are not function types. They are types that<br />
have a functional value. <br />
<br />
Moreover, since we defined the type constructor of type<br />
"IamNotAfunctionButYouCanUnPackAndRunMe", F, with a label field, in<br />
that label field we defined a method (a label selector) to "extract"<br />
the function from the type "IamNotAfunctionButYouCanUnPackAndRunMe",<br />
and run it:<br />
<br />
<haskell><br />
unpackAndRun a3 4<br />
</haskell><br />
<br />
And what about "a2"? Is it lost forever?<br />
<br />
Obviously not! We need to write a function that unpacks a type<br />
"IamNotAfunction", using its type constructor NF to match the internal<br />
function:<br />
<br />
<haskell><br />
<br />
> unpackNF :: IamNotAfunction a -> a -> a<br />
> unpackNF (NF f) = f<br />
<br />
</haskell><br />
<br />
and run:<br />
<br />
*TheMonadicWay> unpackNF a2 4<br />
16<br />
*TheMonadicWay> <br />
<br />
As you see, "unpackNF" definition is a partial application: we specify<br />
one argument to get a function that gets another argument.<br />
<br />
A label selector does the same thing.<br />
<br />
Later we will see the importance of this distinction, quite obvious<br />
for haskell gurus, but not for us. Till now.<br />
<br />
===The evaluator, monadic, with a counter, without do-notation===<br />
<br />
<br />
'''(Text to be done yet: just a summary)'''<br />
<br />
The moadic version without do notation.<br />
<br />
<haskell><br />
<br />
> -- monadic<br />
> type MS a = State -> (a, State)<br />
<br />
> mkMS :: a -> MS a<br />
> mkMS a = \x -> (a, x)<br />
<br />
> bindMS :: MS a -> (a -> MS b) -> MS b<br />
> bindMS m f = \x -> <br />
> let (a, y) = m x in<br />
> let (b, z) = f a y in<br />
> (b, z)<br />
<br />
> combineMS :: MS a -> MS b -> MS b<br />
> combineMS m f = m `bindMS` \_ -> f<br />
<br />
> incState :: MS ()<br />
> incState = \s -> ((), s + 1)<br />
<br />
> evalMS :: Term -> MS Int<br />
> evalMS (Con a) = incState `combineMS` mkMS a<br />
> evalMS (Add t u) = evalMS t `bindMS` \a -><br />
> evalMS u `bindMS` \b -><br />
> incState `combineMS` mkMS (a + b)<br />
<br />
> --evalMS (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) 0<br />
<br />
</haskell><br />
<br />
===The evaluator, monadic, with counter and output, without do-notation===<br />
<br />
Now we'll add Output to the stateful evaluator:<br />
<br />
<haskell><br />
<br />
> -- state and output<br />
<br />
> type MSO a = State -> (a, State, Output)<br />
<br />
> mkMSO :: a -> MSO a<br />
> mkMSO a = \s -> (a, s, "")<br />
<br />
> bindMSO :: MSO a -> (a -> MSO b) -> MSO b<br />
> bindMSO m f = \x -> <br />
> let (a, y, s1) = m x in<br />
> let (b, z, s2) = f a y in<br />
> (b, z, s1 ++ s2)<br />
<br />
> combineMSO :: MSO a -> MSO b -> MSO b<br />
> combineMSO m f = m `bindMSO` \_ -> f<br />
<br />
> incMSOstate :: MSO ()<br />
> incMSOstate = \s -> ((), s + 1, "")<br />
<br />
> outMSO :: Output -> MSO ()<br />
> outMSO = \x s -> ((),s, x)<br />
<br />
> evalMSO :: Term -> MSO Int<br />
> evalMSO (Con a) = incMSOstate `combineMSO` <br />
> outMSO (formatLine (Con a) a) `combineMSO` <br />
> mkMSO a<br />
> evalMSO (Add t u) = evalMSO t `bindMSO` \a -><br />
> evalMSO u `bindMSO` \b -><br />
> incMSOstate `combineMSO` <br />
> outMSO (formatLine (Add t u) (a + b)) `combineMSO`<br />
> mkMSO (a + b)<br />
<br />
> --evalMSO (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) 0<br />
<br />
</haskell><br />
<br />
===The monadic evaluator with output and counter in do-notation=== <br />
<br />
State, Output in do-notation. Look at how much the complexity of our<br />
(>>=) founction is increasing:<br />
<br />
<haskell><br />
<br />
> -- thanks to Brian Hulley<br />
<br />
> newtype MSIO a = MSIO (State -> (a, State, Output))<br />
> instance Monad MSIO where<br />
> return a = MSIO (\s -> (a, s, ""))<br />
> (MSIO m) >>= f = MSIO $ \x -><br />
> let (a, y, s1) = m x in<br />
> let MSIO runNextStep = f a in<br />
> let (b, z, s2) = runNextStep y in<br />
> (b, z, s1 ++ s2)<br />
<br />
<br />
> incMSOIstate :: MSIO ()<br />
> incMSOIstate = MSIO (\s -> ((), s + 1, ""))<br />
<br />
> print_MSOI :: Output -> MSIO ()<br />
> print_MSOI x = MSIO (\s -> ((),s, x))<br />
<br />
> eval_MSOI :: Term -> MSIO Int<br />
> eval_MSOI (Con a) = do incMSOIstate<br />
> print_MSOI (formatLine (Con a) a)<br />
> return a<br />
<br />
> eval_MSOI (Add t u) = do a <- eval_MSOI t<br />
> b <- eval_MSOI u<br />
> incMSOIstate<br />
> print_MSOI (formatLine (Add t u) (a + b))<br />
> return (a + b)<br />
<br />
> run_MSOI :: MSIO a -> State -> (a, State, Output)<br />
> run_MSOI (MSIO f) s = f s<br />
<br />
> --run_MSOI (eval_MSOI (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12))))) 0<br />
<br />
</haskell><br />
<br />
===Another version of the monadic evaluator with output and counter, in do-notation===<br />
<br />
This is e second version that exploit label fields in datatype to<br />
decrease the complexity of the binding operations.<br />
<br />
<haskell><br />
<br />
> -- Thanks Udo Stenzel<br />
<br />
> newtype Eval_SIO a = Eval_SIO { unPackMSIOandRun :: State -> (a, State, Output) }<br />
> instance Monad Eval_SIO where<br />
> return a = Eval_SIO (\s -> (a, s, ""))<br />
> (>>=) m f = Eval_SIO (\x -><br />
> let (a, y, s1) = unPackMSIOandRun m x in<br />
> let (b, z, s2) = unPackMSIOandRun (f a) y in<br />
> (b, z, s1 ++ s2))<br />
<br />
> incSIOstate :: Eval_SIO ()<br />
> incSIOstate = Eval_SIO (\s -> ((), s + 1, ""))<br />
<br />
> print_SIO :: Output -> Eval_SIO ()<br />
> print_SIO x = Eval_SIO (\s -> ((),s, x))<br />
<br />
> eval_SIO :: Term -> Eval_SIO Int<br />
> eval_SIO (Con a) = do incSIOstate<br />
> print_SIO (formatLine (Con a) a)<br />
> return a<br />
> eval_SIO (Add t u) = do a <- eval_SIO t<br />
> b <- eval_SIO u<br />
> incSIOstate<br />
> print_SIO (formatLine (Add t u) (a + b))<br />
> return (a + b)<br />
<br />
> --unPackMSIOandRun (eval_SIO (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12))))) 0<br />
<br />
</haskell><br />
<br />
<br />
==If There's A State We Need Some Discipline: Dealing With Complexity==<br />
<br />
In order to increase the complexity of our monad now we will try to<br />
mix State (counter), Exceptions and Output.<br />
<br />
This is an email [http://www.haskell.org/pipermail/haskell-cafe/2006-August/017672.html I send to the haskell-cafe mailing list]:<br />
<br />
<pre><br />
Now I'm trying to create a statefull evaluator, with output and<br />
exception, but I'm facing a problem I seem not to be able to<br />
conceptually solve.<br />
<br />
Take the code below.<br />
Now, in order to get it run (and try to debug) the Eval_SOI type has a<br />
Raise constructor that produces the same type of SOIE. Suppose instead it<br />
should be constructing something like Raise "something". <br />
Moreover, I wrote a second version of >>=, commented out.<br />
This is just to help me illustrate to problem I'm facing.<br />
<br />
Now, >>= is suppose to return Raise if "m" is matched against Raise<br />
(second version commented out).<br />
If "m" matches SOIE it must return a SOIE only if "f a" does not<br />
returns a Raise (output must be concatenated).<br />
<br />
I seem not to be able to find a way out. Moreover, I cannot understand<br />
if a way out can be possibly found. Something suggests me it could be<br />
related to that Raise "something".<br />
But my feeling is that functional programming could be something out<br />
of the reach of my mind... by the way, I teach Law, so perhaps you'll<br />
forgive me...;-)<br />
<br />
If you can help me to understand this problem all I can promise is<br />
that I'll mention your help in the tutorial I'm trying to write on<br />
"the monadic way"... that seems to lead me nowhere.<br />
<br />
Thanks for your kind attention.<br />
<br />
Andrea<br />
</pre><br />
<br />
This was the code:<br />
<br />
<haskell><br />
data Eval_SOI a = Raise { unPackMSOIandRun :: State -> (a, State, Output) }<br />
| SOIE { unPackMSOIandRun :: State -> (a, State, Output) }<br />
<br />
instance Monad Eval_SOI where<br />
return a = SOIE (\s -> (a, s, ""))<br />
m >>= f = SOIE (\x -><br />
let (a, y, s1) = unPackMSOIandRun m x in<br />
case f a of<br />
SOIE nextRun -> let (b, z, s2) = nextRun y in <br />
(b, z, s1 ++ s2)<br />
Raise e1 -> e1 y --only this happens<br />
<br />
)<br />
-- (>>=) m f = case m of<br />
-- Raise e -> error "ciao" -- why this is not going to happen?<br />
-- SOIE a -> SOIE (\x -><br />
-- let (a, y, s1) = unPackMSOIandRun m x in<br />
-- let (b, z, s2) = unPackMSOIandRun (f a) y in <br />
-- (b, z, s1 ++ s2)) <br />
<br />
<br />
incSOIstate :: Eval_SOI ()<br />
incSOIstate = SOIE (\s -> ((), s + 1, ""))<br />
<br />
print_SOI :: Output -> Eval_SOI ()<br />
print_SOI x = SOIE (\s -> ((),s, x))<br />
<br />
raise x e = Raise (\s -> (x,s,e))<br />
<br />
eval_SOI :: Term -> Eval_SOI Int<br />
eval_SOI (Con a) = do incSOIstate<br />
print_SOI (formatLine (Con a) a)<br />
return a<br />
eval_SOI (Add t u) = do a <- eval_SOI t<br />
b <- eval_SOI u<br />
incSOIstate<br />
print_SOI (formatLine (Add t u) (a + b))<br />
if (a + b) == 42 <br />
then raise (a+b) " = The Ultimate Answer!!"<br />
else return (a + b)<br />
<br />
runEval exp = case eval_SOI exp of<br />
Raise a -> a 0<br />
SOIE p -> let (result, state, output) = p 0 in<br />
(result,state,output)<br />
<br />
<br />
<br />
--runEval (Add (Con 10) (Add (Con 28) (Add (Con 40) (Con 2))))<br />
</haskell><br />
<br />
This code will produce <br />
<br />
eval (Con 10) <= 10 -<br />
eval (Con 28) <= 28 -<br />
eval (Con 40) <= 40 -<br />
eval (Con 2) <= 2 - = The Ultimate Answer!!<br />
eval (Add (Con 28) (Add (Con 40) (Con 2))) <= 70 -<br />
eval (Add (Con 10) (Add (Con 28) (Add (Con 40) (Con 2)))) <= 80 -<br />
<br />
The exception appears in the output, but executioon is not stopped.<br />
<br />
===Monadic evaluator with output, counter and exception, in do-notation===<br />
<br />
Brian Hulley [http://www.haskell.org/pipermail/haskell-cafe/2006-August/017680.html came up with this solution]:<br />
<br />
<haskell><br />
<br />
> -- thanks to Brian Hulley<br />
> data Result a<br />
> = Good a State Output<br />
> | Bad State Output Exception<br />
> deriving Show<br />
<br />
> newtype Eval_SIOE a = SIOE {runSIOE :: State -> Result a}<br />
<br />
> instance Monad Eval_SIOE where<br />
> return a = SIOE (\s -> Good a s "")<br />
> m >>= f = SIOE $ \x -><br />
> case runSIOE m x of<br />
> Good a y o1 -><br />
> case runSIOE (f a) y of<br />
> Good b z o2 -> Good b z (o1 ++ o2)<br />
> Bad z o2 e -> Bad z (o1 ++ o2) e<br />
> Bad z o2 e -> Bad z o2 e<br />
<br />
> raise_SIOE e = SIOE (\s -> Bad s "" e)<br />
<br />
> incSIOEstate :: Eval_SIOE ()<br />
> incSIOEstate = SIOE (\s -> Good () (s + 1) "")<br />
<br />
> print_SIOE :: Output -> Eval_SIOE ()<br />
> print_SIOE x = SIOE (\s -> Good () s x)<br />
<br />
<br />
> eval_SIOE :: Term -> Eval_SIOE Int<br />
> eval_SIOE (Con a) = do incSIOEstate<br />
> print_SIOE (formatLine (Con a) a)<br />
> return a<br />
> eval_SIOE (Add t u) = do a <- eval_SIOE t<br />
> b <- eval_SIOE u<br />
> incSIOEstate<br />
> let out = formatLine (Add t u) (a + b)<br />
> print_SIOE out<br />
> if (a+b) == 42<br />
> then raise_SIOE $ out ++ "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else return (a + b)<br />
<br />
> runEval exp = case runSIOE (eval_SIOE exp) 0 of<br />
> Bad s o e -> "Error at iteration n. " ++ show s ++ <br />
> " - Output stack = " ++ o ++ <br />
> " - Exception = " ++ e<br />
> Good a s o -> "Result = " ++ show a ++ <br />
> " - Iterations = " ++ show s ++ " - Output = " ++ o<br />
<br />
</haskell><br />
<br />
Run with runEval (Add (Con 18) (Add (Con 12) (Add (Con 10) (Con 2))))<br />
<br />
==Suggested Readings==<br />
<br />
Cale Gibbard, [http://haskell.org/haskellwiki/Monads_as_Containers Monads as Containers]<br />
<br />
Jeff Newbern, [http://www.nomaware.com/monads/html/index.html All About Monads]<br />
<br />
[http://haskell.org/haskellwiki/IO_inside IO Inside]<br />
<br />
[http://sigfpe.blogspot.com/2006/08/you-could-have-invented-monads-and.html You Could Have Invented Monads! (And Maybe You Already Have.) by sigfpe]<br />
<br />
<br />
==Acknowledgments==<br />
<br />
Thanks to Neil Mitchell, Daniel Fisher, Bulat Ziganzhin, Brian Hulley<br />
and Udo Stenzel for the invaluable help they gave, in the<br />
[http://www.haskell.org/mailman/listinfo/haskell-cafe haskell-cafe mailing list], <br />
in understanding this topic.<br />
<br />
I couldn't do it without their help.<br />
<br />
Obviously errors are totally mine. But this is a wiki so, please,<br />
correct them!<br />
<br />
- [[User:AndreaRossato|Andrea Rossato]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=The_Monadic_Way/Part_I&diff=5860The Monadic Way/Part I2006-09-07T08:51:09Z<p>AndreaRossato: a new example of "bind"</p>
<hr />
<div>'''Note: this is the first part of [[The Monadic Way]]'''<br />
==An evaluation of Philip Wadler's "Monads for functional programming"==<br />
<br />
This tutorial is a "translation" of Philip Wadler's [http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf "Monads for functional programming"].<br />
(avail. from [http://homepages.inf.ed.ac.uk/wadler/topics/monads.html here])<br />
<br />
I'm a Haskell newbie trying to grasp such a difficult concept as the<br />
one of Monad and monadic computation.<br />
<br />
While [http://www.cs.utah.edu/~hal/htut/ "Yet Another Haskell Tutorial"] <br />
gave me a good understanding of the type system when it<br />
comes to monads I find it almost unreadable.<br />
<br />
But I had also Wadler's paper, and started reading it. Well, just<br />
wonderful! It explains how to ''create'' a monad!<br />
<br />
So I decided to "translate it", in order to clarify to myself the<br />
topic. And I'm now sharing this traslation ('''not completed yet'')<br />
with the hope it will be useful to someone else.<br />
<br />
Moreover, that's a wiki, so please improve it. And, specifically,<br />
correct my poor English. I'm Italian, after all.<br />
<br />
'''Note: The source of this page can be used as a Literate Haskell<br />
file and can be run with ghci or hugs: so cut paste change and run (in<br />
emacs for instance) while reading it...'''<br />
<br />
==A Simple Evaluator==<br />
<br />
Let's start with something simple: suppose we want to implement a new<br />
programming language. We just finished with<br />
[http://swiss.csail.mit.edu/classes/6.001/abelson-sussman-lectures/ Abelson and Sussman's Structure and Interpretation of Computer Programs] <br />
and we want to test what we have learned.<br />
<br />
Our programming language will be very simple: it will just compute the<br />
sum of two terms.<br />
<br />
So we have just one primitive operation (Add) that takes two constants<br />
and calculates their sum.<br />
<br />
Moreover we have just one kind of data type: Con a, which is an Int.<br />
<br />
For instance, something like:<br />
<br />
(Add (Con 5) (Con 6))<br />
<br />
should yeld:<br />
<br />
11<br />
<br />
===The basic evaluator===<br />
<br />
We will implement our language with the help of a data type<br />
constructor such as:<br />
<br />
<div id="BasicEval"><br />
<haskell><br />
<br />
> module TheMonadicWay where<br />
> data Term = Con Int<br />
> | Add Term Term<br />
> deriving (Show)<br />
<br />
</haskell><br />
<br />
After that we build our interpreter:<br />
<br />
<haskell><br />
<br />
> eval :: Term -> Int<br />
> eval (Con a) = a<br />
> eval (Add a b) = eval a + eval b<br />
<br />
</haskell><br />
<br />
That's it. Just an example:<br />
<br />
*TheMonadicWay> eval (Add (Con 5) (Con 6))<br />
11<br />
*TheMonadicWay><br />
<br />
Very very simple. The evaluator checks if its argument is of type Con<br />
Int: if it is it just returns the Int.<br />
<br />
If the argument is not of type Con, but it is of type Term, it<br />
evaluates the first Term and sums the result with the result of the<br />
evaluation of the second Term.<br />
<br />
As you may understand, our evaluator uses some of the powerful<br />
features of Haskell type system. Instead of writing a parser that<br />
takes a string (the user input) and transforms that string into an<br />
expression to be evaluated, we use the two type constructors defined<br />
for our data type Term (Con and Add) to build the expression - such as<br />
(Add (Con 5) (Con 6)) - and to match the expression's elements in our<br />
"eval" function.<br />
<br />
<br />
== Some Output, Please!==<br />
<br />
Now, that's fine, but we'd like to add some features, like providing<br />
some output, to show how the computation was carried out.<br />
<br />
Well, but Haskell is a pure functional language, with no side effects,<br />
we were told.<br />
<br />
Now we seem to be wanting to create a side effect of the computation,<br />
its output, and be able to stare at it...<br />
<br />
If we had some global variable to store the out that would be<br />
simple...<br />
<br />
But we can create the output and carry it along the computation,<br />
concatenating it with the old one, and present it at the end of the<br />
evaluation together with the evaluation of the expression given to our<br />
evaluator/interpreter!<br />
<br />
===The basic evaluator with output===<br />
<br />
Simple and neat:<br />
<div id="BasivEvalO"><br />
<haskell><br />
<br />
> type MOut a = (a, Output)<br />
> type Output = String<br />
> <br />
> formatLine :: Term -> Int -> Output<br />
> formatLine t a = "eval (" ++ show t ++ ") <= " ++ show a ++ " - " <br />
> <br />
> evalO :: Term -> MOut Int<br />
> evalO (Con a) = (a, formatLine (Con a) a)<br />
> evalO (Add t u) = ((a + b),(x ++ y ++ formatLine (Add t u) (a + b)))<br />
> where (a, x) = evalO t<br />
> (b, y) = evalO u<br />
<br />
</haskell><br />
<br />
Now we have what we want. But we had to change our evaluator quite a<br />
bit. <br />
<br />
First we added a function, formatLine, that takes an argument of type<br />
Term (the expression to be evaluated), one of type Int (the result of<br />
the evaluation of Term) and gives back an output of type Output (that<br />
is a synonymous of String). This is just a helper function to format<br />
the string to output. Not very interesting at all.<br />
<br />
The evaluator itself changed quite a lot! Now it has a different type<br />
signature: it takes an argument of type Term and produces a new type,<br />
we called it MOut, that is actually a compound pair of a variable type<br />
a (an Int in our evaluator) and a type Output, a string.<br />
<br />
So our evaluator, now, will take a Term (the type of the expressions<br />
in our new programming language) and will produce a pair, composed of<br />
the result of the evaluation (an Int) and the Output, a string.<br />
<br />
So far so good. But what's happening inside the evaluator?<br />
<br />
The first part will just return a pair with the number evaluated ("a")<br />
and the output formatted by formatLine.<br />
<br />
The second part does something more complicated: it returns a pair<br />
composed by <br />
1. the result of the evaluation of the right Term summed to the result<br />
of the evaluation of the second Term<br />
2. the output: the concatenation of the output produced by the<br />
evaluation of the right Term, the output produced by the evaluation of<br />
the left Term (each this evaluation returns a pair with the number and<br />
the output), and the formatted output of the evaluation.<br />
<br />
Let's try it:<br />
*TheMonadicWay> evalO (Add (Con 5) (Con 6))<br />
(11,"eval (Con 5) <= 5 - eval (Con 6) <= 6 - eval (Add (Con 5) (Con 6)) <= 11 - ")<br />
*TheMonadicWay><br />
<br />
It works! Let's put the output this way:<br />
eval (Con 5) <= 5 - <br />
eval (Con 6) <= 6 - <br />
eval (Add (Con 5) (Con 6)) <= 11 -<br />
<br />
Great! We are able to produce a side effect of our evaluation and<br />
present it at the end of the computation, after all.<br />
<br />
Let's have a closer look at this expression:<br />
<haskell><br />
<br />
evalO (Add t u) = ((a + b),(x ++ y ++ formatLine (Add t u) (a + b)))<br />
where (a, x) = evalO t<br />
(b, y) = evalO u<br />
<br />
</haskell><br />
<br />
Why all that? The problem is that we need:<br />
* "a" and "b" to calculate their sum (a + b), that will be the first element of the compund pair rapresenting the type (MOut) our evaluator will return <br />
* "x and "y" (the output of each evaluation) to be concatenated with the ourput of formatLine by the expression (x ++ y ++ formatLine(...)): this will be the second element of the compound pair MOut, the string part.<br />
<br />
So we need to separate the pairs produced by "evalO t" and "evalO u".<br />
<br />
We do that within the where clause (remember: evalO now produces a value of type<br />
MOut Int, i.e. a pair of an Int and a String).<br />
<br />
Then we use the single element, "extraded" within the where clause, to<br />
return a new MOut composed by <br />
<br />
((a + b),(x ++ y ++ formatLine (Add t u) (a + b))).<br />
------ -------------------------------------<br />
Int Output = String<br />
<br />
== Let's Go Monadic==<br />
<br />
Is there a more general way of doing so?<br />
<br />
Let's analyze the evaluator from another perspective. From the type<br />
perspective.<br />
<br />
We solved our problem by creating a new type, a pair of an Int (the<br />
result of the evaluation) and a String (the output of the process of<br />
evaluation).<br />
<br />
The first part of the evaluator does nothing else but creating, from a<br />
value of type Int, an object of type MOut Int (Int,Output). It does so<br />
by creating a pair with that Int and some text produced by formatLine.<br />
<br />
The second part evaluates the two Term(s) and "stores" the values thus<br />
produced in some variables to be use later to compute the output.<br />
<br />
Let's focus on the "stores" action. The correct term should be<br />
"binds".<br />
<br />
Take a function:<br />
<haskell><br />
f x = x + x<br />
</haskell><br />
"x" appears on both sides of the expression. We say that on the right<br />
side "x" is bound to the value of x given on the left side.<br />
<br />
So<br />
<haskell><br />
f 3<br />
</haskell><br />
binds x to 3 for the evaluation of the expression "x + x".<br />
<br />
Our evaluator binds "a" and "x" / "b" and "y" with the evaluation of<br />
"evalO t" and "evalO u" respectively. <br />
<br />
Then "a","b","x" and "y" will be used in the evaluation of<br />
((a+b),(x++y++formatLine)), that will produce a value of type MOut Int:<br />
<br />
<pre><br />
<br />
((a + b),(x ++ y ++ formatLine (Add t u) (a + b))).<br />
------ -------------------------------------<br />
\ / \ /<br />
Int Output = String<br />
---------------------------------<br />
\ /<br />
MOut Int <br />
</pre><br />
<br />
The binding happens in the "where" clause:<br />
<haskell><br />
where (a, x) = evalO t<br />
(b, y) = evalO u<br />
</haskell><br />
<br />
We know that there is an ad hoc operator for binding variables to a<br />
value: lambda, or \.<br />
<br />
Indeed f x = x + x is syntactic sugar for:<br />
<haskell><br />
f = \x -> x + x<br />
</haskell><br />
When we write f 3 we are actually binding "x" to 3 within what's next<br />
"->", that will be used (substituted) for evaluating f 3.<br />
<br />
So we can try to abstract this phenomenon.<br />
<br />
===Monadic evaluator with output===<br />
What we need is a function that takes our composed type MOut Int and a<br />
function in order to produce a new MOut Int, concatenating the<br />
output of the computation of the first with the output of the<br />
computation of the second.<br />
<br />
This is what bindM does:<br />
<br />
<haskell><br />
<br />
> bindM :: MOut a -> (a -> MOut b) -> MOut b<br />
> bindM m f = (b, x ++ y)<br />
> where (a, x) = m<br />
> (b, y) = f a<br />
<br />
</haskell><br />
<br />
It takes:<br />
* "m": the compound type MOut Int carrying the result of an "eval Term",<br />
* a function "f". This function will take the Int ("a") extracted by the evaluation of "m" ((a,x)=m). This function will produce a new pair: a new Int produced by a new evaluation; some new output.<br />
<br />
bindM will return the new Int in pair with the concatenated outputs<br />
resulting from the evaluation of "m" and "f a".<br />
<br />
As you see, we took the binding part out from evalO and put it in this new function.<br />
<br />
So let's write the new version of the evaluator, that we will call evalM_1:<br />
<br />
<haskell><br />
<br />
> evalM_1 :: Term -> MOut Int<br />
> evalM_1 (Con a) = (a, formatLine (Con a) a)<br />
> evalM_1 (Add t u) = bindM (evalM_1 t) (\a -> <br />
> bindM (evalM_1 u) (\b -> <br />
> ((a + b), formatLine (Add t u) (a + b))<br />
> )<br />
> )<br />
<br />
</haskell><br />
<br />
Ugly, isn't it?<br />
<br />
Let's start from the outside:<br />
<br />
<haskell><br />
bindM (evalM_1 u) (\b -> ((a + b), formatLine (Add t u) (a + b)))<br />
</haskell><br />
<br />
bindM takes the result of the evaluation "evalM_1 u", a type Mout Int,<br />
and a function. It will extract the Int from that type and use it to<br />
bind "b".<br />
<br />
So in bindM (evalM_1 u) (\b ->) "b" will be bound to the value<br />
returned by evalM_1 u, and this bound variable will be available in<br />
what comes after "->" as a bound variable (not free).<br />
<br />
Then the outer part (bindM (evalM_1 t) (\a...) will bind "a" to the<br />
value returned "evalM_1 t", the result of the evaluatuion of the first<br />
Term. This value is needed to evaluate "((a+b), formatLine...) and<br />
produce our final MOut Int.<br />
<br />
We can try to explain "bindM" in a different way by using more descriptive names.<br />
<br />
As we have seen, "bindM" extracts the Int part from our type. The Int<br />
part will be used for further computations and the Output part will be<br />
concatenated. As a result we will have a new pair with a new Int and<br />
an accumulated Output.<br />
<br />
The new version of "bindM":<br />
<haskell><br />
<br />
> getIntFromType typeMOut doSomething = (newInt,oldOutput ++ newOutput)<br />
> where (oldInt,oldOutput) = typeMOut<br />
> (newInt,newOutput) = (doSomething oldInt)<br />
<br />
</haskell><br />
<br />
As you can see it does the very same things that "bindM" does: it<br />
takes something of type MOut and a function to perform some<br />
computation with the Int part. <br />
<br />
In the "where" clause, the old Int and the old output<br />
will be extracted from our type MOut (first line of the "where"<br />
clause). <br />
<br />
A new Int and a new output will be extracted from evaluating<br />
(doSomething oldInt) in the second line.<br />
<br />
Our function will return the new Int and the concatenated outputs.<br />
<br />
We do not need to define our doSomething function, because it will be<br />
an anonymous function:<br />
<br />
<haskell><br />
<br />
> evaluator (Con a) = (a, "output-")<br />
> evaluator (Add t u) = <br />
> getIntFromType (evaluator t) <br />
> (\firstInt -> getIntFromType (evaluator u) <br />
> (\secondInt -> ((firstInt + secondInt),("-newoutput"))))<br />
<br />
</haskell><br />
<br />
As you can see we are feeding our "getIntFromType" with the evaluation<br />
of an expression ("evaluator t" and "evaluator u"). The second<br />
argument of "getIntFromType" is an anonymous function that takes the<br />
"oldInt" and does something with it.<br />
<br />
So we have a series of nested anonymous functions. Their arguments<br />
("\firstInt" and "\secondInt") will be used to produce the computation<br />
we need ("(firstInt + secondInt). Moreover "getIntFromType" will take<br />
care of concatenating the outputs.<br />
<br />
This is the result:<br />
<br />
*TheMonadicWay> evaluator (Add (Con 5) (Con 6))<br />
(11,"output-output--newoutput")<br />
*TheMonadicWay> <br />
<br />
Going back to our "bindM", we can now use lambda notation to write our<br />
evaluator in a more convinient way:<br />
<br />
<haskell><br />
<br />
> evalM_2 :: Term -> MOut Int<br />
> evalM_2 (Con a) = (a, formatLine (Con a) a)<br />
> evalM_2 (Add t u) = evalM_2 t `bindM` \a -><br />
> evalM_2 u `bindM` \b -><br />
> ((a + b), (formatLine (Add t u) (a + b)))<br />
<br />
</haskell><br />
<br />
Now, look at the first part:<br />
<br />
<haskell><br />
evalM_2 (Con a) = (a, formatLine (Con a) a)<br />
</haskell><br />
<br />
We could use a more general way of creating some output. <br />
<br />
We can create a function that takes an Int and returns the type MOut<br />
Int. We do that by pairing the received Int with an empty string "".<br />
<br />
This will be a general way of creating an object with type MOut Int starting from an Int.<br />
<br />
Or, more generaly, a function that takes something of a variable type<br />
a, and return an object of type MOut a, a coumpunt object made up of<br />
an element of type a, and one of type String.<br />
<br />
There it is:<br />
<br />
<haskell><br />
<br />
> mkM :: a -> MOut a<br />
> mkM a = (a, "")<br />
<br />
</haskell><br />
<br />
As you can see, this function will just push an Int and an empty<br />
string ("") inside our type MOut.<br />
<br />
Then we need a method of inserting some text in our object of type<br />
MOut. So we will take a string and return it paired with a void<br />
element "()":<br />
<br />
<haskell><br />
<br />
> outPut :: Output -> MOut ()<br />
> outPut x = ((), x)<br />
<br />
</haskell><br />
<br />
Very simple: we have a string "x" (Output) and create a pair with a ()<br />
instead of an Int, and the output.<br />
<br />
You can see this function as one that pushes a string, paired with a<br />
void int, inside our type MOut.<br />
<br />
Now we can rewrite:<br />
<haskell><br />
evalM_2 (Con a) = (a, formatLine (Con a) a)<br />
</haskell><br />
using the bindM function:<br />
<haskell><br />
evalM_2 (Con a) = outPut (formatLine (Con a) a) `bindM` \_ -> mkM a<br />
</haskell><br />
<br />
First we create an object of type MOut with the Int part (). As you<br />
see bindM will not use it ("\_"), but will concatenate the String part<br />
with the result of mkM, which in turn is the empry string "".<br />
<br />
In other words, first we insert the Output part (a string) in our<br />
MOut, and then we insert the Int paired with an empty string: "bindM"<br />
will not use the void int (the anonymous function will not use it's<br />
argument: "\_"), but will take care of concatenating the non empty<br />
string inserted by "outPut" with the empty one inserted by "mkM".<br />
<br />
Let's rewrite the evaluator:<br />
<br />
<haskell><br />
<br />
> evalM_3 :: Term -> MOut Int<br />
> evalM_3 (Con a) = outPut (formatLine (Con a) a) `bindM` \_ -> <br />
> mkM a<br />
> evalM_3 (Add t u) = evalM_3 t `bindM` \a -><br />
> evalM_3 u `bindM` \b -><br />
> outPut (formatLine (Add t u) (a + b)) `bindM` \_ -> <br />
> mkM (a + b)<br />
<br />
</haskell><br />
<br />
Well, this is fine, definetly better then before, anyway.<br />
<br />
Still we use `bindM` \_ -> that binds something we do not use (_). We<br />
could write a function for this specific case, when we concatenate<br />
computations without the need of binding variables for later uses.<br />
Let's call it `combineM`:<br />
<br />
<haskell><br />
<br />
> combineM :: MOut a -> MOut b -> MOut b<br />
> combineM m f = m `bindM` \_ -> f<br />
<br />
</haskell><br />
<br />
This is just something that will allow us to write the evaluator in a<br />
more concise way. <br />
<br />
So the new evaluator:<br />
<br />
<haskell><br />
<br />
> evalM :: Term -> MOut Int<br />
> evalM (Con a) = outPut (formatLine (Con a) a) `combineM` <br />
> mkM a<br />
> evalM (Add t u) = evalM t `bindM` \a -><br />
> evalM u `bindM` \b -><br />
> outPut (formatLine (Add t u) (a + b)) `combineM` <br />
> mkM (a + b)<br />
<br />
</haskell><br />
<br />
Let's put everything together (changing M into MO, so that this file<br />
will be still usable as a Literate Haskell file):<br />
<br />
<haskell><br />
<br />
> type MO a = (a, Out)<br />
> type Out = String<br />
<br />
> mkMO :: a -> MO a<br />
> mkMO a = (a, "")<br />
<br />
> bindMO :: MO a -> (a -> MO b) -> MO b<br />
> bindMO m f = (b, x ++ y)<br />
> where (a, x) = m<br />
> (b, y) = f a<br />
<br />
> combineMO :: MO a -> MO b -> MO b<br />
> combineMO m f = m `bindM` \_ -> f<br />
<br />
> outMO :: Out -> MO ()<br />
> outMO x = ((), x)<br />
<br />
> evalMO :: Term -> MO Int<br />
> evalMO (Con a) = outMO (formatLine (Con a) a) `combineMO`<br />
> mkMO a<br />
> evalMO (Add t u) = evalMO t `bindMO` \a -><br />
> evalMO u `bindMO` \b -><br />
> outMO (formatLine (Add t u) (a + b)) `combineMO` <br />
> mkMO (a + b)<br />
<br />
</haskell><br />
<br />
==What Does Bind Bind?==<br />
<br />
<div id="Bind"><br />
The evaluator looks like:<br />
<haskell><br />
evalM t >>= \a -> evalM u >>= \b -> outPut "something" >>= \_ -> mkM (a +b)<br />
</haskell><br />
where >>= is bindMO, obviously.<br />
<br />
Let's do some substitution, writing the type of their output of each function:<br />
* evalMO t => (a,Out) - where a is Int<br />
* evalMO u => (b,Out) - where b is the same of a, an Int, but with a different value<br />
* outMO Out = ((),Out)<br />
* mkMO (a+b) => ((a+b),Out) - where (a+b) is the same of a and b, but with a different value from either a and b<br />
<br />
<pre><br />
B | (a,Out) >>= \a -> (b,Out) >>= \b -> ((),Out) >>= \_ >>= ((a + b), Out)---\<br />
i | V V V V V V V V ^ ^ ^ ^ |\<br />
n | |__|________^ | | ^ | | | | | | | MOut Int <=> ((a+b), Out)<br />
d |_____|__(++)__|_Out_|__|__(++)__V_Out_|___|___(++)_|_(++)__|___|____|_____|/<br />
i | | |______(b)__|_____|_____(b)____|__(b)__|___|<br />
n | |_________(a)___________|____________|__(a)__|<br />
g | |_____()_____|<br />
<br />
</pre><br />
<br />
Clear, isn't it?<br />
<br />
"bindMO" is just a function that takes care of gluing together, inside<br />
a data type, a sequence of computations!<br />
<br />
== Some Sugar, Please!==<br />
Now our evaluator has been completely transformed into a monadic<br />
evaluator. That's what it is: a monad.<br />
<br />
We have a function that constructs an object of type MO Int, formed by<br />
a pair: the result of the evaluation and the accumulated<br />
(concatenated) output.<br />
<br />
The process of accumulation and the act of parting the MO Int into its<br />
component is buried into bindMO, now, that can also preserve some<br />
value for later uses.<br />
<br />
So we have:<br />
* MO a type constructor for a type carrying a pair composed by an Int and a String;<br />
* bindMO, that gives a direction to the process of evaluation: it concatenates computations and captures some side effects we created (the direction is given by the changes in the Out part: there's a "before" when Out was something and there's a "later" when Out is something else).<br />
* mkMO lets us create an object of type MO Int starting from an Int.<br />
<br />
As you see this is all we need to create a monad. In other words<br />
monads arise from the type system and the lambda calculus. Everything<br />
else is just syntactic sugar.<br />
<br />
So, let's have a look at that sugar: the famous do-notation!<br />
<br />
===Basic monadic evaluator in do-notation===<br />
<br />
We will now rewrite our [[The Monadic Way Part I#BasicEval|basic evaluator]] using the do-notation.<br />
<br />
Now we have to crate a new type: this is necessary in order to use<br />
specific monadic notation and have at our disposal the more practical<br />
do-notation ('''below we will see the consequences of doing so!'''):<br />
<br />
<haskell><br />
<br />
> newtype Eval a = Eval a<br />
> deriving (Show)<br />
<br />
</haskell><br />
<br />
So, our type will be an instance of the monad class. We will have to<br />
define the methods of this class (>>= and return), but that will be<br />
easy since we have already done that when defining bindMO and mkMO:<br />
<br />
<haskell><br />
<br />
> instance Monad Eval where<br />
> return a = Eval a<br />
> Eval m >>= f = f m<br />
<br />
</haskell><br />
<br />
You can see that return will create, from an argument of a variable<br />
type a (in our case that will be an Int) an object of type Eval Int,<br />
that carries inside just an Int, the result of the evaluation of a<br />
Con.<br />
<br />
Bind (>>=) will match for an object of type Eval, extracting what's<br />
inside ("m") and will bind "m" in "f". We know that "f" must return an<br />
object of type Eval with inside an Int resulted by the computations<br />
made by "f" over "m" (that is to say, computations made by "f" where<br />
"f" is a functions with variables, and one of those variables is bound<br />
to the value resulting from the evaluation of "m").<br />
<br />
In exchange for doing so we will now be able to take the old version<br />
of our evaluator and substitute `bindMO` with >>= and `mkMO` with<br />
return: <br />
<br />
<haskell><br />
<br />
> evalM_4 :: Term -> Eval Int<br />
> evalM_4 (Con a) = return a<br />
> evalM_4 (Add t u) = evalM_4 t >>= \a -><br />
> evalM_4 u >>= \b -><br />
> return (a + b)<br />
<br />
</haskell><br />
<br />
which is equivalent, in the do-notation, to:<br />
<br />
<haskell><br />
<br />
> evalM_5 :: Term -> Eval Int<br />
> evalM_5 (Con a) = return a<br />
> evalM_5 (Add t u) = do a <- evalM_5 t<br />
> b <- evalM_5 u<br />
> return (a + b)<br />
<br />
</haskell><br />
<br />
Simple: <hask>do</hask> binds the result of "eval_M5 t" to "a", binds<br />
the result of "eval_M5 u" to "b" and then returns the sum of "a" and<br />
"b". In a very imperative style.<br />
<br />
===Monadic evaluator with output in do-notation===<br />
<br />
We can now have an image of what our monad should be, if we want it to<br />
produce output: it is out type (Eval) that is made up of a pair, and<br />
Int and a String called Output.<br />
<br />
During evaluation the first member of the pair (the Int) will "store"<br />
the results of our computation (i.e.: the procedures to calculate the<br />
final result). The second part, the String called Output, will get<br />
filled up with the concatenated output of the computation.<br />
<br />
The sequencing done by bindMO (now >>=) will take care of passing to<br />
the next evaluation the needed (way to calculate the) Int and will do<br />
some more side calculation to produce the output (concatenating<br />
outputs resulting from computation of the new Int, for instance).<br />
<br />
So we can grasp the basic concept of a monad: it is like a label which<br />
we attach to each step of the evaluation (the String attached to the<br />
Int). <br />
This label is persistent within the process of computation and at each<br />
step bindMO can do some manipulation of it.<br />
We are creating side-effects and propagating them within our monads.<br />
<br />
Ok. Let's translate our output-producing evaluator in monadic<br />
notation:<br />
<br />
<div id="MonadicEvalIO"><br />
<haskell><br />
<br />
> newtype Eval_IO a = Eval_IO (a, O)<br />
> deriving (Show)<br />
> type O = String<br />
<br />
> instance Monad Eval_IO where<br />
> return a = Eval_IO (a, "")<br />
> (>>=) m f = Eval_IO (b, x ++ y)<br />
> where Eval_IO (a, x) = m<br />
> Eval_IO (b, y) = f a<br />
> print_IO :: O -> Eval_IO ()<br />
> print_IO x = Eval_IO ((), x)<br />
<br />
> eval_IO :: Term -> Eval_IO Int<br />
> eval_IO (Con a) = do print_IO (formatLine (Con a) a)<br />
> return a<br />
> eval_IO (Add t u) = do a <- eval_IO t<br />
> b <- eval_IO u<br />
> print_IO (formatLine (Add t u) (a + b))<br />
> return (a + b)<br />
<br />
</haskell><br />
<br />
Let's see the evaluator with output in action:<br />
*TheMonadicWay> eval_IO (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) <br />
Eval_IO (54,"eval (Con 6) <= 6 - eval (Con 16) <= 16 - eval (Con 20) <= 20 - eval (Con 12) <= 12 - \<br />
eval (Add (Con 20) (Con 12)) <= 32 - eval (Add (Con 16) (Add (Con 20) (Con 12))) <= 48 - \<br />
eval (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) <= 54 - ")<br />
*TheMonadicWay> <br />
<br />
Let's format the output part:<br />
eval (Con 6) <= 6 <br />
eval (Con 16) <= 16 <br />
eval (Con 20) <= 20 <br />
eval (Con 12) <= 12 <br />
eval (Add (Con 20) (Con 12)) <= 32 <br />
eval (Add (Con 16) (Add (Con 20) (Con 12))) <= 48 <br />
eval (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) <= 54 <br />
<br />
==What Happened to Our Output??==<br />
<br />
Well, actually something happened to the output. Let's compare the<br />
output of evalMO (the monadic evaluator written without the<br />
do-notation) and eval_IO:<br />
<br />
*TheMonadicWay> evalMO (Con 6)<br />
(6,"eval (Con 6) <= 6 - ")<br />
*TheMonadicWay> eval_IO (Con 6)<br />
Eval_IO (6,"eval (Con 6) <= 6 - ")<br />
*TheMonadicWay> <br />
<br />
They look almost the same, but they are not the same: the output of<br />
eval_IO has the Eval_IO stuff. It must be related to the changes we<br />
had to do to our evaluator in order to use the do-conation, obviously.<br />
<br />
What's changed?<br />
<br />
First the type definition. We have now:<br />
<br />
<haskell><br />
newtype Eval_IO a = Eval_IO (a, O)<br />
deriving (Show)<br />
</haskell><br />
<br />
instead of <br />
<br />
<haskell><br />
type MO a = (a, Out)<br />
</haskell><br />
<br />
Moreover our bindMO and mkMO functions changed too, to reflect the<br />
change of the type definition:<br />
<br />
<haskell><br />
instance Monad Eval_IO where<br />
return a = Eval_IO (a, "")<br />
(>>=) m f = Eval_IO (b, x ++ y)<br />
where Eval_IO (a, x) = m<br />
Eval_IO (b, y) = f a<br />
</haskell><br />
<br />
Now <hask>return a</hask> is the product of the application of the<br />
type constructor Eval_IO to the pair that are going to form our monad.<br />
<br />
"return" takes an Int and insert it into our monad. It will also<br />
insert an empty String "" that (>>=) or (>>) will then concatenate in<br />
the sequence of computations they glue together.<br />
<br />
The same for (>>=): it will now return something constructed by<br />
Eval_IO: "b", the result of the application of "f" to "a" (better, the<br />
binding of "a" in "f") and "x" (matched by <hask>Eval_IO (a, x)</hask> with<br />
the evaluation of "m") and "y", (matched by "Eval_IO(b,y)" with the<br />
evaluation of "f a".<br />
<br />
That is to say: in the "where" clause, we are matching for the<br />
elements paired in a type Eval_IO: this is indeed the type of "m"<br />
(corresponding to "eval_IO t" in the body of the evaluator) and "f a"<br />
(where "f" correspond to another application of "eval_IO" to the<br />
result of the previous application of "m").<br />
<br />
And so, "Eval_IO (a,x) = m" means: match "a" and "x", paired in a type<br />
Eval_IO, and that are produced by the evaluation of "m" (that is to<br />
say: "eval_IO t"). The same for Eval_IO (b,y): match "b" and "y"<br />
produced by the evaluation of "f a".<br />
<br />
So the output of the evaluator is now not simply a pair made of and<br />
Int and a String. It is a specific type (Eval_IO) that happens to<br />
carry a pair of an Int and a String. But, if we want the Int and the<br />
string, we have to extract them from the Eval_IO type, as we do in the<br />
"where" clause: we ''unpack'' our type object (let's call it with its<br />
name: our monad!) and take out the Int and the String to feed the next<br />
function application and the output generation.<br />
<br />
The same to insert something in our monad: if we want to create a pair<br />
of an Int and a String, pair of type Eval_IO, we now have to ''pack''<br />
the together by using our type constructor, feeding it with pair<br />
composed by and Int and a String. This is what we do with the "return"<br />
method of out monad and with "print_IO" function, where:<br />
* return insert into the monad an Int;<br />
* print_IO insert into the monad a String.<br />
<br />
Notice that "combineM" disappeared. This is because it comes for free<br />
by just defining our type Eval_IO as an instance of the Monad class.<br />
<br />
Indeed, if we look at the definition of the Monad class in the Prelude we read:<br />
<haskell><br />
class Monad m where<br />
return :: a -> m a<br />
(>>=) :: m a -> (a -> m b) -> m b<br />
(>>) :: m a -> m b -> m b<br />
fail :: String -> m a<br />
<br />
-- Minimal complete definition: (>>=), return<br />
p >> q = p >>= \ _ -> q<br />
fail s = error s<br />
</haskell><br />
<br />
You can see that the "combineM"" method (or (>>)) is automatically derived by<br />
the "bindMO" (or >>=) method:<br />
<br />
<haskell><br />
p >> q = p >>= \ _ -> q<br />
</haskell><br />
<br />
So, what the hell is the old <hask>type MO a = (a, Out)</hask> that<br />
did not required all this additional work (apart the need to<br />
specifically define (>>)? <br />
<br />
Thanks the help of some nice guy of the<br />
[http://www.haskell.org/mailman/listinfo/haskell-cafe haskell-cafe mailing list]<br />
(look at the thread started by <br />
[http://www.haskell.org/pipermail/haskell-cafe/2006-August/017634.html this silly question of mine])<br />
we can answer.<br />
<br />
Type MO is just a synonymous for (a,Out): the two can be substituted<br />
one for the other. That's it.<br />
<br />
We did not have to pack "a" and "Out" together with a type constructor<br />
to have a new type MO.<br />
<br />
As a consequence, we cannot use MO as an instance of Monad, and so, we<br />
cannot use with it the syntactic sugar we needed: the do-notation.<br />
<br />
That is to say: a type created with the "type" keyword cannot be an<br />
instance of a class, and cannot inherits its methods (in our case<br />
(>>=, >> and return). And without those methods the do-notation is not<br />
usable.<br />
<br />
Anyway we will better understand all the far reaching consequences of<br />
this new approach later on.<br />
<br />
==Errare Monadicum Est==<br />
<br />
Now that we have a basic understanding of what a monad is, and does,<br />
we will further explore it by making some changes to our evaluator.<br />
<br />
In this section we will se how to handle exceptions in our monadic<br />
evaluator.<br />
<br />
Suppose that we want to stop the execution of our monad if some<br />
conditions occurs. If our evaluator was to compute divisions, instead<br />
of sums, then we would like to stop the evaluator when a division by<br />
zero occurs, possibly producing some output, instead of the result of<br />
the evaluation of the expression, that explains what happened.<br />
<br />
Basic error handling.<br />
<br />
We will do so starting from the beginning once again...<br />
<br />
===The basic evaluator, non monadic, with exception===<br />
<br />
We just take our basic evaluator, without any output, and write a<br />
method to stop execution if a condition occurs: <br />
<br />
<haskell><br />
<br />
> data M a = Raise Exception<br />
> | Return a<br />
> deriving (Show)<br />
> type Exception = String<br />
<br />
</haskell><br />
<br />
Now, our monad is of datatype "M a" which can either be constructed<br />
with the "Raise" constructor, that takes a String (Exception is a<br />
synonymous of String), or by the "Return" constructor, that takes a<br />
variable type ("a"), an Int in our case.<br />
<br />
<haskell><br />
<br />
> evalE :: Term -> M Int<br />
> evalE (Con a) = Return a<br />
<br />
</haskell><br />
<br />
If evalE matches a Con it will construct a type Return with, inside, the content of the Con.<br />
<br />
<haskell><br />
<br />
> evalE (Add a b) = <br />
> case evalE a of<br />
> Raise e -> Raise e<br />
> Return a -><br />
> case evalE b of <br />
> Raise e -> Raise e<br />
> Return b -><br />
> if (a+b) == 42<br />
> then Raise "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else Return (a+b)<br />
<br />
</haskell><br />
<br />
If evalE matches an Add it will check if evaluating the first part<br />
produces a "Raise" or a "Return": in the first case it will return a<br />
"Raise" whose content is the same received. <br />
<br />
If instead the evaluation produces a value of a type matched by<br />
"Return", the evaluator will evaluate the second term of Add.<br />
<br />
If this returns a "Raise", a "Raise" will be returned all the way up<br />
the recursion, otherwise the evaluator will check whether a condition<br />
for raising a "Raise" exists. If not, it will return a "Return" with<br />
the sum inside.<br />
<br />
Test it with:<br />
<br />
evalE (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2)))<br />
<br />
<br />
===The basic evaluator, monadic, with exceptions===<br />
<br />
In order to produce a monadic version of the previous evaluator, the<br />
one that raises exceptions, we just need to abstract out from the<br />
evaluator all that case analysis.<br />
<br />
<haskell><br />
<br />
> data M1 a = Except Exception<br />
> | Ok {showM :: a }<br />
> deriving (Show)<br />
<br />
</haskell><br />
<br />
The data type didn't change at all. Well, we changed the name of the<br />
Return type constructor (now Ok) so that this constructor can coexist<br />
with the previous one in the same Literate Haskell file.<br />
<br />
<div id="MonadicEvalE"><br />
<haskell><br />
<br />
> instance Monad M1 where<br />
> return a = Ok a<br />
> m >>= f = case m of<br />
> Except e -> Except e<br />
> Ok a -> f a<br />
<br />
</haskell><br />
<br />
Binding operations are now very easy. Basically we check:<br />
* if the result of the evaluation of "m" produces an exception (first match: Except e ->...), in which case we return its content by constructing our M1 Int with the "Raise" constructor".<br />
* if the result of the evaluation of "m" is matched with the "Ok" constructor, we get its content and use it to bind the argument of "f" to its value.<br />
<br />
<hask>return a</hask> will just use the Ok type constructor for<br />
inserting "a" (in our case an Int) into M1 Int, the type of our monad.<br />
<br />
<haskell><br />
<br />
> raise :: Exception -> M1 a<br />
> raise e = Except e<br />
<br />
</haskell><br />
<br />
This is just a helper function to construct our "M1 a" type with the<br />
Raise constructor. It takes a string and returns a type (M1 a) to be<br />
matched with the "Raise" constructor.<br />
<br />
<haskell><br />
<br />
> eval_ME :: Term -> M1 Int<br />
> eval_ME (Con a) = do return a<br />
> eval_ME (Add t u) = do a <- eval_ME t<br />
> b <- eval_ME u<br />
> if (a+b) == 42<br />
> then raise "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else return (a + b)<br />
<br />
</haskell><br />
<br />
The evaluator itself is very simple. We bind "a" with the result of<br />
"eval_ME t", "b" with the result of "eval_ME u", and we check for a<br />
condition: <br />
* if the condition is met we raise an exception, that is to say: we return a value constructed with the "Raise" constructor. This value will be matched by ">>=" in the next recursion. And >>= will just return it all the way up the recursion.<br />
* if the condition is not me, we return a value constructed with the "Return" type constructor and go on with the recursion...<br />
<br />
Run with:<br />
<br />
eval_ME (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2)))<br />
<br />
It is noteworthy the fact that in our datatype definition we used a<br />
label field with a label selector (we called it showM), even though it<br />
was not used in our code. We will use this methodology later on.<br />
<br />
So, just to refresh your memory:<br />
<br />
<haskell><br />
<br />
> data Person = Person {name :: String,<br />
> age :: Int,<br />
> hobby :: String<br />
> } deriving (Show)<br />
<br />
> andreaRossato = Person "Andrea" 37 "Haskell The Monadic Way"<br />
> personName (Person a b c) = a<br />
<br />
</haskell><br />
<br />
will produce:<br />
*TheMonadicWay> andreaRossato<br />
Person {name = "Andrea", age = 37, hobby = "Haskell The Monadic Way"}<br />
*TheMonadicWay> personName andreaRossato<br />
"Andrea"<br />
*TheMonadicWay> name andreaRossato<br />
"Andrea"<br />
*TheMonadicWay> age andreaRossato<br />
37<br />
*TheMonadicWay> hobby andreaRossato<br />
"Haskell The Monadic Way"<br />
*TheMonadicWay> <br />
<br />
<br />
===Monadic evaluator with output and exceptions===<br />
<br />
We will now try to combine the [[The Monadic Way Part I#MonadicEvalIO|output-producing monadic evaluator]] <br />
with [[The Monadic Way Part I#MonadicEvalE| exception producing one]].<br />
<br />
<br />
<haskell><br />
<br />
> data M2 a = Ex Exception<br />
> | Done {unpack :: (a,O) }<br />
> deriving (Show)<br />
<br />
</haskell><br />
<br />
Now we need a datatype with two constructor: one to produce a value<br />
type "M2 a" using "Ex String" and one for value type "M2 a" (Int in<br />
this case) using "Done a".<br />
<br />
'''Note''' that we changed the name of the exception type constructor<br />
from "Raise" to "Ex" just to make the two coexist in the same Literate<br />
Haskell file.<br />
<br />
The constructor "Done a" is defined with a label sector: <hask>Done {unpack :: (a,O)}</hask> <br />
is equivalent to <hask>Done (a,O)</hask>. <br />
<br />
The only difference is that, this way, we are also defining a method<br />
to retrieve the pair (a,O) (in our case "O" is a synonymous for<br />
String, whereas "a" is a variable type) from an object of type "Done<br />
a".<br />
<br />
<haskell><br />
<br />
> instance Monad M2 where<br />
> return a = Done (a, "")<br />
> m >>= f = case m of<br />
> Ex e -> Ex e<br />
> Done (a, x) -> case (f a) of<br />
> Ex e1 -> Ex e1<br />
> Done (b, y) -> Done (b, x ++ y)<br />
<br />
</haskell><br />
<br />
Now our binding operations gets more complicated by the fact that we<br />
have to concatenate the output, as we did before, '''and''' check for<br />
exceptions.<br />
<br />
It is not possible to do has we did in the [[The Monadic Way Part I#MonadicEvalE| exception producing evaluator]], <br />
where we could check just for "m" (remember the "m" in the first run<br />
stands for "eval t").<br />
<br />
Since at the end we must return the output produced by the evaluation<br />
of "m" ''concatenated'' with the output produced by the evaluation of<br />
"f a" (where "a" is returned by "m", paired with "x" by "Done"), now<br />
we must check if we '''do have''' an output from "f a" produced by<br />
"Done".<br />
<br />
Indeed, now, "f a" can also produce a value constructed by "Ex", and<br />
this value does not contain the pair as the value produced by "Done".<br />
<br />
So, we evaluate "m": <br />
* if we match a value produced by type constructor "Ex" we return a value produced by type constructor "Ex" whose content is the one we extracted in the matching;<br />
* if we match a value produced by "Done" we match the pair it carries "(a,x)" and we analyze what "f a" returns:<br />
** if "f a" returns a value produced by "Ex" we extract the exception and we return it, constructing a value with "Ex"<br />
** if "f a" returns a value produced by "Done" we return "b" and the concatenated "x" and "y". <br />
<br />
And now the evaluator:<br />
<br />
<haskell><br />
<br />
> raise_IOE :: Exception -> M2 a<br />
> raise_IOE e = Ex e<br />
<br />
</haskell><br />
<br />
This is the function to insert in our monad (M2) an exception: we take<br />
a String and produce a value applying the type constructor for<br />
exception "Ex" to its value.<br />
<br />
<haskell><br />
<br />
> print_IOE :: O -> M2 ()<br />
> print_IOE x = Done ((), x)<br />
<br />
</haskell><br />
<br />
The function to produce output is the very same of the one of the [[The Monadic Way Part I#MonadicEvalIO|output-producing monadic evaluator]].<br />
<br />
<haskell><br />
<br />
> eval_IOE :: Term -> M2 Int<br />
> eval_IOE (Con a) = do print_IOE (formatLine (Con a) a)<br />
> return a<br />
> eval_IOE (Add t u) = do a <- eval_IOE t<br />
> b <- eval_IOE u<br />
> let out = formatLine (Add t u) (a + b)<br />
> print_IOE out<br />
> if (a+b) == 42<br />
> then raise_IOE $ out ++ "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else return (a + b)<br />
<br />
</haskell><br />
<br />
The evaluator procedure did not change very much from the one of the [[The Monadic Way Part I#MonadicEvalIO|output-producing monadic evaluator]].<br />
<br />
We just added the case analysis to see if the condition for raising an exception is met.<br />
<br />
Running with<br />
<br />
eval_IOE (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2)))<br />
<br />
will produce <br />
<br />
Ex "eval (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2))) <= 42 - <br />
The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
<br />
Look at the <hask>let</hask> clause within the do-notation. We do not<br />
need to use the "let ... in" construction: since all bound variables<br />
remain bound within a <hask>do</hask> procedure (see [[The Monadic Way Part I#Bind|here]]), <br />
we do not need the "in" to specify "where" the variable "out" will be bound in!<br />
<br />
==We Need A State==<br />
<br />
We will keep on adding complexity to our monadic evaluator and this<br />
time we will add a counter. We just want to count the number of<br />
iterations (the number of times "eval" will be called) needed to<br />
evaluate the expression.<br />
<br />
===The basic evaluator, non monadic, with a counter===<br />
<br />
As before we will start by adding this feature to our [[The Monadic Way Part I#BasicEval|basic evaluator]]. <br />
<br />
A method to count the number of iterations, since the lack of<br />
assignment and destructive updates (such as for i=0;i<10;i++;),<br />
is to add an argument to our function, the initial state, number that<br />
in each call of the function will be increased and passed to the next<br />
function call.<br />
<br />
And so, very simply:<br />
<br />
<haskell><br />
<br />
> -- non monadic<br />
> type St a = State -> (a, State)<br />
> type State = Int<br />
> evalNMS :: Term ->St Int<br />
> evalNMS (Con a) x = (a, x + 1)<br />
> evalNMS (Add t u) x = let (a, y) = evalNMS t x in<br />
> let (b, z) = evalNMS u y in<br />
> (a + b, z +1)<br />
<br />
</haskell><br />
<br />
Now evalNMS takes two arguments: the expression of type Term and an<br />
State (which is a synonymous for Int), and will produce a pair<br />
(a,State), that is to say a pair with a variable type "a" and an Int.<br />
<br />
The operations in the evaluator are very similar to the non monadic [[The Monadic Way Part I#BasivEvalO|output producing evaluator]].<br />
<br />
We are now using the "let ... in" clause, instead of the "where", and we are increasing the counter "z" the comes from the evaluation of the second term, but the basic operation are the same:<br />
* we evaluate "evalNMS t x" where "x" is the initial state, and we match and bind the result in "let (a, y) ... in"<br />
* we evaluate "evalNMS u y", where "y" was bound to the value returned by the previous evaluation, and we match and bind the result in "let (b, z) ... in"<br />
* we return a pair formed by the sum of the result (a+b) and the state z increased by 1. <br />
<br />
Let's try it:<br />
<br />
*TheMonadicWay> evalNMS (Add (Con 10) (Add (Add (Con 20) (Con 10)) (Con 2))) 0<br />
(42,7)<br />
*TheMonadicWay> <br />
<br />
As you see we must pass to "evalNMS"the initial state of our counter: 0.<br />
<br />
Look at the type signature of the function "evalNMS":<br />
<haskell><br />
evalNMS :: Term ->St Int<br />
</haskell><br />
<br />
From this signature you could argue that our function takes only<br />
'''one''' argument. But since our type St is defined with the "type"<br />
keyword, St can be substituted with what comes after the "=" sign. So,<br />
the real type signature of our function is:<br />
<br />
<haskell><br />
evalNMS :: Term -> State -> (Int,State)<br />
</haskell><br />
<br />
<div if="typeNewtype"><br />
Just to refresh your memory:<br />
<br />
<haskell><br />
<br />
> type IamAfunction a = (a -> a)<br />
> newtype IamNotAfunction a = NF (a -> a)<br />
> newtype IamNotAfunctionButYouCanUnPackAndRunMe a = F { unpackAndRun :: (a -> a) }<br />
<br />
> a = \x -> x * x<br />
<br />
> a1 :: IamAfunction Int<br />
> a1 = a<br />
<br />
> a2 :: IamNotAfunction Int<br />
> a2 = NF a<br />
<br />
> a3 :: IamNotAfunctionButYouCanUnPackAndRunMe Int<br />
> a3 = F a<br />
<br />
</haskell><br />
<br />
<br />
*TheMonadicWay> a 4<br />
16<br />
*TheMonadicWay> a1 4<br />
16<br />
*TheMonadicWay> a2 4<br />
<br />
<interactive>:1:0:<br />
The function `a2' is applied to one arguments,<br />
but its type `IamNotAfunction Int' has only 0<br />
In the definition of `it': it = a2 4<br />
*TheMonadicWay> a3 4<br />
<br />
<interactive>:1:0:<br />
The function `a3' is applied to one arguments,<br />
but its type `IamNotAfunctionButYouCanUnPackAndRunMe Int' has only 0<br />
In the definition of `it': it = a3 4<br />
*TheMonadicWay> unpackAndRun a3 4<br />
16<br />
*TheMonadicWay><br />
<br />
This means that "a1" is a partial application hidden by a type<br />
synonymous. <br />
<br />
"a2" and "a3" are not function types. They are types that<br />
have a functional value. <br />
<br />
Moreover, since we defined the type constructor of type<br />
"IamNotAfunctionButYouCanUnPackAndRunMe", F, with a label field, in<br />
that label field we defined a method (a label selector) to "extract"<br />
the function from the type "IamNotAfunctionButYouCanUnPackAndRunMe",<br />
and run it:<br />
<br />
<haskell><br />
unpackAndRun a3 4<br />
</haskell><br />
<br />
And what about "a2"? Is it lost forever?<br />
<br />
Obviously not! We need to write a function that unpacks a type<br />
"IamNotAfunction", using its type constructor NF to match the internal<br />
function:<br />
<br />
<haskell><br />
<br />
> unpackNF :: IamNotAfunction a -> a -> a<br />
> unpackNF (NF f) = f<br />
<br />
</haskell><br />
<br />
and run:<br />
<br />
*TheMonadicWay> unpackNF a2 4<br />
16<br />
*TheMonadicWay> <br />
<br />
As you see, "unpackNF" definition is a partial application: we specify<br />
one argument to get a function that gets another argument.<br />
<br />
A label selector does the same thing.<br />
<br />
Later we will see the importance of this distinction, quite obvious<br />
for haskell gurus, but not for us. Till now.<br />
<br />
===The evaluator, monadic, with a counter, without do-notation===<br />
<br />
<br />
'''(Text to be done yet: just a summary)'''<br />
<br />
The moadic version without do notation.<br />
<br />
<haskell><br />
<br />
> -- monadic<br />
> type MS a = State -> (a, State)<br />
<br />
> mkMS :: a -> MS a<br />
> mkMS a = \x -> (a, x)<br />
<br />
> bindMS :: MS a -> (a -> MS b) -> MS b<br />
> bindMS m f = \x -> <br />
> let (a, y) = m x in<br />
> let (b, z) = f a y in<br />
> (b, z)<br />
<br />
> combineMS :: MS a -> MS b -> MS b<br />
> combineMS m f = m `bindMS` \_ -> f<br />
<br />
> incState :: MS ()<br />
> incState = \s -> ((), s + 1)<br />
<br />
> evalMS :: Term -> MS Int<br />
> evalMS (Con a) = incState `combineMS` mkMS a<br />
> evalMS (Add t u) = evalMS t `bindMS` \a -><br />
> evalMS u `bindMS` \b -><br />
> incState `combineMS` mkMS (a + b)<br />
<br />
> --evalMS (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) 0<br />
<br />
</haskell><br />
<br />
===The evaluator, monadic, with counter and output, without do-notation===<br />
<br />
Now we'll add Output to the stateful evaluator:<br />
<br />
<haskell><br />
<br />
> -- state and output<br />
<br />
> type MSO a = State -> (a, State, Output)<br />
<br />
> mkMSO :: a -> MSO a<br />
> mkMSO a = \s -> (a, s, "")<br />
<br />
> bindMSO :: MSO a -> (a -> MSO b) -> MSO b<br />
> bindMSO m f = \x -> <br />
> let (a, y, s1) = m x in<br />
> let (b, z, s2) = f a y in<br />
> (b, z, s1 ++ s2)<br />
<br />
> combineMSO :: MSO a -> MSO b -> MSO b<br />
> combineMSO m f = m `bindMSO` \_ -> f<br />
<br />
> incMSOstate :: MSO ()<br />
> incMSOstate = \s -> ((), s + 1, "")<br />
<br />
> outMSO :: Output -> MSO ()<br />
> outMSO = \x s -> ((),s, x)<br />
<br />
> evalMSO :: Term -> MSO Int<br />
> evalMSO (Con a) = incMSOstate `combineMSO` <br />
> outMSO (formatLine (Con a) a) `combineMSO` <br />
> mkMSO a<br />
> evalMSO (Add t u) = evalMSO t `bindMSO` \a -><br />
> evalMSO u `bindMSO` \b -><br />
> incMSOstate `combineMSO` <br />
> outMSO (formatLine (Add t u) (a + b)) `combineMSO`<br />
> mkMSO (a + b)<br />
<br />
> --evalMSO (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12)))) 0<br />
<br />
</haskell><br />
<br />
===The monadic evaluator with output and counter in do-notation=== <br />
<br />
State, Output in do-notation. Look at how much the complexity of our<br />
(>>=) founction is increasing:<br />
<br />
<haskell><br />
<br />
> -- thanks to Brian Hulley<br />
<br />
> newtype MSIO a = MSIO (State -> (a, State, Output))<br />
> instance Monad MSIO where<br />
> return a = MSIO (\s -> (a, s, ""))<br />
> (MSIO m) >>= f = MSIO $ \x -><br />
> let (a, y, s1) = m x in<br />
> let MSIO runNextStep = f a in<br />
> let (b, z, s2) = runNextStep y in<br />
> (b, z, s1 ++ s2)<br />
<br />
<br />
> incMSOIstate :: MSIO ()<br />
> incMSOIstate = MSIO (\s -> ((), s + 1, ""))<br />
<br />
> print_MSOI :: Output -> MSIO ()<br />
> print_MSOI x = MSIO (\s -> ((),s, x))<br />
<br />
> eval_MSOI :: Term -> MSIO Int<br />
> eval_MSOI (Con a) = do incMSOIstate<br />
> print_MSOI (formatLine (Con a) a)<br />
> return a<br />
<br />
> eval_MSOI (Add t u) = do a <- eval_MSOI t<br />
> b <- eval_MSOI u<br />
> incMSOIstate<br />
> print_MSOI (formatLine (Add t u) (a + b))<br />
> return (a + b)<br />
<br />
> run_MSOI :: MSIO a -> State -> (a, State, Output)<br />
> run_MSOI (MSIO f) s = f s<br />
<br />
> --run_MSOI (eval_MSOI (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12))))) 0<br />
<br />
</haskell><br />
<br />
===Another version of the monadic evaluator with output and counter, in do-notation===<br />
<br />
This is e second version that exploit label fields in datatype to<br />
decrease the complexity of the binding operations.<br />
<br />
<haskell><br />
<br />
> -- Thanks Udo Stenzel<br />
<br />
> newtype Eval_SIO a = Eval_SIO { unPackMSIOandRun :: State -> (a, State, Output) }<br />
> instance Monad Eval_SIO where<br />
> return a = Eval_SIO (\s -> (a, s, ""))<br />
> (>>=) m f = Eval_SIO (\x -><br />
> let (a, y, s1) = unPackMSIOandRun m x in<br />
> let (b, z, s2) = unPackMSIOandRun (f a) y in<br />
> (b, z, s1 ++ s2))<br />
<br />
> incSIOstate :: Eval_SIO ()<br />
> incSIOstate = Eval_SIO (\s -> ((), s + 1, ""))<br />
<br />
> print_SIO :: Output -> Eval_SIO ()<br />
> print_SIO x = Eval_SIO (\s -> ((),s, x))<br />
<br />
> eval_SIO :: Term -> Eval_SIO Int<br />
> eval_SIO (Con a) = do incSIOstate<br />
> print_SIO (formatLine (Con a) a)<br />
> return a<br />
> eval_SIO (Add t u) = do a <- eval_SIO t<br />
> b <- eval_SIO u<br />
> incSIOstate<br />
> print_SIO (formatLine (Add t u) (a + b))<br />
> return (a + b)<br />
<br />
> --unPackMSIOandRun (eval_SIO (Add (Con 6) (Add (Con 16) (Add (Con 20) (Con 12))))) 0<br />
<br />
</haskell><br />
<br />
<br />
==If There's A State We Need Some Discipline: Dealing With Complexity==<br />
<br />
In order to increase the complexity of our monad now we will try to<br />
mix State (counter), Exceptions and Output.<br />
<br />
This is an email [http://www.haskell.org/pipermail/haskell-cafe/2006-August/017672.html I send to the haskell-cafe mailing list]:<br />
<br />
<pre><br />
Now I'm trying to create a statefull evaluator, with output and<br />
exception, but I'm facing a problem I seem not to be able to<br />
conceptually solve.<br />
<br />
Take the code below.<br />
Now, in order to get it run (and try to debug) the Eval_SOI type has a<br />
Raise constructor that produces the same type of SOIE. Suppose instead it<br />
should be constructing something like Raise "something". <br />
Moreover, I wrote a second version of >>=, commented out.<br />
This is just to help me illustrate to problem I'm facing.<br />
<br />
Now, >>= is suppose to return Raise if "m" is matched against Raise<br />
(second version commented out).<br />
If "m" matches SOIE it must return a SOIE only if "f a" does not<br />
returns a Raise (output must be concatenated).<br />
<br />
I seem not to be able to find a way out. Moreover, I cannot understand<br />
if a way out can be possibly found. Something suggests me it could be<br />
related to that Raise "something".<br />
But my feeling is that functional programming could be something out<br />
of the reach of my mind... by the way, I teach Law, so perhaps you'll<br />
forgive me...;-)<br />
<br />
If you can help me to understand this problem all I can promise is<br />
that I'll mention your help in the tutorial I'm trying to write on<br />
"the monadic way"... that seems to lead me nowhere.<br />
<br />
Thanks for your kind attention.<br />
<br />
Andrea<br />
</pre><br />
<br />
This was the code:<br />
<br />
<haskell><br />
data Eval_SOI a = Raise { unPackMSOIandRun :: State -> (a, State, Output) }<br />
| SOIE { unPackMSOIandRun :: State -> (a, State, Output) }<br />
<br />
instance Monad Eval_SOI where<br />
return a = SOIE (\s -> (a, s, ""))<br />
m >>= f = SOIE (\x -><br />
let (a, y, s1) = unPackMSOIandRun m x in<br />
case f a of<br />
SOIE nextRun -> let (b, z, s2) = nextRun y in <br />
(b, z, s1 ++ s2)<br />
Raise e1 -> e1 y --only this happens<br />
<br />
)<br />
-- (>>=) m f = case m of<br />
-- Raise e -> error "ciao" -- why this is not going to happen?<br />
-- SOIE a -> SOIE (\x -><br />
-- let (a, y, s1) = unPackMSOIandRun m x in<br />
-- let (b, z, s2) = unPackMSOIandRun (f a) y in <br />
-- (b, z, s1 ++ s2)) <br />
<br />
<br />
incSOIstate :: Eval_SOI ()<br />
incSOIstate = SOIE (\s -> ((), s + 1, ""))<br />
<br />
print_SOI :: Output -> Eval_SOI ()<br />
print_SOI x = SOIE (\s -> ((),s, x))<br />
<br />
raise x e = Raise (\s -> (x,s,e))<br />
<br />
eval_SOI :: Term -> Eval_SOI Int<br />
eval_SOI (Con a) = do incSOIstate<br />
print_SOI (formatLine (Con a) a)<br />
return a<br />
eval_SOI (Add t u) = do a <- eval_SOI t<br />
b <- eval_SOI u<br />
incSOIstate<br />
print_SOI (formatLine (Add t u) (a + b))<br />
if (a + b) == 42 <br />
then raise (a+b) " = The Ultimate Answer!!"<br />
else return (a + b)<br />
<br />
runEval exp = case eval_SOI exp of<br />
Raise a -> a 0<br />
SOIE p -> let (result, state, output) = p 0 in<br />
(result,state,output)<br />
<br />
<br />
<br />
--runEval (Add (Con 10) (Add (Con 28) (Add (Con 40) (Con 2))))<br />
</haskell><br />
<br />
This code will produce <br />
<br />
eval (Con 10) <= 10 -<br />
eval (Con 28) <= 28 -<br />
eval (Con 40) <= 40 -<br />
eval (Con 2) <= 2 - = The Ultimate Answer!!<br />
eval (Add (Con 28) (Add (Con 40) (Con 2))) <= 70 -<br />
eval (Add (Con 10) (Add (Con 28) (Add (Con 40) (Con 2)))) <= 80 -<br />
<br />
The exception appears in the output, but executioon is not stopped.<br />
<br />
===Monadic evaluator with output, counter and exception, in do-notation===<br />
<br />
Brian Hulley [http://www.haskell.org/pipermail/haskell-cafe/2006-August/017680.html came up with this solution]:<br />
<br />
<haskell><br />
<br />
> -- thanks to Brian Hulley<br />
> data Result a<br />
> = Good a State Output<br />
> | Bad State Output Exception<br />
> deriving Show<br />
<br />
> newtype Eval_SIOE a = SIOE {runSIOE :: State -> Result a}<br />
<br />
> instance Monad Eval_SIOE where<br />
> return a = SIOE (\s -> Good a s "")<br />
> m >>= f = SIOE $ \x -><br />
> case runSIOE m x of<br />
> Good a y o1 -><br />
> case runSIOE (f a) y of<br />
> Good b z o2 -> Good b z (o1 ++ o2)<br />
> Bad z o2 e -> Bad z (o1 ++ o2) e<br />
> Bad z o2 e -> Bad z o2 e<br />
<br />
> raise_SIOE e = SIOE (\s -> Bad s "" e)<br />
<br />
> incSIOEstate :: Eval_SIOE ()<br />
> incSIOEstate = SIOE (\s -> Good () (s + 1) "")<br />
<br />
> print_SIOE :: Output -> Eval_SIOE ()<br />
> print_SIOE x = SIOE (\s -> Good () s x)<br />
<br />
<br />
> eval_SIOE :: Term -> Eval_SIOE Int<br />
> eval_SIOE (Con a) = do incSIOEstate<br />
> print_SIOE (formatLine (Con a) a)<br />
> return a<br />
> eval_SIOE (Add t u) = do a <- eval_SIOE t<br />
> b <- eval_SIOE u<br />
> incSIOEstate<br />
> let out = formatLine (Add t u) (a + b)<br />
> print_SIOE out<br />
> if (a+b) == 42<br />
> then raise_SIOE $ out ++ "The Ultimate Answer Has Been Computed!! Now I'm tired!"<br />
> else return (a + b)<br />
<br />
> runEval exp = case runSIOE (eval_SIOE exp) 0 of<br />
> Bad s o e -> "Error at iteration n. " ++ show s ++ <br />
> " - Output stack = " ++ o ++ <br />
> " - Exception = " ++ e<br />
> Good a s o -> "Result = " ++ show a ++ <br />
> " - Iterations = " ++ show s ++ " - Output = " ++ o<br />
<br />
</haskell><br />
<br />
Run with runEval (Add (Con 18) (Add (Con 12) (Add (Con 10) (Con 2))))<br />
<br />
==Suggested Readings==<br />
<br />
Cale Gibbard, [http://haskell.org/haskellwiki/Monads_as_Containers Monads as Containers]<br />
<br />
Jeff Newbern, [http://www.nomaware.com/monads/html/index.html All About Monads]<br />
<br />
[http://haskell.org/haskellwiki/IO_inside IO Inside]<br />
<br />
[http://sigfpe.blogspot.com/2006/08/you-could-have-invented-monads-and.html You Could Have Invented Monads! (And Maybe You Already Have.) by sigfpe]<br />
<br />
<br />
==Acknowledgments==<br />
<br />
Thanks to Neil Mitchell, Daniel Fisher, Bulat Ziganzhin, Brian Hulley<br />
and Udo Stenzel for the invaluable help they gave, in the<br />
[http://www.haskell.org/mailman/listinfo/haskell-cafe haskell-cafe mailing list], <br />
in understanding this topic.<br />
<br />
I couldn't do it without their help.<br />
<br />
Obviously errors are totally mine. But this is a wiki so, please,<br />
correct them!<br />
<br />
- [[User:AndreaRossato|Andrea Rossato]]</div>AndreaRossatohttps://wiki.haskell.org/index.php?title=The_Monadic_Way&diff=5849The Monadic Way2006-09-06T13:51:30Z<p>AndreaRossato: an example to explain the tutorial approach</p>
<hr />
<div>'''Note''' Since the size of the previous file was getting too big for a wiki, the tutorial has been divided into two parts: [[The Monadic Way Part I]] and [[The Monadic Way Part II]]. See below for some introductory remarks.<br />
<br />
'''Contents'''<br />
<br />
;[[Meet Bob The Monadic Lover]]<br />
:A (supposed-to-be) funny and short introduction to Monads, with code but without any reference to category theory: what monads look like and what they are useful for, from the perspective of a ... lover. It could be an introduction to "The Monadic Way" tutorial.<br />
<br />
;[[The Monadic Way Part I]]<br />
:In the first part of the tutorial we will start from a very simple evaluator that will be transformed into a monadic evaluator with an increasing number of features: output, exceptions, and state: a very simple counter for tracking the number of recursions of the evaluation precess.<br />
<br />
;[[The Monadic Way Part II]]<br />
:In the second part of the tutorial we will see how to take complexity out of our monadic evaluator with the use of monadic transformers, and specifically StateT. This part is just a skeleton, since, for the time being, it contains only the code.<br />
<br />
<br />
==Preliminary Remarks==<br />
<br />
When I started writing this tutorial I though that the only way to<br />
explain monads to a newcomer was to show them from the inside, with<br />
the use of lambda abstraction. Not only because this is the way Philip<br />
Wedler's<br />
[http://homepages.inf.ed.ac.uk/wadler/papers/marktoberdorf/baastad.pdf paper] <br />
adopts, but also because I believed, and still believe, that the<br />
only way to understand what bind (>>=) does is to explain it as a<br />
function that takes a monad and an anonymous function.<br />
<br />
I had this feeling because I am a newcomer, and this is the way I came to understand monads.<br />
<br />
I did not received very much feedback for this tutorial, and I must<br />
admit that I would like to. But one person, on the haskell-cafe<br />
mailing list, [http://www.haskell.org/pipermail/haskell-cafe/2006-September/017740.html told me]<br />
that:<br />
<pre><br />
imho your tutorial makes the error that is a very typical: when you<br />
write your tutorial you already know what are monads and what the<br />
program you will construct at the end. but your reader don't know all these!<br />
for such fresh reader this looks as you made some strange steps, write<br />
some ugly code and he doesn't have chances to understand that this ugly<br />
code is written just to show that this can be simplified by using monads.<br />
</pre><br />
<br />
I believe that Bulat is right. In this tutorial you go through some<br />
code that is '''really''' ugly and then, with some kind of magic, it<br />
turns out in the (redundant but) clean evaluator of the end of [[The Monadic Way Part II]].<br />
<br />
I took that mail as a challenge and <br />
[http://www.haskell.org/pipermail/haskell-cafe/2006-September/017778.html I responded] <br />
by writing [[Meet Bob The Monadic Lover]].<br />
<br />
In "Meet Bob" the code is clean, variable names are very descriptive,<br />
and you see that I can create a monad without any use of lambda<br />
abstraction.<br />
<br />
Bind (in "Meet Bob" is askLover) now takes a monad and a partial<br />
application, not an anonymous function.<br />
<br />
Obviously you can see an anonymous function as a partial application.<br />
<br />
The problem, I think, is that, in "Meet Bob", you cannot understand<br />
the strict relation between what I did before and what I do when I<br />
start using the "do-notation". You see that the same functions are<br />
being used ("tellMyself" and "newLove"), but "andrea <- tellMyself 1"<br />
is not explained. It's just magic.<br />
<br />
The fact is that you cannot understand "andrea <- tellMyself 1"<br />
without the use of lambda abstraction.<br />
<br />
I should have written an intermediate step, something like this:<br />
<haskell><br />
drunk = do newLove "Paula " >><br />
(tellLover 1 10) >>= \paula -><br />
(tellMyself 3) >>= \lorena -> tellMyself (paula + lorena)<br />
<br />
</haskell><br />
<br />
With this approach I think you can understand '''why and how''' you<br />
come to write something like this:<br />
<haskell><br />
drunk = do newLove "Paula "<br />
paula <- (tellLover 1 10)<br />
lorena <- tellMyself 3<br />
tellMyself (paula + lorena)<br />
</haskell><br />
<br />
That is to say, in this way you can see a do block as a series of<br />
nested anonymous functions whose arguments are bound to some value by<br />
the application of >>=. Anonymous functions that bind to some value<br />
the variables appearing after the "->" ("paula" and "lorena").<br />
<br />
To summarize, I think that even if you can start using monads without<br />
understanding that what happens inside a "do" block is strictly<br />
related with lambda calculus, I don't think you can claim you<br />
understand monads just because you are using them. <br />
<br />
And I'm quite sure that if a problem arises somewhere you can have a<br />
very difficult time in trying to find out what the problem is. This is<br />
even more true when you start doing monadic transformations.<br />
<br />
==How to Explain Monads to Newcomers?==<br />
<br />
Monads are not an impossible-to-understand-unless-you-have-a-phd<br />
topic. I don't know if I can claim I'm a living proof of this<br />
assumption, since I have a PhD. But please take into account that I<br />
have a PhD in Comparative Private Law! Nothing related to computer<br />
science. And I claim I understand monads.<br />
<br />
Moreover, since I have come to understand them, I think I can try to<br />
explain them to newcomers like me. This is why I started writing this<br />
tutorial.<br />
<br />
Still, in order to understand them I studied, and I studied hard. I<br />
wanted to understand, it was difficult, and I kept studying. Going<br />
back to the foundation when needed.<br />
<br />
So, I cannot explain monads to you unless you are willing to study. I can<br />
show you the way, but you must take your time and follow that way.<br />
<br />
==What Do I Need to Know to Understand Monads?==<br />
<br />
===Functional Programming===<br />
<br />
First you need at least a basic understanding of functional<br />
programming. <br />
<br />
This is going to be the easiest step, because there is an invaluable<br />
resource available on line for free.<br />
<br />
This is what I did: I spent 20 hours of my life watching the Video<br />
Lectures by Hal Abelson and Gerald Jay Sussman on<br />
[http://swiss.csail.mit.edu/classes/6.001/abelson-sussman-lectures/ Structure and Interpretation of Computer Programs].<br />
<br />
This course, and the annexed text book (I did not read it entirely),<br />
are an amazing introduction to computer science: you start by learning<br />
some basic Scheme and then Abelson and Sussman will bring you, step by<br />
step, to understand evaluation, interpretation and compilation of Lisp<br />
(Scheme) code.<br />
<br />
The course is clear, interesting, funny and will provide you with a<br />
basic, but strong, understanding of functional programming.<br />
<br />
Believe me: if you like programming but don't have a computer science<br />
curriculum, you'll find out that spending 20 hours of your life to<br />
watch that course has been the most productive investment of your<br />
learning life.<br />
<br />
===My Problem With Haskell===<br />
<br />
I find Haskell an amazingly expressive programming language. It makes<br />
it incredibly easy to perform very difficult tasks, just like monads,<br />
for instance.<br />
<br />
The problem is that, in doing so, it makes it difficult to understand<br />
Haskell to newcomers.<br />
<br />
I must confess that tutorials and other learning material are quite<br />
dissatisfying on this regards too.<br />
<br />
Since Haskell seems to make easy what is not easy, these tutorial take<br />
the "it's easy" approach.<br />
<br />