### Execute pooled concurrency example Source: https://context7.com/fpco/unliftio/llms.txt Runs the `downloadUrls` example, demonstrating how `pooledMapConcurrentlyN` limits the number of concurrent downloads to 3, as evidenced by the thread IDs printed. ```haskell main :: IO () main = do putStrLn "Downloading with 3 threads max:" results <- downloadUrls ["url1", "url2", "url3", "url4", "url5"] mapM_ putStrLn results -- Output shows only 3 threads active at a time ``` -------------------------------- ### Execute example `safeDivide` and `processFile` Source: https://context7.com/fpco/unliftio/llms.txt Demonstrates the usage of `safeDivide` for exception-safe division and `processFile` for guaranteed cleanup using `finally` within a `ReaderT` context. ```haskell main :: IO () main = do result <- safeDivide 10 0 print result -- Left "Error: divide by zero" result2 <- safeDivide 10 2 print result2 -- Right 5 runReaderT processFile (AppEnv 3) -- Output: -- Starting... -- Processing file -- Cleanup complete ``` -------------------------------- ### Example: Download URLs with a thread limit Source: https://context7.com/fpco/unliftio/llms.txt Demonstrates using `pooledMapConcurrentlyN` to download multiple URLs, limiting the concurrent downloads to 3 threads to manage network or server load. ```haskell -- Example: Process URLs with max 3 concurrent downloads downloadUrls :: [String] -> IO [String] downloadUrls urls = pooledMapConcurrentlyN 3 simulateDownload urls where simulateDownload url = do tid <- myThreadId putStrLn $ show tid ++ " downloading: " ++ url threadDelay 1000000 -- 1 second return $ "Content of " ++ url ``` -------------------------------- ### Execute async examples: `race`, `concurrently`, and `mapConcurrently` Source: https://context7.com/fpco/unliftio/llms.txt Demonstrates the practical usage of `race` for competing asynchronous tasks, `concurrently` for parallel execution of two tasks, and `mapConcurrently` for parallel list processing. ```haskell main :: IO () main = do -- Race example result <- race (threadDelay 100000 >> return "slow") (return "fast") print result -- Right "fast" -- Concurrent processing results <- processItems [1, 2, 3, 4, 5] print results -- [2, 4, 6, 8, 10] -- Both actions complete (a, b) <- concurrently (return 1) (return 2) print (a, b) -- (1, 2) ``` -------------------------------- ### Main execution function Source: https://context7.com/fpco/unliftio/llms.txt The main function orchestrates the execution of the different IORef examples. ```haskell import UnliftIO.IORef import UnliftIO.Async import Control.Monad (replicateM_) main :: IO () main = do refExample atomicExample atomicWriteExample ``` -------------------------------- ### Capture Unlift Function with askUnliftIO Source: https://context7.com/fpco/unliftio/llms.txt Shows how to use `askUnliftIO` to obtain an `UnliftIO` wrapper, which contains a function to run monadic actions in `IO`. This is useful for storing or passing the unlift capability, for example, to background threads. ```haskell import Control.Monad.IO.Unlift import Control.Monad.Trans.Reader import Control.Concurrent (forkIO) -- UnliftIO newtype wraps the unlift function -- newtype UnliftIO m = UnliftIO { unliftIO :: forall a. m a -> IO a } data Env = Env { envName :: String } -- Store the unlift capability for later use spawnWorker :: ReaderT Env IO () spawnWorker = do u <- askUnliftIO env <- ask liftIO $ do _ <- forkIO $ unliftIO u $ do name <- asks envName liftIO $ putStrLn $ "Worker started for: " ++ name return () main :: IO () main = runReaderT spawnWorker (Env "MyApp") -- Output: Worker started for: MyApp ``` -------------------------------- ### Unlifting with mask using MonadUnliftIO Source: https://github.com/fpco/unliftio/blob/master/unliftio/README.md A more complex example showing how to unlift the 'mask' function using MonadUnliftIO. It involves using withRunInIO to capture a run function and then applying liftIO with restore. ```haskell mask :: MonadUnliftIO m => ((forall a. m a -> m a) -> m b) -> m b mask f = withRunInIO $ \run -> Control.Exception.mask $ \restore -> run $ f $ liftIO . restore . run ``` -------------------------------- ### Spawn asynchronous computation with `async` Source: https://context7.com/fpco/unliftio/llms.txt Use `async` from `UnliftIO.Async` to start a computation in a separate thread within a `MonadUnliftIO` context. It returns an `Async` handle for managing the computation. ```haskell import UnliftIO.Async import UnliftIO.Exception import Control.Monad.Trans.Reader import Control.Concurrent (threadDelay) data Config = Config { timeout :: Int } -- async - Spawn an async computation spawnTask :: ReaderT Config IO (Async Int) spawnTask = async $ do cfg <- ask liftIO $ threadDelay (timeout cfg * 1000) return 42 ``` -------------------------------- ### MonadUnliftIO Typeclass and withRunInIO Source: https://context7.com/fpco/unliftio/llms.txt Demonstrates the core MonadUnliftIO typeclass and its primary method, withRunInIO, for safely unlifting monadic computations. Includes an example of implementing a function that uses withRunInIO within a ReaderT transformer. ```APIDOC ## MonadUnliftIO Typeclass and withRunInIO ### Description The `MonadUnliftIO` typeclass allows for safely running monadic actions within the `IO` monad by capturing the current monadic context. The `withRunInIO` function is the primary method for achieving this, taking a function that receives an 'unlift' function (`m a -> IO a`) and returns a result within the monad `m`. ### Method `withRunInIO :: ((forall a. m a -> IO a) -> IO b) -> m b` ### Endpoint N/A (Typeclass method) ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```haskell {-# LANGUAGE RankNTypes #-} import Control.Monad.IO.Unlift import Control.Monad.Trans.Reader import System.IO (Handle, IOMode(..), hPutStrLn) -- Helper function using withRunInIO myWithBinaryFile :: MonadUnliftIO m => FilePath -> IOMode -> (Handle -> m a) -> m a myWithBinaryFile fp mode inner = withRunInIO $ \runInIO -> System.IO.withBinaryFile fp mode (\h -> runInIO (inner h)) -- Example usage in a ReaderT context data AppConfig = AppConfig { logLevel :: Int } app :: ReaderT AppConfig IO () app = do config <- ask myWithBinaryFile "output.txt" WriteMode $ \handle -> do liftIO $ hPutStrLn handle $ "Log level: " ++ show (logLevel config) return () main :: IO () main = runReaderT app (AppConfig 3) ``` ### Response #### Success Response (200) N/A (This is a typeclass method, not an HTTP endpoint) #### Response Example N/A ``` -------------------------------- ### Implement withBinaryFile for ReaderT Source: https://github.com/fpco/unliftio/blob/master/unliftio/README.md Shows the manual implementation of a bracket-style function for ReaderT by unwrapping and re-wrapping the environment. ```haskell myWithBinaryFile fp mode inner = ReaderT $ \env -> withBinaryFile fp mode (\h -> runReaderT (inner h) env) ``` -------------------------------- ### Implement withBinaryFile using MonadUnliftIO Source: https://github.com/fpco/unliftio/blob/master/unliftio/README.md Uses withRunInIO to safely execute IO-based functions that accept callbacks within a MonadUnliftIO context. ```haskell import Control.Monad.IO.Unlift myWithBinaryFile :: MonadUnliftIO m => FilePath -> IOMode -> (Handle -> m a) -> m a myWithBinaryFile fp mode inner = withRunInIO $ \runInIO -> withBinaryFile fp mode (\h -> runInIO (inner h)) ``` -------------------------------- ### Implementing Timeout with MonadUnliftIO Source: https://github.com/fpco/unliftio/blob/master/unliftio/README.md Demonstrates how to implement a 'timeout' function using MonadUnliftIO. It uses withRunInIO to capture a run function and then applies System.Timeout.timeout. ```haskell timeout :: MonadUnliftIO m => Int -> m a -> m (Maybe a) timeout x y = withRunInIO $ \run -> System.Timeout.timeout x $ run y ``` -------------------------------- ### Lift IO using MonadIO Source: https://github.com/fpco/unliftio/blob/master/unliftio/README.md Standard approach for lifting simple IO actions into a monad stack using MonadIO. ```haskell myReadFile :: MonadIO m => FilePath -> m ByteString myReadFile = liftIO . readFile ``` -------------------------------- ### Implementing Race with MonadUnliftIO Source: https://github.com/fpco/unliftio/blob/master/unliftio/README.md Shows how to implement a 'race' function using MonadUnliftIO. It utilizes withRunInIO to run both monadic actions concurrently and return the first one to complete. ```haskell race :: MonadUnliftIO m => m a -> m b -> m (Either a b) race a b = withRunInIO $ \run -> A.race (run a) (run b) ``` -------------------------------- ### askUnliftIO - Capture Unlift Function Source: https://context7.com/fpco/unliftio/llms.txt The `askUnliftIO` function provides a way to capture the current monadic context and obtain an `UnliftIO` wrapper. This wrapper contains a function that can execute monadic actions in the `IO` monad, useful for scenarios where the unlift capability needs to be stored or passed around. ```APIDOC ## askUnliftIO - Capture Unlift Function ### Description `askUnliftIO` captures the current monadic context and returns an `UnliftIO` wrapper. This wrapper contains a function (`unliftIO`) that can run any monadic action `m a` as `IO a`. This is particularly useful when you need to pass the unlifting capability to other threads or store it for later use. ### Method `askUnliftIO :: MonadUnliftIO m => m (UnliftIO m)` ### Endpoint N/A (Function within a monad) ### Parameters None ### Request Example ```haskell import Control.Monad.IO.Unlift import Control.Monad.Trans.Reader import Control.Concurrent (forkIO) -- Assume Env and spawnWorker are defined as in the library's documentation data Env = Env { envName :: String } spawnWorker :: ReaderT Env IO () spawnWorker = do u <- askUnliftIO env <- ask liftIO $ do _ <- forkIO $ unliftIO u $ do name <- asks envName liftIO $ putStrLn $ "Worker started for: " ++ name return () main :: IO () main = runReaderT spawnWorker (Env "MyApp") -- Expected Output: Worker started for: MyApp ``` ### Response #### Success Response (200) N/A (This is a function within a monad, not an HTTP endpoint) #### Response Example N/A ``` -------------------------------- ### Manage Temporary Files and Directories Source: https://context7.com/fpco/unliftio/llms.txt Utilize UnliftIO.Temporary for automatic cleanup of temporary files and directories. These functions ensure resources are deleted after the provided block completes. ```haskell import UnliftIO.Temporary import UnliftIO.IO import Control.Monad.Trans.Reader import System.IO (hPutStrLn, hGetLine) data AppEnv = AppEnv { appName :: String } -- withSystemTempFile - Create temp file in system temp directory processTempFile :: ReaderT AppEnv IO String processTempFile = withSystemTempFile "myapp.txt" $ \path handle -> do env <- ask liftIO $ do hPutStrLn handle $ "App: " ++ appName env hFlush handle putStrLn $ "Temp file created at: " ++ path return path -- File is deleted after this block -- withSystemTempDirectory - Create temp directory processInTempDir :: MonadUnliftIO m => (FilePath -> m a) -> m a processInTempDir action = withSystemTempDirectory "myapp" $ \dir -> do liftIO $ putStrLn $ "Working in: " ++ dir action dir -- Directory is deleted after this block -- withTempFile - Create temp file in specific directory writeToTempFile :: MonadUnliftIO m => FilePath -> String -> m () writeToTempFile dir content = withTempFile dir "output.txt" $ \path handle -> do liftIO $ hPutStrLn handle content liftIO $ putStrLn $ "Wrote to: " ++ path main :: IO () main = do -- Temp file in system temp dir _ <- runReaderT processTempFile (AppEnv "TestApp") -- Temp directory processInTempDir $ \dir -> do writeToTempFile dir "Hello, World!" putStrLn "Cleanup complete - temp files removed" ``` -------------------------------- ### Implement MonadUnliftIO for Newtypes with wrappedWithRunInIO Source: https://context7.com/fpco/unliftio/llms.txt Illustrates using `wrappedWithRunInIO` to easily implement the `MonadUnliftIO` instance for newtype wrappers around existing `MonadUnliftIO` monads. This simplifies boilerplate code for custom monad transformers. ```haskell {-# LANGUAGE GeneralizedNewtypeDeriving #} import Control.Monad.IO.Unlift import Control.Monad.Trans.Reader -- Define a custom application monad newtype AppT m a = AppT { unAppT :: ReaderT Int m a } deriving (Functor, Applicative, Monad, MonadIO) -- Implement MonadUnliftIO using wrappedWithRunInIO instance MonadUnliftIO m => MonadUnliftIO (AppT m) where withRunInIO = wrappedWithRunInIO AppT unAppT -- Now you can use all unliftio functions with your custom monad runApp :: AppT IO a -> Int -> IO a runApp (AppT action) = runReaderT action example :: AppT IO () example = do config <- AppT ask liftIO $ putStrLn $ "Running with config: " ++ show config main :: IO () main = runApp example 42 -- Output: Running with config: 42 ``` -------------------------------- ### Unwrap ReaderT manually Source: https://github.com/fpco/unliftio/blob/master/unliftio/README.md Demonstrates manually unwrapping the ReaderT constructor to execute an IO action. ```haskell myReadFile :: FilePath -> ReaderT Env IO ByteString myReadFile fp = ReaderT $ \_env -> readFile fp ``` -------------------------------- ### MonadUnliftIO Instance for ReaderT Source: https://github.com/fpco/unliftio/blob/master/unliftio/README.md Provides a MonadUnliftIO instance for ReaderT. It captures the reader environment and recursively calls withRunInIO on the underlying monad. ```haskell instance MonadUnliftIO m => MonadUnliftIO (ReaderT r m) where withRunInIO inner = ReaderT $ \r -> withRunInIO $ \run -> inner (run . flip runReaderT r) ``` -------------------------------- ### Implement MonadUnliftIO with withRunInIO Source: https://context7.com/fpco/unliftio/llms.txt Demonstrates using `withRunInIO` to unlift a function within a `MonadUnliftIO` context. This is useful for integrating IO-bound operations like file handling into monad transformer stacks. ```haskell {-# LANGUAGE RankNTypes #-} import Control.Monad.IO.Unlift import Control.Monad.Trans.Reader import System.IO (Handle, IOMode(..), hPutStrLn) -- The typeclass definition (for reference) -- class MonadIO m => MonadUnliftIO m where -- withRunInIO :: ((forall a. m a -> IO a) -> IO b) -> m b -- Using withRunInIO to unlift a function myWithBinaryFile :: MonadUnliftIO m => FilePath -> IOMode -> (Handle -> m a) -> m a myWithBinaryFile fp mode inner = withRunInIO $ \runInIO -> withBinaryFile fp mode (\h -> runInIO (inner h)) -- Example usage in a ReaderT context data AppConfig = AppConfig { logLevel :: Int } app :: ReaderT AppConfig IO () app = do config <- ask myWithBinaryFile "output.txt" WriteMode $ \handle -> do liftIO $ hPutStrLn handle $ "Log level: " ++ show (logLevel config) return () main :: IO () main = runReaderT app (AppConfig 3) ``` -------------------------------- ### MonadUnliftIO Instance for IO Source: https://github.com/fpco/unliftio/blob/master/unliftio/README.md The base instance for IO, which does not perform any lifting or unlifting and can use 'id' directly. ```haskell instance MonadUnliftIO IO where withRunInIO inner = inner id ``` -------------------------------- ### wrappedWithRunInIO - Implement MonadUnliftIO for Newtypes Source: https://context7.com/fpco/unliftio/llms.txt The `wrappedWithRunInIO` helper function simplifies the process of creating `MonadUnliftIO` instances for newtype wrappers around existing monads that already have a `MonadUnliftIO` instance. ```APIDOC ## wrappedWithRunInIO - Implement MonadUnliftIO for Newtypes ### Description `wrappedWithRunInIO` is a utility function designed to make it easier to implement the `MonadUnliftIO` typeclass for newtype wrappers. It takes the constructor and deconstructor of the newtype and uses them to adapt the `withRunInIO` implementation from the underlying monad. ### Method `wrappedWithRunInIO :: MonadUnliftIO m => (n -> m a) -> (m a -> n) -> ((forall b. m b -> IO b) -> IO c) -> n c` ### Endpoint N/A (Helper function for typeclass instances) ### Parameters None ### Request Example ```haskell {-# LANGUAGE GeneralizedNewtypeDeriving #-} import Control.Monad.IO.Unlift import Control.Monad.Trans.Reader -- Define a custom application monad transformer stack newtype AppT m a = AppT { unAppT :: ReaderT Int m a } deriving (Functor, Applicative, Monad, MonadIO) -- Implement MonadUnliftIO using wrappedWithRunInIO instance MonadUnliftIO m => MonadUnliftIO (AppT m) where withRunInIO = wrappedWithRunInIO AppT unAppT -- Helper to run the custom monad runApp :: AppT IO a -> Int -> IO a runApp (AppT action) = runReaderT action -- Example usage demonstrating unlifting example :: AppT IO () example = do config <- AppT ask liftIO $ putStrLn $ "Running with config: " ++ show config main :: IO () main = runApp example 42 -- Expected Output: Running with config: 42 ``` ### Response #### Success Response (200) N/A (This is a helper function, not an HTTP endpoint) #### Response Example N/A ``` -------------------------------- ### MonadUnliftIO Instance for IdentityT Source: https://github.com/fpco/unliftio/blob/master/unliftio/README.md Provides a MonadUnliftIO instance for IdentityT. It unwraps the IdentityT constructor and recursively calls withRunInIO on the underlying monad. ```haskell instance MonadUnliftIO m => MonadUnliftIO (IdentityT m) where withRunInIO inner = IdentityT $ withRunInIO $ \run -> inner (run . runIdentityT) ``` -------------------------------- ### Basic IORef Operations Source: https://context7.com/fpco/unliftio/llms.txt Illustrates fundamental IORef operations: creating, reading, writing, and modifying references. Note that modifyIORef is not atomic. ```haskell import UnliftIO.IORef import UnliftIO.Async import Control.Monad (replicateM_) -- Basic IORef operations refExample :: IO () refExample = do -- Create and read ref <- newIORef (0 :: Int) initial <- readIORef ref putStrLn $ "Initial: " ++ show initial -- Write writeIORef ref 10 -- Modify (not atomic) modifyIORef ref (* 2) -- Strict modify (forces evaluation) modifyIORef' ref (+ 1) final <- readIORef ref putStrLn $ "Final: " ++ show final -- 21 ``` -------------------------------- ### Catch exceptions with `catch` Source: https://context7.com/fpco/unliftio/llms.txt Use `catch` to handle synchronous exceptions within a `MonadUnliftIO` context. Asynchronous exceptions are re-thrown. Requires `UnliftIO.Exception` import. ```haskell import UnliftIO.Exception import Control.Monad.Trans.Reader data AppEnv = AppEnv { maxRetries :: Int } -- catch - Catch and handle exceptions safeDivide :: MonadUnliftIO m => Int -> Int -> m (Either String Int) safeDivide x y = catch (return $ Right $ x `div` y) (\ (e :: SomeException) -> return $ Left $ "Error: " ++ show e) ``` -------------------------------- ### Run Concurrent Computations with Conc Source: https://context7.com/fpco/unliftio/llms.txt Use `conc` to wrap IO actions and `runConc` to execute them concurrently. This is an efficient alternative to `Concurrently` for parallelizing applicative structures. ```haskell import UnliftIO.Async import Control.Applicative ((<|>), liftA2) import Control.Concurrent (threadDelay) -- conc - Create a Conc value from an action -- runConc - Execute the concurrent computation -- Applicative style - all run concurrently fetchUserData :: IO (String, Int, [String]) fetchUserData = runConc $ (,,) <$> conc fetchName <*> conc fetchAge <*> conc fetchTags where fetchName = threadDelay 100000 >> return "Alice" fetchAge = threadDelay 100000 >> return 30 fetchTags = threadDelay 100000 >> return ["admin", "user"] ``` ```haskell -- Alternative style - first to complete wins firstAvailable :: IO String firstAvailable = runConc $ conc (threadDelay 200000 >> return "slow server") <|> conc (threadDelay 50000 >> return "fast server") ``` ```haskell -- Combining with liftA2 combineResults :: IO Int combineResults = runConc $ liftA2 (+) (conc (return 10)) (conc (return 32)) ``` ```haskell main :: IO () main = do -- All three fetches run concurrently userData <- fetchUserData print userData -- ("Alice", 30, ["admin", "user"]) -- Race between servers server <- firstAvailable putStrLn server -- "fast server" -- Combine concurrent results sum <- combineResults print sum -- 42 ``` -------------------------------- ### Guaranteed resource cleanup with `bracket` Source: https://context7.com/fpco/unliftio/llms.txt Ensures that a cleanup action is always performed after a resource acquisition action, even if the main action throws an exception. Use `bracket acquire (const release) (const action)` for resource management. ```haskell -- bracket - Resource acquisition with guaranteed cleanup withResource :: MonadUnliftIO m => m () -> m () -> m a -> m a withResource acquire release action = bracket acquire (const release) (const action) ``` -------------------------------- ### Atomic IORef Operations for Concurrency Source: https://context7.com/fpco/unliftio/llms.txt Demonstrates thread-safe IORef updates using atomicModifyIORef' for concurrent increments and atomicWriteIORef for atomic replacement. ```haskell import UnliftIO.IORef import UnliftIO.Async import Control.Monad (replicateM_) -- Atomic operations for concurrent access atomicExample :: IO () atomicExample = do counter <- newIORef (0 :: Int) -- atomicModifyIORef' for thread-safe updates let increment = atomicModifyIORef' counter $ \n -> (n + 1, n + 1) -- Concurrent increments results <- mapConcurrently (\_ -> increment) [1..1000] final <- readIORef counter putStrLn $ "Counter: " ++ show final -- 1000 putStrLn $ "Results: " ++ show (length results) ``` ```haskell import UnliftIO.IORef import UnliftIO.Async import Control.Monad (replicateM_) -- atomicWriteIORef for atomic replacement atomicWriteExample :: IO () atomicWriteExample = do ref <- newIORef "initial" atomicWriteIORef ref "updated" value <- readIORef ref putStrLn value -- "updated" ``` -------------------------------- ### Implement Timeout Operations with UnliftIO Source: https://context7.com/fpco/unliftio/llms.txt Use the timeout function to wrap actions with a time limit in any MonadUnliftIO monad. The module handles both simple timeouts and retry logic. ```haskell import UnliftIO.Timeout import UnliftIO.Exception import Control.Monad.Trans.Reader import Control.Concurrent (threadDelay) data Config = Config { requestTimeout :: Int } -- microseconds -- timeout - Run action with time limit withTimeout :: MonadUnliftIO m => Int -> m a -> m (Maybe a) withTimeout microseconds = timeout microseconds -- Example: HTTP request with timeout fetchWithTimeout :: ReaderT Config IO (Maybe String) fetchWithTimeout = do cfg <- ask timeout (requestTimeout cfg) $ do liftIO $ threadDelay 500000 -- Simulate network delay return "Response data" -- Retry with timeout retryWithTimeout :: MonadUnliftIO m => Int -> Int -> m a -> m (Maybe a) retryWithTimeout maxAttempts timeoutMicros action = go maxAttempts where go 0 = return Nothing go n = do result <- timeout timeoutMicros action case result of Just x -> return (Just x) Nothing -> go (n - 1) main :: IO () main = do -- Fast operation completes result1 <- timeout 1000000 $ return "quick" print result1 -- Just "quick" -- Slow operation times out result2 <- timeout 100000 $ threadDelay 1000000 >> return "slow" print result2 -- Nothing -- With config result3 <- runReaderT fetchWithTimeout (Config 1000000) print result3 -- Just "Response data" ``` -------------------------------- ### Conditional cleanup on exception with `onException` Source: https://context7.com/fpco/unliftio/llms.txt The `onException` function executes a cleanup action only if the main action throws an exception. It's useful for rollback scenarios where cleanup is only needed upon failure. ```haskell -- onException - Cleanup only on exception withRollback :: MonadUnliftIO m => m a -> m () -> m a withRollback action rollback = onException action rollback ``` -------------------------------- ### Ensure cleanup runs with `finally` Source: https://context7.com/fpco/unliftio/llms.txt The `finally` function guarantees that a cleanup action executes after the main action completes, regardless of whether an exception occurred. Suitable for actions that need finalization. ```haskell -- finally - Guarantee cleanup runs processFile :: ReaderT AppEnv IO () processFile = do liftIO $ putStrLn "Starting..." finally (liftIO $ putStrLn "Processing file") (liftIO $ putStrLn "Cleanup complete") ``` -------------------------------- ### Limit concurrent threads with `pooledMapConcurrentlyN` Source: https://context7.com/fpco/unliftio/llms.txt Processes a list of items concurrently, but limits the number of threads used to a specified maximum. Useful for managing resources or rate-limiting operations. Requires `UnliftIO.Async`. ```haskell import UnliftIO.Async import Control.Concurrent (myThreadId, threadDelay) -- pooledMapConcurrentlyN - Limit concurrent threads processWithLimit :: MonadUnliftIO m => Int -> [a] -> (a -> m b) -> m [b] processWithLimit maxThreads items action = pooledMapConcurrentlyN maxThreads action items ``` -------------------------------- ### Async operations with automatic cancellation using `withAsync` Source: https://context7.com/fpco/unliftio/llms.txt The `withAsync` function provides a scope for an asynchronous computation, ensuring it's cancelled automatically when the scope is exited, even if exceptions occur. Useful for managing the lifecycle of async tasks. ```haskell -- withAsync - Async with automatic cancellation on scope exit scopedAsync :: ReaderT Config IO () scopedAsync = withAsync longRunningTask $ \a -> do liftIO $ threadDelay 100000 cancel a liftIO $ putStrLn "Task cancelled" where longRunningTask = liftIO $ threadDelay 10000000 >> return () ``` -------------------------------- ### Repeat an action concurrently with a thread limit using `pooledReplicateConcurrentlyN_` Source: https://context7.com/fpco/unliftio/llms.txt Executes a given monadic action a specified number of times concurrently, while enforcing a maximum number of simultaneously running threads. The `_` suffix indicates it discards results. ```haskell -- pooledReplicateConcurrentlyN - Repeat action with thread limit runWorkers :: MonadUnliftIO m => Int -> Int -> m () -> m () runWorkers maxThreads count action = pooledReplicateConcurrentlyN_ maxThreads count action ``` -------------------------------- ### Race two actions with `race` Source: https://context7.com/fpco/unliftio/llms.txt The `race` function runs two monadic actions concurrently and returns the result of the first one to complete. The other action is cancelled. Requires `UnliftIO.Async`. ```haskell -- race - Run two actions, return first to complete raceExample :: MonadUnliftIO m => m a -> m a -> m a raceExample fast slow = do result <- race fast slow return $ either id id result ``` -------------------------------- ### Process items concurrently with `mapConcurrently` Source: https://context7.com/fpco/unliftio/llms.txt Applies a monadic action to each element of a list concurrently, returning the results in a list. Requires `UnliftIO.Async` and `MonadUnliftIO`. ```haskell -- mapConcurrently - Process items in parallel processItems :: MonadUnliftIO m => [Int] -> m [Int] processItems items = mapConcurrently (\x -> return (x * 2)) items ``` -------------------------------- ### Return exceptions as `Either` with `try` Source: https://context7.com/fpco/unliftio/llms.txt The `tryAny` function (aliased as `try` in this context) catches any synchronous exception and returns it as `Either SomeException a`. Useful for operations that might fail but should not halt execution. ```haskell -- try - Return Either instead of throwing tryOperation :: MonadUnliftIO m => m a -> m (Either SomeException a) tryOperation = tryAny ``` -------------------------------- ### MonadUnliftIO Typeclass Definition Source: https://github.com/fpco/unliftio/blob/master/unliftio/README.md Defines the MonadUnliftIO typeclass with its single method, withRunInIO. This method allows running computations in the underlying monad within the IO monad. ```haskell class MonadIO m => MonadUnliftIO m where withRunInIO :: ((forall a. m a -> IO a) -> IO b) -> m b ``` -------------------------------- ### Run two actions concurrently with `concurrently` Source: https://context7.com/fpco/unliftio/llms.txt The `concurrently` function executes two monadic actions in parallel and waits for both to complete, returning their results as a tuple. Useful for independent parallel tasks. ```haskell -- concurrently - Run two actions in parallel, wait for both fetchBoth :: MonadUnliftIO m => m a -> m b -> m (a, b) fetchBoth action1 action2 = concurrently action1 action2 ``` -------------------------------- ### Safe MVar Operations with withMVar and modifyMVar Source: https://context7.com/fpco/unliftio/llms.txt Utilize `withMVar` for safe reading and `modifyMVar` for atomic updates of `MVar` contents within any `MonadUnliftIO` context. `modifyMVar_` is used for modifications that do not require returning a value. ```haskell import UnliftIO.MVar import UnliftIO.Async import Control.Monad.Trans.Reader data Counter = Counter { count :: MVar Int } -- newMVar, takeMVar, putMVar - Basic operations createCounter :: MonadIO m => m Counter createCounter = Counter <$> newMVar 0 ``` ```haskell -- withMVar - Safely read MVar contents readCounter :: MonadUnliftIO m => Counter -> m Int readCounter (Counter var) = withMVar var return ``` ```haskell -- modifyMVar - Atomically modify MVar incrementCounter :: MonadUnliftIO m => Counter -> m Int incrementCounter (Counter var) = modifyMVar var $ \n -> return (n + 1, n + 1) ``` ```haskell -- modifyMVar_ - Modify without returning a value resetCounter :: MonadUnliftIO m => Counter -> m () resetCounter (Counter var) = modifyMVar_ var $ \_ -> return 0 ``` ```haskell -- Thread-safe counter example counterExample :: IO () counterExample = do counter <- createCounter -- Increment from multiple threads mapConcurrently_ (\_ -> incrementCounter counter) [1..100] -- Read final value final <- readCounter counter putStrLn $ "Final count: " ++ show final -- Final count: 100 main :: IO () main = counterExample ``` -------------------------------- ### Memoize Expensive Computations Source: https://context7.com/fpco/unliftio/llms.txt Use memoizeMVar for thread-safe caching of expensive action results. This ensures the action runs exactly once even under concurrent access. ```haskell import UnliftIO.Memoize import UnliftIO.Async import Control.Concurrent (threadDelay) -- memoizeRef - Single-threaded memoization (may run multiple times in concurrent access) -- memoizeMVar - Thread-safe memoization (guaranteed single execution) -- Expensive computation that should only run once expensiveComputation :: IO Int expensiveComputation = do putStrLn "Computing..." threadDelay 1000000 -- 1 second return 42 -- Thread-safe memoization example memoExample :: IO () memoExample = do -- Create memoized computation memoized <- memoizeMVar expensiveComputation -- First call - runs the computation result1 <- runMemoized memoized putStrLn $ "First call: " ++ show result1 -- Second call - uses cached result result2 <- runMemoized memoized putStrLn $ "Second call: " ++ show result2 -- Concurrent access - computation still only runs once results <- mapConcurrently (\_ -> runMemoized memoized) [1..5] putStrLn $ "Concurrent results: " ++ show results -- Lazy initialization pattern data App = App { appConfig :: Memoized Config , appDb :: Memoized Connection } data Config = Config { dbUrl :: String } deriving Show data Connection = Connection String deriving Show initApp :: IO App initApp = do config <- memoizeMVar $ do putStrLn "Loading config..." return $ Config "postgres://localhost/db" db <- memoizeMVar $ do cfg <- runMemoized config putStrLn $ "Connecting to: " ++ dbUrl cfg return $ Connection (dbUrl cfg) return $ App config db main :: IO () main = do memoExample -- Output: -- Computing... -- First call: 42 -- Second call: 42 -- Concurrent results: [42,42,42,42,42] ``` -------------------------------- ### Composable STM Transactions with TVar and atomically Source: https://context7.com/fpco/unliftio/llms.txt Leverage `TVar` for transactional variables and `atomically` to execute STM transactions. `retrySTM` is used to block until transaction conditions are met, enabling composable concurrent operations. ```haskell import UnliftIO.STM import UnliftIO.Async import Control.Monad (replicateM_) -- TVar - Transactional variable data BankAccount = BankAccount { balance :: TVar Int } newAccount :: MonadIO m => Int -> m BankAccount newAccount initial = BankAccount <$> newTVarIO initial ``` ```haskell -- atomically - Execute STM transaction getBalance :: MonadIO m => BankAccount -> m Int getBalance (BankAccount bal) = readTVarIO bal ``` ```haskell -- Compose transactions transfer :: BankAccount -> BankAccount -> Int -> STM () transfer from to amount = do fromBal <- readTVar (balance from) if fromBal >= amount then do modifyTVar' (balance from) (subtract amount) modifyTVar' (balance to) (+ amount) else retrySTM -- Retry when insufficient funds ``` ```haskell -- Safe concurrent transfers bankExample :: IO () bankExample = do alice <- newAccount 1000 bob <- newAccount 500 -- Concurrent transfers concurrently_ (replicateM_ 10 $ atomically $ transfer alice bob 50) (replicateM_ 10 $ atomically $ transfer bob alice 30) aliceBal <- getBalance alice bobBal <- getBalance bob putStrLn $ "Alice: " ++ show aliceBal ++ ", Bob: " ++ show bobBal -- Total should still be 1500 main :: IO () main = bankExample ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.