Haskell:隐藏懒惰IO中的失败

emc*_*sen 6 haskell lazy-io

这是一个菜鸟问题.

我想写一个提供懒惰图像流的函数,大概是这样的:

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.从该函数的文档来看,它似乎可行,但显然我想尽可能地尊重类型系统.

Gab*_*lez 9

你可以使用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特别的.它也保留了懒惰!

应用flattenImageimageStream,为我们提供了类型的东西:

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)