即使对于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
Run Code Online (Sandbox Code Playgroud)
两种定义都可以从类型检查器中接受,但给出完全不同的行为.第一个是什么输出mapM?它看起来像是读取一些文件的无限循环.也可以用listDirectory一行中的前导线组成do-arrow map (dir </>)线?
第一个是什么输出
mapM?它看起来像是读取一些文件的无限循环.
它不是一个无限循环 - 只是一个非常非常长的循环.
您未使用mapM的IO; 你mapM在非确定性monad 中使用.这是mapMmonad专用的类型:
Traversable t => (a -> [b]) -> t a -> [t b]
Run Code Online (Sandbox Code Playgroud)
请按以下方式阅读:
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"]
Run Code Online (Sandbox Code Playgroud)
在每个结果中,第一个字母是字母表中前三个字母之一(a,b或c); 第二个来自前五个,第三个来自前两个.更重要的是,我们得到每个具有此属性的列表.
现在让我们考虑一下这对您的代码意味着什么.你写
mapM (dir </>) lsfiles
Run Code Online (Sandbox Code Playgroud)
所以你会得到的是一系列清单.集合中的每个列表都将完全一样长lsfiles.让我们关注集合中的一个列表; 叫它cs.
cs将从中抽出第一个元素dir </> filename,其中filename第一个元素是lsfiles; 也就是说,它将是其中一个字符dir,或斜杠,或其中一个字符filename.第二个元素cs将是相似的:其中一个字符dir,或斜杠,或者第二个文件名中的一个字符lsfiles.我想你可以看到它的发展方向......这里有很多可能性.=)
也可以用
listDirectory一行中的前导线组成do-arrowmap (dir </>)线?
是:
loadCtx dir = do
files <- map (dir </>) <$> listDirectory dir
putStrLn $ "Files " ++ show files
Run Code Online (Sandbox Code Playgroud)