2012-06-08 27 views
8

Tôi muốn bắt một ngoại lệ bên trong runResourceT mà không giải phóng các nguồn lực, nhưng chức năng bắt chạy tính toán bên trong IO. Có cách nào để bắt ngoại lệ bên trong runResourceT hoặc cách được đề xuất để cấu trúc lại mã?Làm thế nào để bắt một ngoại lệ bên trong runResourceT

Cảm ơn sự giúp đỡ của bạn.

{-# LANGUAGE FlexibleContexts #-} 

module Main where 

import Control.Exception as EX 
import Control.Monad.IO.Class 
import Control.Monad.Trans.Resource 

type Resource = String 

allocResource :: IO Resource 
allocResource = let r = "Resource" 
        in putStrLn (r ++ " opened.") >> return r 

closeResource :: Resource -> IO() 
closeResource r = putStrLn $ r ++ " closed." 

withResource :: (MonadIO m 
       , MonadBaseControl IO m 
       , MonadThrow m 
       , MonadUnsafeIO m 
       ) => (Resource -> ResourceT m a) -> m a 
withResource f = runResourceT $ do 
    (_, r) <- allocate allocResource closeResource 
    f r 

useResource :: (MonadIO m 
       , MonadBaseControl IO m 
       , MonadThrow m 
       , MonadUnsafeIO m 
       ) => Resource -> ResourceT m Int 
useResource r = liftIO $ putStrLn ("Using " ++ r) >> return 1 

main :: IO() 
main = do 
    putStrLn "Start..." 

    withResource $ \r -> do 

    x <- useResource r 

    {-- This does not compile as the catch computation runs inside IO 
    y <- liftIO $ EX.catch (useResource r) 
          (\e -> do putStrLn $ show (e::SomeException) 
            return 0) 
    --} 

    return() 

    putStrLn "Done." 

Trả lời

8

ResourceT là một thể hiện của MonadBaseControl từ monad-control gói, được thiết kế để nâng cấu trúc điều khiển như forkIOcatch vào monads chuyển.

Gói lifted-base, được xây dựng dựa trên điều khiển đơn nguyên, chứa các mô-đun có phiên bản cấu trúc điều khiển tiêu chuẩn hoạt động trong bất kỳ MonadBaseControl nào. Để xử lý ngoại lệ, bạn có thể sử dụng các chức năng trong mô-đun Control.Exception.Lifted. Do đó, chỉ cần import qualified Control.Exception.Lifted as EX và mã của bạn sẽ hoạt động tốt.

Lưu ý qualified tại đây; khá khó hiểu, import A as B thực sự nhập tất cả các định nghĩa trong A vào phạm vi và chỉ cần xác định B làm bí danh cho mô-đun! Bạn cần sử dụng qualified để đảm bảo rằng các định nghĩa không được đưa vào phạm vi và thay vào đó được truy cập độc quyền thông qua bí danh B.

+0

Cảm ơn bạn đã trả lời và bình luận của bạn. Nó hoạt động như mong đợi và giúp tôi hiểu rõ hơn về máy biến áp monads. – jedf

0

Là phương pháp thay thế, bạn có thể sử dụng ví dụ MonadCatch của ResourceT, được tìm thấy trong gói exceptions. Bạn chỉ cần thay thế các phiên bản khái quát hóa của catch từ Control.Monad.Catch:

import Control.Monad.Catch 
… 
main = do 
    … 
    withResource $ \r -> do 
    … 
    y <- Control.Monad.Catch.catch (useResource r) (\e -> …) 
Các vấn đề liên quan