这是一个noob问题。
我想写一个提供懒惰图像流的函数,大概是这样的:
imageStream :: [IO Image]
不幸的是,读取图像的功能可能会失败,所以它看起来像:
readImage :: IO (Maybe Image)
因此,我可以编写的函数如下:
maybeImageStream :: [IO (Maybe Image)]
如何在保持懒惰IO的同时实现如下功能?
flattenImageStream :: [IO (Maybe Image)] -> [IO Image]
从语义上讲,当您向flattenImageStream
询问下一张图像时,它应该遍历列表并尝试读取每张图像。它会在找到加载并返回的图像之前执行此操作。
sequence
的解决方案,但我很确定我测试了它并发现它会破坏懒惰。
(我会再次测试它,以确保我何时回到计算机上。)
有人还建议使用unsafeInterleaveIO
。
从该函数的文档来看,它似乎可行,但显然我想尽可能地尊重类型系统。
答案 0 :(得分:9)
您可以使用ListT
中的pipes
,它提供了一种比懒惰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
library,那么您甚至可以懒惰地流式传输目录内容。
仅筛选出成功结果的函数将具有以下类型:
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
我也在考虑为fail
添加ListT
定义,以便您可以写一下:
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
答案 1 :(得分:1)
如建议你可以使用序列<[p]将[m a]变成m [a]
所以你得到:
imageStream :: IO [Image]
然后您可以使用Data.Maybe中的cayMaybes来保留Just值:
catMaybes `liftM` imageStream
答案 2 :(得分:0)
按要求实现这个似乎需要知道IO monad之外是否IO内的值是Nothing
,并且IO旨在防止其值“泄漏”到外部纯功能世界(尽管unsafePerformIO
),这是不可能的。相反,我建议生成IO [Image]
:使用sequence
将[IO (Maybe Image)]
转换为IO [Maybe Image]
,然后在IO monad中使用Data.Maybe.catMaybes
(例如,使用fmap
{1}}或liftM
)转换为IO [Image]
,例如:
flattenImageStream = fmap catMaybes $ sequence maybeImageStream
答案 3 :(得分:0)
我认为这些其他答案中的任何一个都没有完全符合您的要求。因为我非常确定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
但你所做的有点奇怪。如果您的文件路径错误怎么办?如果图像根本无法加载怎么办?您将尝试永远加载图像。您应该多次尝试在放弃之前加载图像。