【问题标题】:Haskell how to use Language.Haskell.Interpreter to read config file?Haskell 如何使用 Language.Haskell.Interpreter 读取配置文件?
【发布时间】:2013-05-18 20:30:06
【问题描述】:

如何使用 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 感到困惑。这种类型的小例子也会很有用。

【问题讨论】:

  • 您为什么要这样做? Language.Haskell.Interpreter 可用于动态解析和执行 Haskell 代码,这与您打算做的简单解析配置文件完全不同。

标签: haskell configuration-files interpreter hint


【解决方案1】:

为什么不呢?

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>

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

【讨论】:

  • 什么是 readConfigFromBackEnd?为此要导入哪个模块?
  • @Tempora 它从磁盘读取配置文件。不需要导入,使用readFile "myconfigfile.txt"
  • 我得到错误:Couldn't match expected type IO t0' 实际类型为a0 -&gt; c0' In the second argument of (>>)',即return . readConfig' In a stmt of a 'do' block: config &lt;- readFile "configFile" &gt;&gt; return . readConfig In the expression: do { config > return 。读取配置; putStrLn $ size config } 失败,加载模块:无。
  • @AndrewC 感谢这个编译但是当我运行它时失败了。我的 configFile 包含 size = 10 path = "mypath/file.ext" 但我总是收到错误 *** Exception: Prelude.read: no parse
  • @Tempora josejuan 改进了答案 - 这是否解决了问题? (您的文件必须具有 Config {size=123, path="a string"} 格式,或与 Haskell 友好的布局类似。)
【解决方案2】:

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

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

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

广告 1. 和 2.

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

广告 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)

广告 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)

【讨论】:

  • 我的意思是,为什么不呢?如果解释器可以解析和评估 Haskell 代码,为什么我们不能用它来解析写成 Haskell 代码的“任意”复杂配置脚本并从中提取(通过评估)我们需要的东西。对于更简单的配置需求,您的代码可以完美运行。我猜,xmonad 正是使用这个东西来处理配置数据。但是,当然,该代码超出了我的水平。见:haskell.org/haskellwiki/Xmonad/Config_archive/…
  • @Tempora 我明白你的意思。然后:是的,必须可以使用解释器来做到这一点,但这对我来说似乎有点矫枉过正。无论如何,darcsden.com/jcpetruzza/hint/browse/examples/example.hs 中的示例应该可以帮助您。
猜你喜欢
  • 2015-03-28
  • 2013-10-23
  • 1970-01-01
  • 2017-11-14
  • 1970-01-01
  • 1970-01-01
  • 2011-12-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多