2017-04-20 86 views
0

这段代码读取一个目录并将所有.ini文件名放入列表中。然后它通过读取每个文件的列表,解析它,并将解析的结果和文件名放置在一个映射(Key = filename,contents = parsed result - Config)中。我的问题是,我怎样才能得到这条线rslt <- parseFromFile parseIni fn以适应getIni的类型签名? parseFromFile的类型签名monad m => m (Maybe Config)不适合。或者有人可能会建议一种更好的编码方式吗?我只在这里包含了相关代码。有问题的功能是maingetIni冲突类型签名

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 

回答

1

表达parseFromFile大概有型IO (Maybe Config)(我立足这一关就需要做IO读取文件内容,你的情况下,表达对公正,没有相匹配的事实),那么如果你打算在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,你将不得不使用一个单子折叠,而不是纯粹的foldr。此外,您可能想要使用Nothing -> return mp,因为解析错误会清除您已解析的任何文件。

或者,您可以创建一个Map FileName String将文件名映射到内容,然后您可以使用纯函数如M.mapM.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 

(声明:我没有编译此代码的任何,但它应该给你一般的想法)

+0

谢谢你。我用foldM替换了foldr,改变了参数并输入了一些签名,并且都很好。没有看过你的替代解决方案,但会尽快完成。 – user1897830