我是Haskell和FP的新手,所以这个问题可能看起来很傻。
我的主要功能
中有一行代码let y = map readFile directoryContents
其中directoryContents的类型为[FilePath]
。这反过来(我认为)使y类型[IO String]
,所以一个字符串列表 - 每个字符串包含directoryContents
中每个文件的内容。
我有一个函数写在另一个模块中,可用于[String]
和String
,但我现在还不清楚如何调用/使用它们,因为y的类型为[IO String]
。有什么指针吗?
修改
有人建议我使用mapM
代替map
,所以:
let y = mapM readFile directoryContents
,y
现在是IO [String]
,我该怎么做?
答案 0 :(得分:22)
你是对的,类型是y :: [IO String]
。
嗯,这里主要有两个部分:
[IO String]
是IO
个动作的列表,我们需要的是一个带有字符串列表的IO动作(即IO [String]
)。幸运的是,函数序列提供了我们所需要的:
sequence :: Monad m => [m a] -> m [a]
y' = sequence y :: IO [String]
现在mapM
函数可以简化此操作,我们可以将y'
重写为:
y' = mapM readFile directoryContents
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
我们还有一个函数return :: Monad m => a -> m a
,它可以将值“加入”IO
。
所以使用这两个函数,如果我们有一些函数f :: [String] -> SomeType
,我们可以写:
ourResult = y' >>= (\theStringList -> return (f theStringList)) :: IO SomeType
函数可以与>>=
函数一起“链接”。这有时可能有点难以理解,因此Haskell提供了do
符号,使视觉上更简单:
ourResult = do
theStringList <- y'
return $ f theStringList
编译器在内部将其转换为y' >>= (\theStringList -> f theStringList)
,这与我们之前的y' >>= f
相同。
我们可能实际上并不希望y'
浮动,所以我们可以消除它并到达:
ourResult = do
theStringList <- mapM readFile directoryContents
return $ f theStringList
事实证明,这实际上并不需要>>=
的全部力量。事实上,我们所需要的只是fmap
!这是因为函数f
在IO
内部只有一个参数,我们没有使用任何其他先前的IO
结果:我们正在制作一个结果,然后立即使用它。
使用法律
fmap f xs == xs >>= return . f
我们可以重写>>=
代码来使用这样的fmap:
ourResult = fmap f (mapM readFile directoryContents)
如果我们想要更加简洁,那么fmap
有<$>
的中缀同义词:
ourResult = f <$> mapM readFile directoryContents