批量修改文件Haskell

Batch modify files Haskell

我正在尝试创建一个程序,向目录(及其子目录)中的每个文件添加一个简单的文本 header。我需要使用 ghc 的内置函数来完成此操作(我无法访问 cabal)。

函数的类型签名是

getRecursiveContents :: FilePath -> IO [FilePath]

addHeaderToFile :: String -> FilePath -> IO ()

这两个函数都可以独立工作,但由于类型的原因,我很难结合使用这两个函数。我假设使用地图是执行此操作的正确方法,但到目前为止我还没有成功。

addHeaderToMultiple :: String -> IO [FilePath] -> IO ()
addHeaderToMultiple header files = map (addHeaderToFile header) files

我知道由于使用的类型,这将无法正常工作,但我还没有找到修复它的方法。

从 运行 生成文件列表的 IO 操作开始:

addHeaderToMultiple :: String -> IO [FilePath] -> IO ()
addHeaderToMultiple header files = do
   -- files has type IO [FilePath]
   paths <- files
   -- paths has type [FilePath], so we can map over that
   map (addHeaderToFile header) paths
   -- this produces [IO ()], which is not IO ()

我们在最后一行遇到了一个问题,它构建了一个 IO 操作列表而不是 运行 它们。我们可以使用辅助函数:

runInSequence :: [IO ()] -> IO ()
runInSequence []     = return ()  -- nothing to do
runInSequence (a:as) = a >> runInSequence as

可以简化为

runInSequence = foldr (>>) (return ())

实际上,已经存在一个库函数可以做到这一点:它被称为 sequence_。我们的代码现在变成了

import Control.Monad
addHeaderToMultiple header files = do
   paths <- files
   sequence_ (map (addHeaderToFile header) paths)
   -- this produces IO (), so it's OK

组合 sequence_ (map ... 也有自己的库函数,称为 mapM_:

addHeaderToMultiple header files = do
   paths <- files
   mapM_ (addHeaderToFile header) paths

这可以直接使用 >>= 进一步细化

addHeaderToMultiple header files =
   files >>= mapM_ (addHeaderToFile header)