unliftio 0.2.7.1 → 0.2.8.0
raw patch · 5 files changed
+133/−5 lines, 5 filesPVP ok
version bump matches the API change (PVP)
API changes (from Hackage documentation)
+ UnliftIO.Memoize: data Memoized a
+ UnliftIO.Memoize: instance GHC.Base.Applicative UnliftIO.Memoize.Memoized
+ UnliftIO.Memoize: instance GHC.Base.Functor UnliftIO.Memoize.Memoized
+ UnliftIO.Memoize: instance GHC.Base.Monad UnliftIO.Memoize.Memoized
+ UnliftIO.Memoize: instance GHC.Show.Show (UnliftIO.Memoize.Memoized a)
+ UnliftIO.Memoize: memoizeMVar :: MonadUnliftIO m => m a -> m (Memoized a)
+ UnliftIO.Memoize: memoizeRef :: MonadUnliftIO m => m a -> m (Memoized a)
+ UnliftIO.Memoize: runMemoized :: MonadIO m => Memoized a -> m a
Files
- ChangeLog.md +4/−0
- src/UnliftIO.hs +2/−0
- src/UnliftIO/Memoize.hs +75/−0
- test/UnliftIO/MemoizeSpec.hs +45/−0
- unliftio.cabal +7/−5
ChangeLog.md view
@@ -1,5 +1,9 @@ # Changelog for unliftio +## 0.2.8.0++* Add 'UnliftIO.Memoize'+ ## 0.2.7.1 * Minor doc improvements
src/UnliftIO.hs view
@@ -7,6 +7,7 @@ , module UnliftIO.Exception , module UnliftIO.IO , module UnliftIO.IORef+ , module UnliftIO.Memoize , module UnliftIO.MVar , module UnliftIO.STM , module UnliftIO.Temporary@@ -19,6 +20,7 @@ import UnliftIO.Exception import UnliftIO.IO import UnliftIO.IORef+import UnliftIO.Memoize import UnliftIO.MVar import UnliftIO.STM import UnliftIO.Temporary
+ src/UnliftIO/Memoize.hs view
@@ -0,0 +1,75 @@+{-# LANGUAGE GeneralizedNewtypeDeriving #-}+-- | Memoize the results of actions. In other words: actions+-- will be run once, on demand, and their results saved.+--+-- Exceptions semantics: if a synchronous exception is thrown while performing+-- the computation, that result will be saved and rethrown each time+-- 'runMemoized' is called subsequently.'+--+-- @since 0.2.8.0+module UnliftIO.Memoize+ ( Memoized+ , runMemoized+ , memoizeRef+ , memoizeMVar+ ) where++import Control.Applicative as A+import Control.Monad (join)+import Control.Monad.IO.Unlift+import UnliftIO.Exception+import UnliftIO.IORef+import UnliftIO.MVar++-- | A \"run once\" value, with results saved. Extract the value with+-- 'runMemoized'. For single-threaded usage, you can use 'memoizeRef' to+-- create a value. If you need guarantees that only one thread will run the+-- action at a time, use 'memoizeMVar'.+--+-- Note that this type provides a 'Show' instance for convenience, but not+-- useful information can be provided.+--+-- @since 0.2.8.0+newtype Memoized a = Memoized (IO a)+ deriving (Functor, A.Applicative, Monad)+instance Show (Memoized a) where+ show _ = "<<Memoized>>"++-- | Extract a value from a 'Memoized', running an action if no cached value is+-- available.+--+-- @since 0.2.8.0+runMemoized :: MonadIO m => Memoized a -> m a+runMemoized (Memoized m) = liftIO m+{-# INLINE runMemoized #-}++-- | Create a new 'Memoized' value using an 'IORef' under the surface. Note that+-- the action may be run in multiple threads simultaneously, so this may not be+-- thread safe (depending on the underlying action). Consider using+-- 'memoizeMVar'.+--+-- @since 0.2.8.0+memoizeRef :: MonadUnliftIO m => m a -> m (Memoized a)+memoizeRef action = withRunInIO $ \run -> do+ ref <- newIORef Nothing+ pure $ Memoized $ do+ mres <- readIORef ref+ res <-+ case mres of+ Just res -> pure res+ Nothing -> do+ res <- tryAny $ run action+ writeIORef ref $ Just res+ pure res+ either throwIO pure res++-- | Same as 'memoizeRef', but uses an 'MVar' to ensure that an action is+-- only run once, even in a multithreaded application.+--+-- @since 0.2.8.0+memoizeMVar :: MonadUnliftIO m => m a -> m (Memoized a)+memoizeMVar action = withRunInIO $ \run -> do+ var <- newMVar Nothing+ pure $ Memoized $ join $ modifyMVar var $ \mres -> do+ res <- maybe (tryAny $ run action) pure mres+ pure (Just res, either throwIO pure res)
+ test/UnliftIO/MemoizeSpec.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE DeriveDataTypeable #-}+module UnliftIO.MemoizeSpec (spec) where++import Control.Concurrent (threadDelay)+import Control.Monad (replicateM_)+import Test.Hspec+import Test.Hspec.QuickCheck+import UnliftIO+import Data.Typeable++data Dummy = Dummy+ deriving (Show, Typeable)+instance Exception Dummy++spec :: Spec+spec = do+ let basics maker = do+ prop "sanity" $ \i -> do+ x <- maker $ return (i :: Int)+ runMemoized x `shouldReturn` i+ prop "runs once" $ \i -> do+ count <- newIORef (0 :: Int)+ x <- maker $ do+ modifyIORef' count (+ 1)+ return (i :: Int)+ replicateM_ 10 $ runMemoized x `shouldReturn` i+ readIORef count `shouldReturn` 1+ it "runs once with exception" $ do+ count <- newIORef (0 :: Int)+ x <- maker $ do+ modifyIORef' count (+ 1)+ throwIO Dummy+ replicateM_ 10 $ runMemoized x `shouldThrow` (\Dummy -> True)+ readIORef count `shouldReturn` 1+ describe "memoizeRef" $ basics memoizeRef+ describe "memoizeMVar" $ do+ basics memoizeMVar+ prop "runs once in multiple threads" $ \i -> do+ count <- newIORef (0 :: Int)+ x <- memoizeMVar $ do+ threadDelay 10000+ atomicModifyIORef' count $ \cnt -> (cnt + 1, ())+ return (i :: Int)+ replicateConcurrently_ 10 $ runMemoized x `shouldReturn` i+ readIORef count `shouldReturn` 1
unliftio.cabal view
@@ -1,13 +1,13 @@-cabal-version: >= 1.10+cabal-version: 1.12 --- This file has been generated from package.yaml by hpack version 0.29.6.+-- This file has been generated from package.yaml by hpack version 0.30.0. -- -- see: https://github.com/sol/hpack ----- hash: 80f98ec8618eddf5cd9e60732404c2af04ec9c97f9edefb9aa7b236b776e10ea+-- hash: 943e82967e90e314577381f82ab9a07663e77328d1364a19289e0c5637e3f2d6 name: unliftio-version: 0.2.7.1+version: 0.2.8.0 synopsis: The MonadUnliftIO typeclass for unlifting monads to IO (batteries included) description: Please see the documentation and README at <https://www.stackage.org/package/unliftio> category: Control@@ -19,8 +19,8 @@ license-file: LICENSE build-type: Simple extra-source-files:- ChangeLog.md README.md+ ChangeLog.md library hs-source-dirs:@@ -60,6 +60,7 @@ UnliftIO.Foreign UnliftIO.IO UnliftIO.IORef+ UnliftIO.Memoize UnliftIO.MVar UnliftIO.Process UnliftIO.STM@@ -93,5 +94,6 @@ other-modules: UnliftIO.ExceptionSpec UnliftIO.IOSpec+ UnliftIO.MemoizeSpec Paths_unliftio default-language: Haskell2010