这是一个菜鸟问题.
我想写一个提供懒惰图像流的函数,大概是这样的:
imageStream :: [IO Image]
Run Code Online (Sandbox Code Playgroud)
不幸的是,读取图像的功能可能会失败,所以它看起来像:
readImage :: IO (Maybe Image)
Run Code Online (Sandbox Code Playgroud)
所以,我能写的功能如下:
maybeImageStream :: [IO (Maybe Image)]
Run Code Online (Sandbox Code Playgroud)
如何在保持惰性IO的同时实现如下功能?
flattenImageStream :: [IO (Maybe Image)] -> [IO Image]
Run Code Online (Sandbox Code Playgroud)
从语义上讲,当您要求flattenImageStream下一个图像时,它应该遍历列表并尝试读取每个图像.它会在找到加载并返回的图像之前执行此操作.
编辑:答案中似乎存在一些分歧.有些人建议使用的解决方案sequence,但我很确定我测试了它,并发现它破坏了懒惰.(我会再次测试它,以确保我何时回到我的电脑.)有人还建议使用unsafeInterleaveIO.从该函数的文档来看,它似乎可行,但显然我想尽可能地尊重类型系统.
你可以使用ListTfrom pipes,它提供了一种更安全的替代方法,IO在这种情况下做了正确的事情.
您为潜在失败图像的懒惰流建模的方式是:
imageStream :: ListT IO (Maybe Image)
Run Code Online (Sandbox Code Playgroud)
假设你有一些类型的图像加载函数:
loadImage :: FileName -> IO (Maybe Image)
Run Code Online (Sandbox Code Playgroud)
..那么你构建这样一个流的方式将是这样的:
imageStream = do
fileName <- Select $ each ["file1.jpg", "file2.jpg", "file3.jpg"]
lift $ loadImage fileName
Run Code Online (Sandbox Code Playgroud)
如果您使用该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
Run Code Online (Sandbox Code Playgroud)
请注意,此函数适用于任何基础monad m.它没什么IO特别的.它也保留了懒惰!
应用flattenImage到imageStream,为我们提供了类型的东西:
finalStream :: List IO Image
finalStream = flattenImage imageStream
Run Code Online (Sandbox Code Playgroud)
现在让我们说你有一些功能消耗这些类型的图像:
useImage :: Image -> IO ()
Run Code Online (Sandbox Code Playgroud)
如果你想ListT使用该useImage函数处理final ,你只需写:
main = runEffect $
for (every finalStream) $ \image -> do
lift $ useImage image
Run Code Online (Sandbox Code Playgroud)
那将懒洋洋地消耗图像流.
当然,你也可以玩代码高尔夫并将所有这些组合成以下更短的版本:
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
Run Code Online (Sandbox Code Playgroud)
我也在考虑添加一个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
Run Code Online (Sandbox Code Playgroud)