IO上的mapM产生无限输出



这是一种奇怪的行为,即使对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的输出是什么?在读取一些文件时,它看起来像是一个无限循环。还有可能将listDirectorydo箭头线与预先结束路径的map (dir </>)组合在一行中吗?

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

它不是一个无限循环,只是一个非常长的循环。

您没有将mapM用于IO;您在非确定性monad中使用mapM。这是mapM的类型,专门用于monad:

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

通过以下方式阅读:

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

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

> 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的长度完全相同。让我们关注集合中的一个列表;称之为CCD_ 18。

cs的第一个元素将从dir </> filename中提取,其中filenamelsfiles的第一个单元;也就是说,它将是dir中的一个字符,或者是斜线,或者是filename中的某个字符。cs的第二个元素将类似:dir的一个字符,或斜线,或lsfiles中第二个文件名的一个字母。我想你可以看到这是怎么回事。。。这里有很多可能性

是否可以将listDirectorydo箭头线与预先结束路径的map (dir </>)组合在一行中?

是:

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

根据文件,

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执行一个mondic映射,在本例中,对于类型为Char的值,它是"非确定性"monad。

为了补充Jorge的答案,这里有一个指数放大,演示了:

> 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个组合。(哎哟!(