即使对于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 </>)
组成一行吗?
答案 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
中提取,其中filename
是lsfiles
的第一个元素;也就是说,它将是dir
中的一个字符,或者是一个斜线,或者是filename
中的一个字符。 cs
的第二个元素将是相似的:dir
的字符之一或斜杠,或lsfiles
中第二个文件名中的字符之一。我想您可以看到它的发展方向……这里有很多可能性。 =)
是否还可以将
listDirectory
的do-arrow行与预先放置路径的map (dir </>)
组成一行?
是:
loadCtx dir = do
files <- map (dir </>) <$> listDirectory dir
putStrLn $ "Files " ++ show files
答案 1 :(得分:4)
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 . map
和sequence
执行列表的笛卡尔积,在这种情况下为["XY\\a","XY\\b","XY\\c"]
,因此得到4 * 4 * 4组合。 (哦!)