packages feed

proplang-0.1: PropLang/Variable.hs

-----------------------------------------------------------------------------
-- |
-- Module      :  Variable.hs
-- Copyright   :  (c) Neil Mitchell 2007
-- License     :
--
-- Maintainer  :
-- Stability   :  unstable
-- Portability :  not portable
--
-- Defines PropLang variables with utility functions
--
-----------------------------------------------------------------------------

module PropLang.Variable(
    Var, newVar, newVarName, newVarWith, newVarWithName,
    getVar,
    injectWith,
    with, with1, with2, with3,
    (=<), (=$=), (=$$=),
    (-<), (-<-), (=<=), (=<>=),
    tie
    ) where

import PropLang.Event
import PropLang.Value
import Control.Monad
import Data.IORef

infixr 1  -<, =<=, -<-, =<>=
infixl 8 =$=, =$$=
infixl 7 =<



-- | A Variable, contains a value and event
data Var a = Var (Value a) Event (IORef [EventHandle])

-- | Create a new Variable
newVar :: (Eq a) => a -> IO (Var a)
newVar x = newVarName "" x

-- | Create a new Variable with an event name
newVarName :: (Eq a) => String -> a -> IO (Var a)
newVarName name x = newVarWithName name (`newValueIORef` x) 

-- | Create a new Variable with an action
newVarWith :: (Event -> IO (Value a)) -> IO (Var a)
newVarWith f = newVarWithName "" f

-- | Create a new Variable with a name and action
newVarWithName :: String -> (Event -> IO (Value a)) -> IO (Var a)
newVarWithName name f = do
    e <- newEventName name
    v <- f e
    i <- newIORef []
    return $ Var v e i


-- | Return the contents of a Variable
getVar :: Var a -> IO a
getVar (Var val _ _) = valGet val


instance Eventer (Var a) where
    event (Var _ evt _) = evt


-- | Inject a value into a Variable
(-<) :: Var a -> a -> IO ()
(-<) (Var val _ source) x = do
    writeIORef source []
    valSet val x

-- | Inject a value from a Variable into another
(-<-) :: Var a -> Var a -> IO ()
(-<-) varto varfrom = getVar varfrom >>= (varto -<)

-- | Inject a value by running a function on another Variable
injectWith :: Var a -> Var b -> (b -> a) -> IO ()
injectWith varto varfrom f = getVar varfrom >>= (varto -<) . f

-- | Tie another Variable with this one
(=<=) :: Var a -> Var a -> IO ()
var1 =<= var2 = var1 =< with1 var2 id

-- | Tie two Variables together with filter functions
tie :: Var a -> Var b -> (a->b) -> (b->a) ->  IO ()
tie var1@(Var val1 _ _) var2@(Var val2 _ _) f12 f21 = do
        -- I tried something sophisticated to avoid loops, but 
	-- it did not work out. This will of course only work if 
	-- the Variables don't fire if they are set to what they are
	-- set already...
	-- Note that I'm not using =<=, not not override the sources
	var1 += (valSet val2 .f12 =<< getVar var1 )
	var2 += (valSet val1 .f21 =<< getVar var2 )
	return ()

-- | Tie two Variables both ways 
(=<>=) :: Var a -> Var a -> IO ()
var1 =<>= var2 = tie var1 var2 id id

-- | Shortcut for with1
with :: Var a -> (a -> x) -> ([Event], IO x)
with = with1

-- | Run a function over a Variable
with1 :: Var a -> (a -> x) -> ([Event], IO x)
with1 varFrom1 f = f =$$= varFrom1

-- |
with2 :: Var a -> Var a1 -> (a -> a1 -> x) -> ([Event], IO x)
with2 var1 var2 f = f =$$= var1 =$= var2

-- |
with3 :: Var a -> Var a1 -> Var a2 -> (a -> a1 -> a2 -> x) -> ([Event], IO x)
with3 var1 var2 var3 f = f =$$= var1 =$= var2 =$= var3

-- | Tie this Variable with the results of a function
(=<) :: Var x -> ([Event], IO x) -> IO ()
(Var valTo _ source) =< (events,f) = do
    srcOld <- readIORef source
    mapM_ remove srcOld
    es <- mapM (+= g) events
    writeIORef source es
    g
    where
        g = f >>= valSet valTo

-- First parameter
-- | Run a function over a Variable
(=$$=) :: (a->x) -> Var a -> ([Event], IO x)
f =$$= x = ([], return f) =$= x

-- Other parameters
-- | Apply a function to a Var, then pass it on or return a result
(=$=) :: ([Event], IO (a -> x)) -> Var a -> ([Event], IO x)
(e,f) =$= v = (event v : e, f `ap` (getVar v))