packages feed

io-memoize 1.0.0.0 → 1.1.0.0

raw patch · 3 files changed

+76/−68 lines, 3 filesdep +asyncdep −spawnPVP ok

version bump matches the API change (PVP)

Dependencies added: async

Dependencies removed: spawn

API changes (from Hackage documentation)

+ Control.Concurrent.Cache: data Cache a
+ Control.Concurrent.Cache: fetch :: Cache a -> IO a -> IO a
+ Control.Concurrent.Cache: newCache :: IO (Cache a)
+ System.IO.Memoize: eagerlyOnce :: IO a -> IO (IO a)
+ System.IO.Memoize: once :: IO a -> IO (IO a)

Files

io-memoize.cabal view
@@ -1,18 +1,16 @@ name:                io-memoize-version:             1.0.0.0+version:             1.1.0.0 synopsis:            Memoize IO actions description:   Transform an IO action into a similar IO action   that performs the original action only once.   .   You can choose to perform the original action-  in one of three ways:+  in one of two ways:   .   (1) lazily (might never be performed)   .-  (2) eagerly-  .-  (3) concurrently (eager)+  (2) eagerly (concurrent)   .   Special thanks to shachaf and headprogrammingczar from #haskell irc   for helping me reason about the behavior of this library.@@ -22,7 +20,7 @@ license-file:        LICENSE author:              Dan Burton maintainer:          danburton.email@gmail.com-copyright:           (c) 2012 Dan Burton+copyright:           (c) 2014 Dan Burton  category:            System build-type:          Simple@@ -30,8 +28,8 @@  library   hs-source-dirs:    src-  exposed-modules:   System.IO.Memoize-  build-depends:     base == 4.*, spawn+  exposed-modules:   System.IO.Memoize, Control.Concurrent.Cache+  build-depends:     base == 4.*, async >= 2.0   ghc-options:       -Wall  source-repository head
+ src/Control/Concurrent/Cache.hs view
@@ -0,0 +1,29 @@+module Control.Concurrent.Cache (Cache, newCache, fetch) where++import Control.Concurrent.MVar+import Data.IORef++-- | A thread-safe write-once cache.+newtype Cache a = Cache {+  -- | Fetch the value stored in the cache,+  -- or call the supplied fallback and store the result,+  -- if the cache is empty.+  fetch :: IO a -> IO a+}++-- | Create an empty cache.+newCache :: IO (Cache a)+newCache = do+  b <- newMVar True+  r <- newIORef undefined+  return (cache b r)++cache :: MVar Bool -> IORef a -> Cache a+cache b r = Cache $ \action -> do+  modifyMVar_ b $ \isEmpty ->+    if isEmpty+      then do v <- action+              writeIORef r v+              return False+      else return False+  readIORef r
src/System/IO/Memoize.hs view
@@ -1,59 +1,60 @@--- | Memoize IO actions,--- performing them at most once,+-- | Memoize IO actions, performing them at most once, -- but recalling their result for subsequent invocations.--- This library provides three sequencing strategies:--- lazy ('ioMemo'), eager ('ioMemo''), and concurrent ('ioMemoPar').--- --- The lazy and eager approaches give stronger sequencing guarantees.+-- This library provides two sequencing strategies:+-- lazy ('once'), and concurrent ('eagerlyOnce'). -- --- The following property holds: @join . ioMemo === id@.--- The same is true for @ioMemo'@ and @ioMemoPar@.+-- The following property holds: @join . once === id@.+-- The same is true for @eagerlyOnce@. -- --- Also, for the three memoizers in this library,--- the memory allocated for the result will not+-- The memory allocated for a memoized result will obviously not -- be available for garbage collection until the corresponding--- memoized action is also available for garbage collection,--- unless your compiler performs deep magicks.+-- memoized action is also available for garbage collection. module System.IO.Memoize (-    ioMemo+    once+  , eagerlyOnce+  , ioMemo   , ioMemo'   , ioMemoPar   ) where -import Control.Concurrent.Spawn-import Control.Concurrent.MVar-import Data.IORef+import Control.Concurrent.Async (async, wait)+import Control.Concurrent.Cache (newCache, fetch) --- | Memoize an IO action.--- The action will be performed+-- | Memoize an IO action. The action will be performed -- the first time that it its value is demanded;--- all subsequent invocations--- will simply recall the value acquired--- from the first call.--- If the value is never demanded,+-- all subsequent invocations will simply recall the value acquired+-- from the first call. If the value is never demanded, -- then the action will never be performed. -- --- This is basically a safe version of--- 'System.IO.Unsafe.unsafeInterleaveIO'.--- This function is also thread-safe:--- it is guaranteed that the action passed in--- will be performed exactly 0 or 1 times--- by this code. Exceptions will be propagated--- to the caller.+-- This is basically a safe version of 'System.IO.Unsafe.unsafeInterleaveIO'.+-- Exceptions will be propagated to the caller, and the action will be retried+-- at each invocation, only until it has successfully completed once. --  -- Example usage: -- --- >>> getLine' <- ioMemo getLine+-- >>> getLine' <- once getLine --  -- >>> replicateM 3 getLine' -- Hello -- ["Hello", "Hello", "Hello"]-ioMemo :: IO a -> IO (IO a)-ioMemo action = do-  memo <- newIOMemoizer-  return (memo action)+once :: IO a -> IO (IO a)+once action = do+  cache <- newCache+  return (fetch cache action)  -- | Memoize an IO action.+-- The action will be started immediately in a new thread.+-- Attempts to access the result will block until the action is finished.+-- If the action produces an error, then attempts to access its value+-- will re-raise the same error each time.+eagerlyOnce :: IO a -> IO (IO a)+eagerlyOnce action = do+  thread <- async action+  return (wait thread)+++{-# DEPRECATED ioMemo' "Please just call the action directly." #-}+-- | Memoize an IO action. -- The action will be performed immediately; -- all subsequent invocations -- will recall the value acquired.@@ -62,30 +63,10 @@   v <- action   return (return v) --- | Memoize an IO action.--- The action will be performed immediately--- in a spawned thread.--- Attempts to access the result--- will block until the action is finished.--- --- This is simply a synonym for 'Control.Concurrent.Spawn.spawn'-ioMemoPar :: IO a -> IO (IO a)-ioMemoPar = spawn----newIOMemoizer :: IO (IO a -> IO a)-newIOMemoizer = do-  b <- newMVar True-  r <- newIORef undefined-  return (ioMemoizer b r)+{-# DEPRECATED ioMemo "Please use 'once'." #-}+ioMemo :: IO a -> IO (IO a)+ioMemo = once -ioMemoizer :: MVar Bool -> IORef a -> IO a -> IO a-ioMemoizer b r action = do-  modifyMVar_ b $ \isEmpty ->-    if isEmpty-      then do v <- action-              writeIORef r v-              return False-      else return False-  readIORef r+{-# DEPRECATED ioMemoPar "Please use 'eagerlyOnce'." #-}+ioMemoPar :: IO a -> IO (IO a)+ioMemoPar = eagerlyOnce