我是Haskell和FP的新手,所以这个问题可能看起来很愚蠢.
我的main函数中有一行代码
let y = map readFile directoryContents
Run Code Online (Sandbox Code Playgroud)
其中directoryContents的类型[FilePath].这反过来(我认为)使y类型[IO String],所以一个字符串列表 - 每个字符串包含每个文件的内容directoryContents.
我有一个函数写在另一个模块中工作[String],String但我现在不清楚如何调用/使用它们因为y是类型[IO String].有什么指针吗?
编辑:
有人向我建议我要用mapM而不是map,所以:
let y = mapM readFile directoryContents,y现在是类型IO [String],我该怎么做?
Dav*_*vid 23
你是对的,类型是y :: [IO String].
那么,这里主要有两个部分:
[IO String]是一个IO动作列表,我们需要的是一个带有字符串列表的IO动作(即IO [String]).幸运的是,功能序列提供了我们所需要的:
sequence :: Monad m => [m a] -> m [a]
y' = sequence y :: IO [String]
Run Code Online (Sandbox Code Playgroud)
现在该mapM函数可以简化这一点,我们可以重写y'为:
y' = mapM readFile directoryContents
Run Code Online (Sandbox Code Playgroud)
mapM 为我们做的顺序.
我们的类型现在IO [String],所以现在的问题是"我们如何从IO中获取[String]?" 这就是函数>>=(bind)的作用:
(>>=) :: Monad m => m a -> (a -> m b) -> m b
-- Specialized to IO, that type is:
(>>=) :: IO a -> (a -> IO b) -> IO b
Run Code Online (Sandbox Code Playgroud)
我们还有一个return :: Monad m => a -> m a可以将值"放入"的功能IO.
所以有了这两个函数,如果我们有一些函数f :: [String] -> SomeType,我们可以写:
ourResult = y' >>= (\theStringList -> return (f theStringList)) :: IO SomeType
Run Code Online (Sandbox Code Playgroud)
功能可以与>>=功能一起"链接" .这有时可能有点难以理解,因此Haskell提供do了使视觉上更简单的符号:
ourResult = do
theStringList <- y'
return $ f theStringList
Run Code Online (Sandbox Code Playgroud)
编译器在内部将其转换为y' >>= (\theStringList -> f theStringList),这与y' >>= f我们之前的相同.
我们可能实际上并不想要y'漂浮,所以我们可以消除它并达到:
ourResult = do
theStringList <- mapM readFile directoryContents
return $ f theStringList
Run Code Online (Sandbox Code Playgroud)
事实证明,这实际上并不需要全部的力量>>=.事实上,我们所需要的只是fmap!这是因为函数f只有一个参数"内部",IO我们没有使用任何其他先前的IO结果:我们正在制作一个结果,然后立即使用它.
使用法律
fmap f xs == xs >>= return . f
Run Code Online (Sandbox Code Playgroud)
我们可以重写>>=代码来使用这样的fmap:
ourResult = fmap f (mapM readFile directoryContents)
Run Code Online (Sandbox Code Playgroud)
如果我们想要更简洁,有一个fmap名为的中缀同义词<$>:
ourResult = f <$> mapM readFile directoryContents
Run Code Online (Sandbox Code Playgroud)