[英]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
(例如,使用fmap
或liftM
)轉換為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.