Haskell如何使用Language.Haskell.Interpreter来读取配置文件?

时间:2013-05-23 12:59:44

标签: haskell configuration-files interpreter hint

如何使用Language.Haskell.Interpreter读取给定的配置文件并指定其中给出的值以初始化程序中的变量?

我的配置文件如下:

numRecords = 10
numFields = 3
inputFile = /home/user1/project/indata.file
outputFile = /home/user1/project/outdata.file
datefmt = ddmmyyyy

我想使用配置文件中给出的值初始化与配置文件中给出的标识符相对应的变量。

如何使用Language.Haskell.Interpreter来完成这件事? 我很困惑因为IO Monad和Interpreter Monad。这种小例子也很有用。

2 个答案:

答案 0 :(得分:7)

为什么不呢?

data Config = Config {size :: Int, path :: String} deriving (Read, Show)

readConfig :: String -> Config
readConfig = read

main = do

  config <- readFile "my.config" >>= return . readConfig

  putStrLn $ "Configured size := " ++ show (size config)
  putStrLn $ "Configured path := " ++ show (path config)

使用my.config文件

Config {
  size = 1024,
  path = "/root/passwords.db"
}

使用ghci

进行测试
*Main> main
Configured size := 1024
Configured path := "/root/passwords.db"
*Main>

(对不起以前的错误,我很着急)

答案 1 :(得分:4)

首先,您不能(至少以任何明显的方式)使用hint包中的Language.Haskell.Interpreter来执行此操作。该模块中的函数用于读入和运行Haskell代码,而不是任意结构化数据。

对于读取结构化数据,您需要某种解析器。以下是您拥有的一些选项:

  1. 使用解析器生成器,例如Happy
  2. 使用解析器 - 组合器库,例如uu-parsinglibparsec
  3. 直接实现自己的解析器。
  4. 使用Read类的自动派生实例。
  5. 广告1.和2.

    如果您需要读取的数据格式非常重要,或者如果解析失败时需要有用的错误消息,我建议您选择1.或2.并查阅相应工具的文档和库。请注意,您需要一些时间来熟悉其主要概念和界面。

    Ad 3。

    如果您的数据格式足够简单(如您的示例中所示),并且如果您的愿望清单中的广泛错误报告不高,您可以轻松地滚动自己的解析器。

    在您的示例中,配置文件本质上是键和值的列表,由换行符分隔。在Haskell中,我们可以通过字符串对列表来表示这样的列表:

    type Config = [(String, String)]
    

    “解析”一个配置然后简化为:(1)将输入字符串分成行,(2)将每行分成单词,(3)从每行中选择第一个和第三个单词:

    readConfig :: String -> Config
    readConfig s =
      [(key, val) | line <- lines s, let (key : _ : val : _) = words line]
    

    要从解析的配置文件中检索条目,我们可以使用函数get

    get :: String -> (String -> a) -> Config -> a
    get key f config = case lookup key config of
      Nothing -> error ("get: not found: " ++ key)
      Just x  -> f x
    

    此函数将第一个参数作为条目的键,并将第二个参数作为将原始值字符串转换为适当类型的函数。对于纯文本配置值,我们可以简单地将标识函数传递给get

    inputFile, outputFile, datefmt :: Config -> String
    inputFile  = get "inputFile" id
    outputFile = get "outputFile" id
    datefmt    = get "datefmt" id
    

    对于整数条目,我们可以使用read

    numRecords, numFields :: Config -> Int
    numRecords = get "numRecords" read
    numFields  = get "numFields" read
    

    也许这些模式很常见,可以被分解到他们自己的get专用版本中:

    getS :: String -> Config -> String
    getS key = get key id
    
    getR :: Read a => String -> Config -> a
    getR key = get key read
    
    inputFile', outputFile', datefmt' :: Config -> String
    inputFile'  = getS "inputFile"
    outputFile' = getS "outputFile"
    datefmt'    = getS "datefmt"
    
    numRecords', numFields' :: Config -> Int
    numRecords' = getR "numRecords"
    numFields'  = getR "numFields"
    

    作为示例,这里是读取配置文件并打印“outputFile”值的程序:

    main :: IO ()
    main = do
      s <- readFile "config.txt"
      let config = readConfig s
      putStrLn (outputFile config)
    

    Ad 4。

    如果您可以控制配置文件的格式,则可以引入用于保存配置数据的新数据类型,并让Haskell自动为其派生类Read的实例。例如:

    data Config = Config
      { numRecords :: Int
      , numFields  :: Int
      , inputFile  :: String
      , outputFile :: String
      , datefmt    :: String
      } deriving Read
    

    现在,您需要确保配置文件与预期格式匹配。例如:

    Config
      { numRecords = 10
      , numFields  = 3
      , inputFile  = "/home/user1/project/indata.file"
      , outputFile = "/home/user1/project/outdata.file"
      , datefmt    = "ddmmyyyy"
      }
    

    例如,这里是打印“outputFile”值的程序:

    main :: IO ()
    main = do
      s <- readFile "config.txt"
      let config = read s
      putStrLn (outputFile config)