这是一种奇怪的行为,即使对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箭头线与预先结束路径的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
中提取,其中filename
是lsfiles
的第一个单元;也就是说,它将是dir
中的一个字符,或者是斜线,或者是filename
中的某个字符。cs
的第二个元素将类似:dir
的一个字符,或斜线,或lsfiles
中第二个文件名的一个字母。我想你可以看到这是怎么回事。。。这里有很多可能性
是否可以将
listDirectory
do箭头线与预先结束路径的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 . map
和sequence
执行列表列表的笛卡尔乘积,在本例中为["XY\a","XY\b","XY\c"]
,因此我们得到4*4*4个组合。(哎哟!(