IO上的mapM产生无限的输出

时间:2018-12-14 20:57:27

标签: haskell

即使对于Haskell,这也是一种奇怪的行为。查看下面的代码段:

import System.Directory
import System.FilePath

-- This spins infinitely
loadCtx :: FilePath -> IO ()
loadCtx dir = do
    lsfiles <- listDirectory dir
    let files = mapM (dir </>) lsfiles
    putStrLn $ "Files " ++ show files

-- This does what I'd expect, prepending the dir path to each file
loadCtx dir = do
    lsfiles <- listDirectory dir
    let files = map (dir </>) lsfiles
    putStrLn $ "Files " ++ show files

两个定义都可以从类型检查器中接受,但完全可以给出 不同的行为。第一个mapM的输出是什么?读取某些文件时看起来像是一个无限循环。还可以将listDirectory的do-arrow线和放在路径前的map (dir </>)组成一行吗?

3 个答案:

答案 0 :(得分:6)

  

第一个mapM的输出是什么?在读取某些文件时看起来像是一个无限循环。

这不是无限循环-只是一个非常非常长的循环。

您没有将mapM用于IO;您在不确定性monad中使用mapM。这是专门针对该单子的mapM的类型:

Traversable t => (a -> [b]) -> t a -> [t b]

通过以下方式阅读:

  • 首先,给我一种将容器元素(类型a)转换为许多可能的替换元素(类型[b])中不确定性选择的方法。
  • 然后给我一个包含元素的容器(类型t a)。
  • 我将为您提供其中包含替换元素的容器(类型[t b])的不确定性选择。 (而且,这部分不是类型,而是:我将采用所有可能的组合的方式;对于容器中的每个位置,我将尝试每种可能的b,并为您提供每种对容器中每个位置进行选择的一种方法。)

例如,如果我们要定义函数f :: Int -> [Char]f n在字母表的前n个字母之间不确定地选择,那么我们可以看到这种交互: / p>

> f 3
"abc"
> f 5
"abcde"
> f 2
"ab"
> mapM f [3,5,2]
["aaa","aab","aba","abb","aca","acb","ada","adb","aea","aeb","baa","bab","bba","bbb","bca","bcb","bda","bdb","bea","beb","caa","cab","cba","cbb","cca","ccb","cda","cdb","cea","ceb"]

在每个结果中,第一个字母是字母表中前三个字母(a,b或c)之一;第二个来自前五个,第三个来自前两个。更重要的是,我们获得了具有此属性的每个列表。

现在让我们考虑一下这对您的代码意味着什么。你写了

mapM (dir </>) lsfiles

,因此您将获得的是列表的集合。集合中的每个列表将与lsfiles一样长。让我们关注集合中的列表之一;称为cs

cs的第一个元素将从dir </> filename中提取,其中filenamelsfiles的第一个元素;也就是说,它将是dir中的一个字符,或者是一个斜线,或者是filename中的一个字符。 cs的第二个元素将是相似的:dir的字符之一或斜杠,或lsfiles中第二个文件名中的字符之一。我想您可以看到它的发展方向……这里有很多可能性。 =)

  

是否还可以将listDirectory的do-arrow行与预先放置路径的map (dir </>)组成一行?

是:

loadCtx dir = do
    files <- map (dir </>) <$> listDirectory dir
    putStrLn $ "Files " ++ show files

答案 1 :(得分:4)

根据documentation

type FilePath = String 

type FilePath = [Char]

所以在这一行中

let files = mapM (dir </>) lsfiles

您拥有mapM的自变量(dir </>)的类型为FilePath -> FilePath。现在查看mapM的类型

mapM :: (Traversable t, Monad m) => (a -> m b) -> t a -> m (t b)
                                         ^^^^^

因此类型a -> m b实例化为FilePath -> FilePath,即FilePath -> [Char]。因此,您正在使用列表monad执行单子映射,在这种情况下,列表类型Char是“不确定性”单子。

答案 2 :(得分:3)

为补充豪尔赫的答案,以下是指数爆炸,演示:

> map ("XY" </>) ["a","b","c"]
["XY\\a","XY\\b","XY\\c"]
> mapM ("XY" </>) ["a","b","c"]
["XXX","XXY","XX\\","XXc","XYX","XYY","XY\\","XYc","X\\X","X\\Y","X\\\\",
 "X\\c","XbX","XbY","Xb\\","Xbc","YXX","YXY","YX\\","YXc","YYX","YYY","YY\\","YYc",
 "Y\\X","Y\\Y","Y\\\\","Y\\c","YbX","YbY","Yb\\","Ybc","\\XX","\\XY","\\X\\",
 "\\Xc","\\YX","\\YY","\\Y\\","\\Yc","\\\\X","\\\\Y","\\\\\\","\\\\c","\\bX",
 "\\bY","\\b\\","\\bc","aXX","aXY","aX\\","aXc","aYX","aYY","aY\\","aYc","a\\X",
 "a\\Y","a\\\\","a\\c","abX","abY","ab\\","abc"]

实际上,列表monad中的mapM = sequence . mapsequence执行列表的笛卡尔积,在这种情况下为["XY\\a","XY\\b","XY\\c"],因此得到4 * 4 * 4组合。 (哦!)