2017-04-20 85 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