簡體   English   中英

Haskell:隱藏懶惰IO中的失敗

[英]Haskell: Hiding failures in lazy IO

這是一個菜鳥問題。

我想寫一個提供懶惰圖像流的函數,大概是這樣的:

imageStream :: [IO Image]

不幸的是,讀取圖像的功能可能會失敗,所以它看起來像:

readImage :: IO (Maybe Image)

所以,我寫的功能如下:

maybeImageStream :: [IO (Maybe Image)]

如何在保持惰性IO的同時實現如下功能?

flattenImageStream :: [IO (Maybe Image)] -> [IO Image]

從語義flattenImageStream ,當您向flattenImageStream請求下一個圖像時,它應該遍歷列表並嘗試讀取每個圖像。 它會在找到加載並返回的圖像之前執行此操作。

編輯:答案中似乎存在一些分歧。 有些人建議使用sequence解決方案,但我很確定我測試了它並發現它破壞了懶惰。 (我會再次測試它,以確保當我回到我的電腦時。)有人還建議使用unsafeInterleaveIO 從該函數的文檔來看,它似乎可行,但顯然我想盡可能地尊重類型系統。

您可以在pipes使用ListT ,這為懶惰IO提供了一種更安全的替代方案,在這種情況下可以正常運行。

您為潛在失敗圖像的懶惰流建模的方式是:

imageStream :: ListT IO (Maybe Image)

假設你有一些類型的圖像加載函數:

loadImage :: FileName -> IO (Maybe Image)

..那么你構建這樣一個流的方式將是這樣的:

imageStream = do
    fileName <- Select $ each ["file1.jpg", "file2.jpg", "file3.jpg"]
    lift $ loadImage fileName

如果您使用dirstream ,那么您甚至可以懶惰地流式傳輸目錄內容。

僅篩選出成功結果的函數將具有以下類型:

flattenImageStream :: (Monad m) => ListT m (Maybe a) -> ListT m a
flattenImageStream stream = do
    ma <- stream
    case ma of
        Just a  -> return a
        Nothing -> mzero

請注意,此函數適用於任何基本monad, m 沒有什么IO特定的。 它也保留了懶惰!

flattenImage應用於imageStream ,給我們一些類型:

finalStream :: List IO Image
finalStream = flattenImage imageStream

現在讓我們說你有一些功能消耗這些類型的圖像:

useImage :: Image -> IO ()

如果你要處理的最終ListT使用useImage功能,你只寫:

main = runEffect $
    for (every finalStream) $ \image -> do
        lift $ useImage image

那將懶洋洋地消耗圖像流。

當然,你也可以玩代碼高爾夫並將所有這些組合成以下更短的版本:

main = runEffect $ for (every image) (lift . useImage)
  where
    image = do
        fileName   <- Select $ each ["file1.jpg", "file2.jpg", "file3.jpg"]
        maybeImage <- lift $ loadImage fileName           
        case maybeImage of
            Just img -> return img
            Nothing  -> mzero

我也在考慮為ListT添加一個fail定義,這樣你就可以寫:

main = runEffect $ for (every image) (lift . useImage)
  where
    image = do
        fileName <- Select $ each ["file1.jpg", "file2.jpg", "file3.jpg"]
        Just img <- lift $ loadImage fileName           
        return img

如你所知,你可以使用序列將[ma]變成m [a]

所以你得到:

imageStream :: IO [Image]

然后你可以使用Data.Maybe中的cayMaybes來保持Just值:

catMaybes `liftM` imageStream

按照要求實現這一點似乎需要知道IO monad之外IO內部的值是否為Nothing ,並且IO旨在防止其值“泄漏”到外部純函數世界(盡管unsafePerformIO ),這樣做不可能。 相反,我建議生成IO [Image] :使用sequence[IO (Maybe Image)]IO [Maybe Image] ,然后在IO monad中使用Data.Maybe.catMaybes (例如,使用fmapliftM )轉換為IO [Image] ,例如:

flattenImageStream = fmap catMaybes $ sequence maybeImageStream

我認為這些其他答案中的任何一個都沒有完全符合您的要求。 因為我很確定catMaybes會跳過圖像而不是試圖重新加載它。 如果您想繼續嘗試重新加載圖片,請試試這個。

flattenImageStream :: [IO (Maybe Image)] -> IO [Image]
flattenImageStream xs = mapM untilSuc xs

untilSuc :: IO (Maybe a) -> IO a
untilSuc f = do
   res <- f
   case res of
      Nothing -> untilSuc f
      Just i  -> return i 

但你所做的有點奇怪。 如果您的文件路徑錯誤怎么辦? 如果圖像根本無法加載怎么辦? 您將嘗試永遠加載圖像。 您可能應該多次嘗試在圖像放棄之前加載圖像。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM