1. Jean-Marie Gaillourdet
  2. global-variables

Commits

Jean-Marie Gaillourdet  committed 2e835e5 Merge

merged default into IO-based-typeOf-workaround to keep it current

This version seems to fail quite often :-(

  • Participants
  • Parent commits f4f8903, da1bbbc
  • Branches IO-based-typeOf-workaround

Comments (0)

Files changed (6)

File examples/multimodule/Main.hs

View file
+module Main where
+
+import Data.IORef
+import Data.Global
+
+import Module1
+import Module2
+
+main :: IO ()
+main = do 
+    increaseBy2
+    tripple
+    increaseBy2
+    tripple
+    let ref = declareIORef "my-global-var" 17 :: IORef Int
+    print =<< readIORef ref

File examples/multimodule/Module1.hs

View file
+module Module1 (
+    increaseBy2
+)where
+
+import Data.Global
+import Data.IORef
+
+someVar :: IORef Int
+someVar = declareIORef "my-global-var" 0
+
+increaseBy2 :: IO ()
+increaseBy2 = atomicModifyIORef someVar inc2
+  where
+    inc2 x = (2+x, ())

File examples/multimodule/Module2.hs

View file
+module Module2 (
+    tripple
+) where
+
+
+import Data.Global
+import Data.IORef
+
+someVar :: IORef Int
+someVar = declareIORef "my-global-var" 0
+
+tripple :: IO ()
+tripple = atomicModifyIORef someVar tripple'
+  where
+    tripple' x = (x*3, ())

File global-variables.cabal

View file
 Name:               global-variables
 Version:            1.0
 
-Synopsis:           Declaratively named, global, and top-level mutable
-                    variables without unsafePerformIO.
+Synopsis:           Namespaced, global, and top-level mutable variables without
+                    unsafePerformIO.
 
-Description:        some desc
+Description:
+
+  '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.
+   .
+   Examples are in the documentation of "Data.Global".
 
 License:            BSD3
 License-file:       LICENSE
 Author:             Jean-Marie Gaillourdet and Patrick Michel
-Maintainer:         Jean-Marie Gaillourdet <jmg@gaillourdet.net>    
+Maintainer:         Jean-Marie Gaillourdet <jmg@gaillourdet.net>
 Copyright:          2011 Jean-Marie Gaillourdet
 Bug-Reports:        http://bitbucket.org/jmg/global-variables/issues
 
 
 -- Extra files to be distributed with the package, such as examples or
 -- a README.
--- Extra-source-files:  
+-- Extra-source-files:
 
 Cabal-version:      >=1.2
 
 --                    , Data.Global.IORef
 --                    , Data.Global.TVar
   Hs-Source-Dirs:   src
-  
+
   Build-depends:      base >= 4
                     , containers >= 0.3 && < 0.5
                     , stm >= 2.1 && < 2.2
-  
+
   Other-modules:    Data.Global.Registry
 
   Ghc-Options:      -Wall -O2
-  
+
 Executable          runtests
   if flag(test)
     Buildable:      True

File src/Data/Global.hs

View file
+-- -*- encoding: utf-8; fill-column: 95 -*-
+
+-----------------------------------------------------------------------------------------------
+--
+-- Module        : Data.Global
+-- Creation Date : 2011-09-01
+-- Authors       : Jean-Marie Gaillourdet <jmg@gaillourdet.net>
+-- License       : BSD-style
+-- Portability   : all
+--
+--
+-----------------------------------------------------------------------------------------------
+
+-- | '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.
 module Data.Global (
-    module Data.Global.Registry
+
+  -- * Introductory Example
+  -- $intro
+
+  -- * The Namespace of Global Variables
+  -- $namespace
+
+  -- * Initialization
+  -- $init
+
+  -- * Reference of Variable Declaration Functions
+    declareIORef
+  , declareMVar
+  , declareTVar
+
 ) where
 
 import Data.Global.Registry
 
 
 
+-- $intro
+--
+-- 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.
 
 
 
+-- $namespace
+
+
+
+-- $init
+-- 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 accessed from some other part of the program.

File src/Data/Global/Registry.hs

View file
+-- -*- encoding: utf-8; fill-column: 95 -*-
+
 {-# LANGUAGE CPP, ScopedTypeVariables #-}
+-----------------------------------------------------------------------------------------------
+-- |
+-- Module        : Data.Global.Registry
+-- Creation Date : 2011-09-01
+-- Authors       : Jean-Marie Gaillourdet <jmg@gaillourdet.net>
+-- License       : BSD-style
+-- Portability   : all
+--
+-- The internal module.
+-----------------------------------------------------------------------------------------------
 module Data.Global.Registry (
+  -- * Public Interface
     declareIORef, declareMVar, declareTVar
 
+  -- * Private Testing Interface
   , lookupOrInsert
   , setupRegistry
 ) where
 
+#if __GLASGOW_HASKELL__ < 702
+import Control.Exception       ( evaluate )
+#endif
 import Control.Concurrent.MVar
 import Control.Concurrent.STM
-import Control.Exception
 import Data.IORef
 import Data.Dynamic
 import Data.Map as M
 
 
 #if __GLASGOW_HASKELL__ >= 702
-type Registry = Map (TypeRep,String) Dynamic
+type Registry = Map (TypeRep,TypeRep,String) Dynamic
 #else
-type Registry = Map (Int,String) Dynamic
+type Registry = Map (Int,Int,String) Dynamic
 #endif
 
 -- | Test helper
   where
     err ex got = error $ "Data.Global.Registry: Invariant violation\n"
                        ++ "expected: " ++ show ex ++ "\n"
-                       ++ "got: " ++ show got ++ "\n" 
+                       ++ "got: " ++ show got ++ "\n"
 
 #if __GLASGOW_HASKELL__ >= 702
-    typ = typeOf val
+    typVal = typeOf val
+    typRef = typeOf (undefined :: ref ()) -- TypeRep representing the reference, e.g. IORef,
+                                          -- MVar
 
     lkup :: Registry -> IO (Registry, ref a)
-    lkup reg = case M.lookup (typ, name) reg of
-        Just ref -> return (reg, fromDyn ref (err typ (dynTypeRep ref)))
-        Nothing -> 
+    lkup reg = case M.lookup (typRef, typVal, name) reg of
+        Just ref -> return (reg, fromDyn ref (err typVal (dynTypeRep ref)))
+        Nothing ->
          do { ref <- new val
-            ; return (M.insert (typ, name) (toDyn ref) reg, ref)
+            ; return (M.insert (typRef, typVal, name) (toDyn ref) reg, ref)
             }
 #else
     lkup :: Registry -> IO (Registry, ref a)
-    lkup reg = 
-     do { typ <- typeOf' val
-        ; typIdx <- typeRepKey typ
-        ; case M.lookup (typIdx, name) reg of
-            Just ref -> return (reg, fromDyn ref (err typ (dynTypeRep ref)))
-            Nothing -> 
+    lkup reg =
+     do { typVal <- typeOf' val
+        ; typRef <- typeOf' (undefined :: ref ()) -- TypeRep representing the reference,
+                                                  -- e.g. IORef, MVar
+        ; typValIdx <- typeRepKey typVal
+        ; typRefIdx <- typeRepKey typRef
+        ; case M.lookup (typRefIdx, typValIdx, name) reg of
+            Just ref -> return (reg, fromDyn ref (err typVal (dynTypeRep ref)))
+            Nothing ->
              do { ref <- new val
                 ; _ <- typeOf' ref
-                ; return (M.insert (typIdx, name) (toDyn ref) reg, ref)
+                ; return (M.insert (typRefIdx, typValIdx, name) (toDyn ref) reg, ref)
                 }
         }
 
 {-# NOINLINE lookupOrInsertTVar #-}
 
 
-
+-- | @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.
 declareIORef
     :: Typeable a
-    => String
-    -> a
-    -> (IORef a)
+    => String     -- ^ The identifying name
+    -> a          -- ^ The initial value of the 'IORef', it may or may not be used.
+    -> (IORef a)  -- ^ A unique 'IORef' determined by @(name, typeOf val)@. Whether it refers
+                  -- to the given initial value or not is unspecified.
 declareIORef name val = unsafePerformIO $ lookupOrInsertIORef name val
 {-# NOINLINE declareIORef #-}
 
 
 
+-- | @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.
 declareMVar
     :: Typeable a
-    => String
-    -> a
-    -> (MVar a)
+    => String    -- ^ The identifying name
+    -> a         -- ^ The initial value of the 'MVar', it may or may not be used.
+    -> (MVar a)  -- ^ A unique 'MVar' determined by @(name, typeOf val)@. Whether it refers to
+                 -- the given initial value or not is unspecified.
 declareMVar name val = unsafePerformIO $ lookupOrInsertMVar name val
 {-# NOINLINE declareMVar #-}
 
 
 
+-- | @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.
 declareTVar
     :: Typeable a
-    => String
-    -> a
-    -> (TVar a)
+    => String    -- ^ The identifying name
+    -> a         -- ^ The initial value of the 'TVar', it may or may not be used.
+    -> (TVar a)  -- ^ A unique 'TVar' determined by @(name, typeOf val)@. Whether it refers to
+                 -- the given initial value or not is unspecified.
 declareTVar name val = unsafePerformIO $ lookupOrInsertTVar name val
 {-# NOINLINE declareTVar #-}