带递归的 Monad Transformer

Monad Transformer with recursion

当我想载入一个 bmp 文件时,我用:

loadBMP :: String -> IO Picture

如果不确定给定路径是否存在,可以执行以下操作:

saveLoad :: String -> IO Picture
saveLoad str = do
   b <- doesFileExist str
   if b then loadBMP str
        else return Blank

另一种方式也可以;使用 monad 转换器:

saveLoadM :: String -> MaybeT IO Picture
saveLoadM s = do
    b <- lift $ doesFileExist s
    if  b   then do
                p <- lift$ loadBMP s
                return p
            else MaybeT $ return Nothing

但是您将如何处理文件列表?

test ::[String] -> ListT IO Picture
test [] = ListT $ return []
test (x:xs) = do

    b <- lift $ doesFileExist x
    if  b   then do
                p <- lift$ loadBMP x
                return p    -- would yield a simple [p] 
                -- therefore:
                p : test xs -- wrong -> not working
            else test xs

您可以使用 the witherable package,像这样:

import Witherable

test :: [String] -> IO [Picture]
test = wither $ \x -> do
    b <- doesFileExist x
    if b then do
             p <- loadBMP x
             return $ Just p
         else return Nothing

为了直观地了解 wither 的含义,想象一下如果它被命名为 mapMaybeM

你只需要 fmap.

test :: [String] -> IO [Picture]
-- this bit as before...
    if b then do
        p <- loadBMP x
        (p:) <$> test xs
    else test xs

但是你不应该这样使用doesFileExist。相反,加载位图并在文件不存在时捕获异常;这可以防止竞争条件。

test (x:xs) = do
    imgE <- try (loadBMP x)
    case imgE of
        Right img -> (img:) <$> test xs
        Left err | isDoesNotExistError err -> test xs
                 | otherwise -> throwIO err -- reraise other exceptions

如果您不介意扩展,我觉得匿名案例很适合这里。

test (x:xs) = try (loadBMP x) >>= \case
    Right img -> ...
    Left err | ...
             | ...