Data.Global
provides a global namespace of IORef
s, MVar
s and TVar
s. This namespace
may be accessed in pure code. Yet reading and writing to those IORef
s, MVar
s and TVar
s
happens still in their respective monads.
Data.Global
is designed to meet the following use cases:
- Simplify the declaration of top-level mutable variables, by avoiding any pragmas as well
as
unsafePerformIO
. - Avoid having to pass references explicitly throughout the program in order to let distant parts communicate.
- Enable a communication by convention scheme, where e.g. different libraries may communicate without code dependencies.
- Simplify the configuration problem - at least for code in the IO monad.
Note, that this library does not encourage sloppy software design by re-introducing all bad
effects of global variables. Nevertheless, sometimes global variables are a suitable
solution to a problem. In that case Data.Global
simplifies and extends their handling
significantly.
- declareIORef :: Typeable a => String -> a -> IORef a
- declareMVar :: Typeable a => String -> a -> MVar a
- declareTVar :: Typeable a => String -> a -> TVar a
Introductory Example
The most simple usage of Data.Global
is as follows:
Let there be an IORef
!
>>>
let ref = declareIORef \"some-cool-variable\" 17
Use ref
like any other IORef
.
>>>
readIORef ref
17
You can do the same with MVar
s and TVar
s.
The Namespace of Global Variables
The types of variables:
, IORef
a
, and MVar
a
create separate
namespaces. I.e. A variable of type TVar
a
and one of type IORef
Int
can both
exist with the same name.
MVar
Int
Initialization
References / variables returned by any of the declare...
functions are initialized
as needed with the value provided to declare...
. Have a look at this example.
someVar1, someVar2 :: IORef Int someVar1 = declareIORef "my-global-var" 0 someVar2 = declareIORef "my-global-var" 1
someVar1
and someVar2
are guaranteed to always denote the exact same IORef
, but it is
unspecified whether the first read access to that IORef
returns 0
or 1
. It can even
have any other initial value if it is also accessed from some other part of the program.
Reference of Variable Declaration Functions
:: Typeable a | |
=> String | The identifying name |
-> a | The initial value of the |
-> IORef a | A unique |
declareIORef name val
maps a variable name to an IORef
. Calling it multiple times with the same
name
and type of val
will always return the same IORef
.
someVar :: IORef Int someVar = declareMVar "my-global-some-var" 0
Note, there is no need to use unsafePerformIO
or to add a {-# NOINLINE someVar #-}
pragma in order to define top-level IORef
s.
:: Typeable a | |
=> String | The identifying name |
-> a | The initial value of the |
-> MVar a | A unique |
declareMVar name val
maps a variable name to an MVar
. Calling it multiple times with the same
name
and type of val
will always return the same MVar
.
someVar :: MVar Int someVar = declareMVar "my-global-some-var" 0
Note, there is no need to use unsafePerformIO
or to add a {-# NOINLINE someVar #-}
pragma in order to define top-level MVar
s.
:: Typeable a | |
=> String | The identifying name |
-> a | The initial value of the |
-> TVar a | A unique |
declareTVar name val
maps a variable name to an TVar
. Calling it multiple times with the same
name
and type of val
will always return the same TVar
.
someVar :: TVar Int someVar = declareMVar "my-global-some-var" 0
Note, there is no need to use unsafePerformIO
or to add a {-# NOINLINE someVar #-}
pragma in order to define top-level TVar
s.