18

我希望让我的 Haskell 程序从外部文件读取设置,以避免重新编译以进行微小的更改。熟悉 YAML,我认为这将是一个不错的选择。现在我必须把这两部分放在一起。到目前为止,谷歌并不是很有帮助。

非常感谢处理从文件中读取和解构 YAML 的小示例代码。

4

4 回答 4

27

如果我对可用的软件包感兴趣,我会去 hackage,查看完整的软件包列表,然后在页面中搜索关键字。这样做会带来这些选择(以及其他一些不太引人注目的选择):

和一个名为 yaml-light 的 HsSyck 包装器:http: //hackage.haskell.org/package/yaml-light

yaml 和 HsSyck 看起来都是最近更新的,并且似乎被其他广泛使用的软件包使用。您可以通过检查反向部门来看到这一点:

两者中,yaml 有更多的 deps,但那是因为它是 yesod 生态系统的一部分。一个依赖于 HsSyck 的库是 yst,我碰巧知道它是积极维护的,所以这向我表明 HsSyck 也很好。

做出选择的下一步是浏览这两个库的文档,看看哪个对我的目的更有吸引力。

在这两者中,看起来 HsSyck 暴露了更多的结构,但其他的不多,而 yaml 则通过 aeson 提供的 json 编码。这向我表明,前者可能更强大,而后者更方便。

于 2012-10-25T01:36:42.737 回答
12

一个简单的例子:

首先你需要一个test.yml文件:

db: /db.sql
limit: 100

在 Haskell 中读取 YAML

{-# LANGUAGE DeriveGeneric #-}

import GHC.Generics
import Data.Yaml

data Config = Config { db :: String
                     , limit :: Int
                     } deriving (Show, Generic)

instance FromJSON Config

main :: IO ()
main = do
  file <- decodeFile "test.yml" :: IO (Maybe Config)
  putStrLn (maybe "Error" show file)
于 2015-10-31T08:34:11.157 回答
1

您可以以静态分析友好的方式yamlparse-applicative描述您的 YAML 解析器,因此您可以免费从解析器获取 YAML 格式的描述。我将为此使用 Matthias Braun 的示例格式

{-# LANGUAGE ApplicativeDo, RecordWildCards, OverloadedStrings #-}

import Data.Yaml
import Data.Aeson.Types (parse)
import YamlParse.Applicative
import Data.Map (Map)
import qualified Data.Text.IO as T

data MyType = MyType
    { stringsToStrings :: Map String String
    , mapOfLists :: Map String [String]
    } deriving Show

parseMyType :: YamlParser MyType
parseMyType = unnamedObjectParser $ do
    stringsToStrings <- requiredField' "strings_to_strings"
    mapOfLists <- requiredField' "map_of_lists"
    pure MyType{..}

main :: IO ()
main = do
    T.putStrLn $ prettyParserDoc parseMyType

    yaml <- decodeFileThrow "config/example.yaml"
    print $ parse (implementParser parseMyType) yaml

请注意,main甚至可以在看到实例之前打印出架构:

strings_to_strings: # required
  <key>: <string>
map_of_lists: # required
  <key>: - <string>
Success 
  (MyType 
    { stringsToStrings = fromList
        [ ("key_one","val_one")
        , ("key_two","val_two")
        ]
    , mapOfLists = fromList 
        [ ("key_one",["val_one","val_two","val_three"])
        , ("key_two",["val_four","val_five"])
        ]
    })
于 2020-07-06T04:41:59.387 回答
0

以下是使用该yaml库从 YAML 文件中解析特定对象的方法。

让我们解析这个文件的部分内容config/example.yaml

# A map from strings to strings
strings_to_strings:
      key_one: val_one
      key_two: val_two

# A map from strings to list of strings
map_of_lists:
  key_one:
      - val_one
      - val_two
      - val_three
  key_two:
      - val_four
      - val_five

# We won't parse this
not_for: us

该模块单独解析strings_to_strings并将map_of_lists它们放入自定义记录中MyType

{-# Language OverloadedStrings, LambdaCase #-}
module YamlTests where
import           Data.Yaml                      ( decodeFileEither
                                                , (.:)
                                                , parseEither
                                                , prettyPrintParseException
                                                )
import           Data.Map                       ( Map )
import           Control.Applicative            ( (<$>) )
import           System.FilePath                ( FilePath
                                                , (</>)
                                                )

data MyType = MyType {stringsToStrings :: Map String String,
                      mapOfLists :: Map String [String]} deriving Show
type ErrorMsg = String

readMyType :: FilePath -> IO (Either ErrorMsg MyType)
readMyType file =
  (\case
      (Right yamlObj) -> do
        stringsToStrings <- parseEither (.: "strings_to_strings") yamlObj
        mapOfLists       <- parseEither (.: "map_of_lists") yamlObj
        return $ MyType stringsToStrings mapOfLists
      (Left exception) -> Left $ prettyPrintParseException exception
    )
    <$> decodeFileEither file

yamlTest = do
  parsedValue <- readMyType $ "config" </> "example.yaml"
  print parsedValue

运行yamlTest查看解析结果。

于 2020-07-05T13:37:26.133 回答