packages feed

value-supply 0.4 → 0.5

raw patch · 2 files changed

+91/−88 lines, 2 files

Files

Data/Supply.hs view
@@ -21,14 +21,15 @@   , newSupply   , newEnumSupply   , newNumSupply-  , unsafeNewIntSupply +  , newDupableSupply+  , newDupableEnumSupply+  , newDupableNumSupply+   -- * Obtaining values from supplies   , supplyValue    -- * Generating new supplies from old-  , supplyLeft-  , supplyRight   , modifySupply   , split   , split2@@ -36,105 +37,100 @@   , split4   ) where --- Usinga an IORef is thread-safe because we update it with 'atomicModifyIORef'.--- XXX: Is the atomic necessary?-import Data.IORef(IORef,newIORef,atomicModifyIORef)-import System.IO.Unsafe(unsafePerformIO,unsafeInterleaveIO)+-- NOTE: Using an IORef is thread-safe because we update it with+-- 'atomicModifyIORef'.  We need the 'atomicModifyRef' because multiple+-- threads may be evaluating different supplies that share the same+-- 'IORef' and we need to avoid race conditions.  This is the case for+-- both the normal and the dupable supplies.+import Data.IORef(newIORef,atomicModifyIORef)+import System.IO.Unsafe(unsafeInterleaveIO)  #if __GLASGOW_HASKELL__ >= 608-import GHC.IOBase(unsafeDupableInterleaveIO,unsafeDupablePerformIO)+import GHC.IOBase(unsafeDupableInterleaveIO) #else unsafeDupableInterleaveIO :: IO a -> IO a unsafeDupableInterleaveIO = unsafeInterleaveIO--unsafeDupablePerformIO :: IO a -> a-unsafeDupablePerformIO = unsafePerformIO #endif  -- Basics ----------------------------------------------------------------------  -- | A type that can be used to generate values on demand.--- A supply may be turned into two different supplies by using--- the functions 'supplyLeft' and 'supplyRight'.-data Supply a = Node-  { -- | Get the value of a supply.  This function, together with-    -- 'modifySupply' forms a comonad on 'Supply'.-    supplyValue :: a+data Supply a = Node a (Supply a) (Supply a) -  -- | Generate a new supply.  This supply is different from the one-  -- generated with 'supplyRight'.-  , supplyLeft  :: Supply a+-- | Get the value of a supply.  This function, together with+-- 'modifySupply' forms a comonad on 'Supply'.+supplyValue    :: Supply a -> a+supplyValue (Node a _ _) = a -  -- | Generate a new supply. This supply is different from the one-  -- generated with 'supplyLeft'.-  , supplyRight :: Supply a-  }+-- | Generate an infinite list of supplies.+split          :: Supply a -> [Supply a]+split (Node _ s1 s2)  = s1 : split s2 +-- | Split a supply into two different supplies.+-- The resulting supplies are different from the input supply.+split2         :: Supply a -> (Supply a, Supply a)+split2 (Node _ s1 s2) = (s1,s2)++-- | Split a supply into three different supplies.+split3         :: Supply a -> (Supply a, Supply a, Supply a)+split3 (Node _ s1 (Node _ s2 s3)) = (s1,s2,s3)++-- | Split a supply into four different supplies.+split4         :: Supply a -> (Supply a, Supply a, Supply a, Supply a)+split4 (Node _ s1 (Node _ s2 (Node _ s3 s4))) = (s1,s2,s3,s4)++++ instance Functor Supply where   fmap f s = modifySupply s (f . supplyValue)  -{-# INLINE genericNewSupply #-}-genericNewSupply :: b -> (IORef b -> IO a) -> IO (Supply a)-genericNewSupply start genSym = gen =<< newIORef start-  where gen r = unsafeInterleaveIO-              $ do ls <- gen r-                   rs <- gen r-                   return (Node (unsafePerformIO (genSym r)) ls rs)- -- | Creates a new supply of values. -- The arguments specify how to generate values: -- the first argument is an initial value, the -- second specifies how to generate a new value from an existing one.-newSupply      :: a -> (a -> a) -> IO (Supply a)-newSupply x f   = genericNewSupply (iterate f x) listGenSym+{-# INLINE newSupply #-}+newSupply :: a -> (a -> a) -> IO (Supply a)+newSupply start next = gen =<< newIORef start+  where gen r = unsafeInterleaveIO+              $ do v  <- unsafeInterleaveIO (atomicModifyIORef r upd)+                   ls <- gen r+                   rs <- gen r+                   return (Node v ls rs)+        upd a = let b = next a in seq b (b, a) -{-# SPECIALIZE newEnumSupply :: IO (Supply Int) #-}--- | A supply of values that are in the 'Enum' class.--- The initial value is @toEnum 0@, new values are generates with 'succ'.-newEnumSupply  :: (Enum a) => IO (Supply a)-newEnumSupply   = genericNewSupply (toEnum 0) enumGenSym -{-# SPECIALIZE newNumSupply :: IO (Supply Int) #-}--- | A supply of values that are in the 'Num' class.--- The initial value is 0, new values are generated by adding 1.-newNumSupply   :: (Num a) => IO (Supply a)-newNumSupply    = genericNewSupply 0 numGenSym---- | Create a supply of ints.--- WARNING: In general, this is not thread safe!--- It should be OK, as long as the supply is not accessed by different threads.--- So, if you are in a multi-threaded setting, first split--- the supply, and give /different/ supply values to the different threads.-unsafeNewIntSupply :: IO (Supply Int)-unsafeNewIntSupply = gen =<< newIORef 0+-- | Create a new supply of values.+-- WARNING: This version is faster then 'newSupply' but it is not completely+-- thread safe, so use only if performance is an issue!+--+-- Rules for using the generated supplies:+--   * Supply splitting should be evaluated in a single thread.+--     For example, use "case" with 'split2' to force the splitting+--     of a supply.+--   * Different threads should work with different supplies.+--     For example, one could (strictly) split a supply, and then+--     fork new threads with the resulting supplies.+{-# INLINE newDupableSupply #-}+newDupableSupply :: a -> (a -> a) -> IO (Supply a)+newDupableSupply start next = gen =<< newIORef start   where gen r = unsafeDupableInterleaveIO-              $ do ls <- gen r+              $ do v  <- unsafeDupableInterleaveIO (atomicModifyIORef r upd)+                   ls <- gen r                    rs <- gen r-                   return (Node (unsafeDupablePerformIO (enumGenSym r)) ls rs)--+                   return (Node v ls rs)+        upd a = let b = next a in seq b (b, a)  --- Different ways to generate new values:-listGenSym     :: IORef [a] -> IO a-listGenSym r    = atomicModifyIORef r (\(a:as) -> (as,a))--enumGenSym     :: Enum a => IORef a -> IO a-enumGenSym r    = atomicModifyIORef r (\a -> let n = succ a in seq n (n,a))--numGenSym      :: Num a => IORef a -> IO a-numGenSym r     = atomicModifyIORef r (\a -> let n = 1 + a in seq n (n,a))-+-- XXX: Is the strictness of 'modifySupply' OK?  -- | Generate a new supply by systematically applying a function -- to an existing supply.  This function, together with 'supplyValue' -- form a comonad on 'Supply'. modifySupply :: Supply a -> (Supply a -> b) -> Supply b-modifySupply s f = Node { supplyValue = f s-                        , supplyLeft  = modifySupply (supplyLeft s) f-                        , supplyRight = modifySupply (supplyRight s) f-                        }+modifySupply s f = Node (f s) (modifySupply l f) (modifySupply r f)+  where Node _ l r = s  -- (Supply, supplyValue, modifySupply) form a comonad: {-@@ -146,26 +142,31 @@ -}  --- Derived functions -----------------------------------------------------------+{-# SPECIALIZE newEnumSupply :: IO (Supply Int) #-}+-- | A supply of values that are in the 'Enum' class.+-- The initial value is @toEnum 0@, new values are generates with 'succ'.+newEnumSupply        :: (Enum a) => IO (Supply a)+newEnumSupply         = newSupply (toEnum 0) succ --- | Generate an infinite list of supplies by using 'supplyLeft' and--- 'supplyRight' repeatedly.-split          :: Supply a -> [Supply a]-split s         = supplyLeft s : split (supplyRight s)+{-# SPECIALIZE newNumSupply :: IO (Supply Int) #-}+-- | A supply of values that are in the 'Num' class.+-- The initial value is 0, new values are generated by adding 1.+newNumSupply         :: (Num a) => IO (Supply a)+newNumSupply          = newSupply 0 (1+) --- | Split a supply into two different supplies.--- The resulting supplies are different from the input supply.-split2         :: Supply a -> (Supply a, Supply a)-split2 s        = (supplyLeft s, supplyRight s)+{-# SPECIALIZE newDupableEnumSupply :: IO (Supply Int) #-}+-- | A supply of values that are in the 'Enum' class.+-- The initial value is @toEnum 0@, new values are generates with 'succ'.+-- WARNING: See comment on 'newDupableSupply'+newDupableEnumSupply :: (Enum a) => IO (Supply a)+newDupableEnumSupply  = newDupableSupply (toEnum 0) succ --- | Split a supply into three different supplies.-split3         :: Supply a -> (Supply a, Supply a, Supply a)-split3 s        = let s1 : s2 : s3 : _ = split s-                  in (s1,s2,s3)+{-# SPECIALIZE newDupableNumSupply :: IO (Supply Int) #-}+-- | A supply of values that are in the 'Num' class.+-- The initial value is 0, new values are generated by adding 1.+-- WARNING: See comment on 'newDupableSupply'+newDupableNumSupply  :: (Num a) => IO (Supply a)+newDupableNumSupply   = newDupableSupply 0 (1+) --- | Split a supply into four different supplies.-split4         :: Supply a -> (Supply a, Supply a, Supply a, Supply a)-split4 s        = let s1 : s2 : s3 : s4 : _ = split s-                  in (s1,s2,s3,s4)  
value-supply.cabal view
@@ -1,9 +1,11 @@ Name:           value-supply-Version:        0.4+Version:        0.5+Homepage:       http://github.com/yav/value-supply License:        BSD3 License-file:   LICENSE Author:         Iavor S. Diatchki Maintainer:     iavor.diatchki@gmail.com+ Category:       Data Synopsis: A library for generating values without having to thread state. Description: