冲突类型签名

时间:2017-04-20 12:26:13

标签: haskell

这段代码读取目录并将所有.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 个答案:

答案 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.mapM.filter之类的纯函数将其转换为Map FileName ConfigData.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

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