Difference between revisions of "PropLang"
Jump to navigation
Jump to search
NeilMitchell (talk | contribs) |
NeilMitchell (talk | contribs) |
||
Line 15: | Line 15: | ||
"Word count: " ++ show (length $ words x)) |
"Word count: " ++ show (length $ words x)) |
||
− | == |
+ | == Overview == |
+ | There are events, you can register to be notified when an event fires by doing |
||
− | This is the low level stuff, on which the library will be built |
||
+ | event += handler |
||
− | data Var a = ... |
||
− | data Notify = ... |
||
+ | There are variables, which have events which fire, and a value which you can set and get. |
||
− | get :: Var a -> IO a |
||
− | set :: Var a -> a -> IO () |
||
− | addNotify :: Var a -> (a -> IO ()) -> IO Notify |
||
− | remNotify :: Notify -> IO () |
||
+ | variable <- newVar "value" |
||
− | newVar :: a -> IO (Var a) |
||
+ | variable += handler |
||
+ | variable -< "value" |
||
+ | x <- getVal variable |
||
+ | There are objects, objects have properties. A property is just a variable. |
||
− | == A concrete implementation of Var == |
||
+ | window!text += handler |
||
− | One thing to avoid, global state that needs a specific init function |
||
+ | window!text -< "Titlebar text" |
||
+ | There are bindings which relate variables. |
||
− | data Var a = Var {value :: IORef a, |
||
− | notifyId :: IORef Integer, |
||
− | notifys :: IORef [(Integer, a -> IO ())], |
||
− | source :: IORef (Maybe Notify)} |
||
+ | <haskell> |
||
− | data Notify = Notify {notifyId :: Integer, notifys :: IORef [(Integer, a -> IO ())]} |
||
+ | -- the window text is the variable |
||
+ | window!text =<= variable |
||
+ | -- the window text is the variable, but upppercase |
||
+ | window!text =< with1 variable (map toUpper) |
||
+ | -- combine the two texts to create the window text |
||
+ | window!text =< with2 variable1 variable2 (++) |
||
+ | </haskell> |
||
+ | If any of the values on the RHS of a binding change, then the LHS is updated. |
||
− | newVar :: a -> IO (Var a) |
||
− | newVar x = do v <- newIORef x |
||
− | n <- newIORef [] |
||
− | c <- newIORef 0 |
||
− | a <- newIORef Nothing |
||
− | return $ Var v c n a |
||
− | |||
− | get :: Var a -> IO a |
||
− | get var = readIORef (value var) |
||
− | |||
− | set :: Var a -> a -> IO () |
||
− | set var x = do n <- readIORef (notifys var) |
||
− | writeIORef (value var) x |
||
− | mapM_ (\(a,b) -> b x) n |
||
− | |||
− | addNotify :: Var a -> (a -> IO ()) -> IO Notify |
||
− | addNotify var f = do n <- readIORef (notifys var) |
||
− | c <- readIORef (notifyId var) |
||
− | writeIORef (notifyId var) (c+1) |
||
− | writeIORef (notifys var) ((c, f) : n) |
||
− | return $ Notify c (notifys var) |
||
− | |||
− | remNotify :: Notify -> IO () |
||
− | remNotify notify = do n <- readIORef (notifys notify) |
||
− | writeIORef (notifys notify) (filter (\x -> fst x /= notifyId notify) n) |
||
− | |||
− | == Object layering == |
||
− | |||
− | textBox!text -< "test" |
||
− | filename <- newVar Nothing |
||
− | addNotify filename hatCover |
||
− | lbl!text =< with filename $ \x -> |
||
− | case x of |
||
− | Nothing -> "Select a file" |
||
− | Just x -> "Loaded: " ++ x |
||
− | |||
− | |||
− | where |
||
− | |||
− | (!) :: GtkObject -> GtkProp -> Var a -- ignoring lots of details here |
||
− | (-<) :: Var a -> a -> IO () |
||
− | (=<) :: Var a -> Action a b -> IO () |
||
− | |||
− | with :: Var a -> (a -> b) -> Action a b |
||
− | |||
− | == Implementation of Object Layering == |
||
− | |||
− | data Action a b = Action (Var a) (a -> b) |
||
− | |||
− | with :: Var a -> (a -> b) -> Action a b |
||
− | with var f = Action var f |
||
− | |||
− | (-<) :: Var a -> a -> IO () |
||
− | (-<) var val = set var val |
||
− | |||
− | (=<) :: Var a -> Action a -> IO () |
||
− | (=<) dest (Act src f) = do srcOld <- readIORef (source dest) |
||
− | when (isJust srcOld) $ remNotify (fromJust srcOld) |
||
− | note <- addNotify src (\x -> set dest (f x)) |
||
− | writeIORef (source dest) (Just note) |
||
− | |||
− | == Implementation of Gtk Layering == |
||
− | |||
− | class TextProvider a where |
||
− | text :: a -> Var String |
||
− | |||
− | instance TextProvider TextView where |
||
− | text x = ... |
||
− | |||
− | txt :: TextView |
||
− | |||
− | (!) :: a -> (a -> b) -> b |
||
− | object ! prop = prop object |
||
− | |||
− | txt!text :: Var String |
Revision as of 08:37, 24 July 2006
A design for a GUI library which is more like Haskell and less like C. To be written over Gtk2Hs.
Link: http://www.cse.unsw.edu.au/~chak/haskell/ports/
Thoughts by
Neil Mitchell, Duncan Coutts
For a darcs repo see: http://www.cs.york.ac.uk/fp/darcs/proplang/
In particular check out Sample.hs, I am particularly proud of the word count:
sb!text =< with1 (txt!text) (\x -> "Word count: " ++ show (length $ words x))
Overview
There are events, you can register to be notified when an event fires by doing
event += handler
There are variables, which have events which fire, and a value which you can set and get.
variable <- newVar "value" variable += handler variable -< "value" x <- getVal variable
There are objects, objects have properties. A property is just a variable.
window!text += handler window!text -< "Titlebar text"
There are bindings which relate variables.
-- the window text is the variable
window!text =<= variable
-- the window text is the variable, but upppercase
window!text =< with1 variable (map toUpper)
-- combine the two texts to create the window text
window!text =< with2 variable1 variable2 (++)
If any of the values on the RHS of a binding change, then the LHS is updated.