这段代码读取目录并将所有.ini文件名放在列表中。然后它通过列表读取每个文件,解析它,并将解析的结果和文件名放在一个映射中(Key = filename,contents = parsed result - Config)。我的问题是如何让这一行rslt <- parseFromFile parseIni fn
符合getIni
的类型签名? parseFromFile
具有不适合的类型签名monad m => m (Maybe Config)
。或者有人可以建议更好的编码方式吗?我这里只包含了相关代码。相关功能包括main
和getIni
。
import System.FilePath.Glob (globDir, compile)
import Control.Monad.IO.Class
import Data.Map (Map)
import qualified Data.Map as M
import Data.ByteString (ByteString)
import Control.Applicative
import Text.Trifecta
fPath = "c:/users/tyrone/myprojects/chp29"
type FileName = String
type Name = String
type Value = String
type Assignments = Map Name Value
newtype Header = Header String deriving (Eq, Ord, Show)
data Section = Section Header Assignments deriving (Eq, Show)
newtype Config = Config (Map Header Assignments) deriving (Eq, Show)
parseIni :: Parser Config
parseIni = do
sections <- some parseSection
let mapOfSections = foldr rollup M.empty sections
return $ Config mapOfSections
getIni :: FileName -> Map FileName Config -> Map FileName Config
getIni fn mp = do
rslt <- parseFromFile parseIni fn
case rslt of
Nothing -> M.empty
Just confg -> do let ky = tail $ dropWhile (/= '\\') fn
M.insert ky confg mp
main :: IO ()
main = do
iniF <- (concat . fst) <$> globDir [compile "*.ini"] fPath
print $ foldr getIni M.empty iniF
答案 0 :(得分:1)
表达式parseFromFile
可能具有类型IO (Maybe Config)
(我基于它需要执行IO来读取文件内容,以及您的案例表达式匹配的事实Just and Nothing),所以如果您要在getIni
中使用它,那么它还需要返回IO
中的类型,可能类似于IO (Map FileName Config)
。这会使getIni
看起来更像
getIni :: FileName -> Map FileName Config -> IO (Map FileName Config)
getIni fn mp = do
rslt <- parseFromFile parseIni fn
case rslt of
Nothing -> return M.empty
Just confg -> do
let ky = tail $ dropWhile (/= '\\') fn
return $ M.insert ky confg mp
然后要在main
中使用它,你必须使用monadic折叠而不是纯foldr
。此外,您可能希望使用Nothing -> return mp
,因为解析错误会消除您已经解析过的任何文件。
或者,您可以构建Map FileName String
将文件名映射到内容,然后您可以使用M.map
和M.filter
之类的纯函数将其转换为Map FileName Config
。 Data.Map
中的某些功能甚至允许您执行
parseIniFiles :: Map FileName String -> (Map FileName ParseError, Map FileName Config)
parseIniFiles contentMap = M.mapEither (parse parseIni "ini") contentMap
您的main
看起来像
main = do
iniF <- (concat . fst) <$> globDir [compile "*.ini"] fPath
contentMap <- M.fromList <$> do
contents <- mapM readFile iniF
-- As an aside, there is a better way to get the stem of the file path
-- Go look for a function that will split file paths into their components
return (tail $ dropWhile (/= '\\') fn, contents)
let (invalidFiles, validFiles) = parseIniFiles contentMap
putStrLn "Valid files"
print validFiles
putStrLn "Invalid Files"
print invalidFiles
(免责声明:我还没有编译任何此代码,但它应该给你一般的想法)