@hackage toml-reader0.2.1.0
TOML format parser compliant with v1.0.0.
Categories
License
BSD-3-Clause
Maintainer
Brandon Chinn <brandonchinn178@gmail.com>
Links
Versions
Installation
Dependencies (6)
Dependents (4)
@hackage/nvfetcher, @hackage/toml-reader-parse, @hackage/weeder, @hackage/multidir
toml-reader
TOML format parser compliant with v1.0.0 (verified with the toml-test tool).
Usage
{-# LANGUAGE OverloadedStrings #-}
import TOML (DecodeTOML, tomlDecoder, getField, decodeFile)
data MyConfig = MyConfig
{ field1 :: Int
, field2 :: Bool
} deriving (Show)
instance DecodeTOML MyConfig where
tomlDecoder =
MyConfig
<$> getField "field1"
<*> getField "field2"
main :: IO ()
main = do
result <- decodeFile "config.toml"
case result of
Right cfg -> print (cfg :: MyConfig)
Left e -> print e
Design decisions
-
Only supports reading, not writing, since TOML is lossy. For example, a simple
a.b.c = 1line could be written in a number of ways:a.b.c = 1 a.b = { c = 1 } a = { b.c = 1 } [a] b.c = 1 b = { c = 1 } [a.b] c = 1Since reading/writing isn't an idempotent operation, this library won't even pretend to provide
DecodeTOML/EncodeTOMLtypeclasses that imply that they're inverses of each other.Hopefully some other
toml-writerlibrary may come along to make it easy to specify how to format your data in TOML (e.g. a combinator fortablevsinlineTable), or you could usetomland. -
This library defines
DecodeTOMLwith an opaqueDecoder aas opposed to aValue -> DecodeM afunction, likeaesondoes. In my opinion, this makes the common case of decoding config files much more straightforward, especially around nested fields, which are much more common in TOML than JSON. e.g.-- aeson-like instance DecodeTOML MyConfig where decodeTOML :: Value -> DecodeM MyConfig decodeTOML = withObject "MyConfig" $ \o -> MyConfig <$> o .: "field1" <*> (o .: "field2" >>= (.: "field3"))-- with toml-parser instance DecodeTOML MyConfig where tomlDecoder :: Decoder MyConfig tomlDecoder = MyConfig <$> getField "field1" <*> getFields ["field2", "field3"]It also makes it easy to define ad-hoc decoders:
instance DecodeTOML MyConfig where tomlDecoder = ... alternativeDecoder :: Decoder MyConfig alternativeDecoder = ... -- uses tomlDecoder decode "a = 1" -- uses explicit decoder decodeWith alternativeDecoder "a = 1"As a bonus, it also makes for a less point-free interface when defining a decoder based on another decoder, which is kinda cool:
-- aeson-like instance DecodeTOML MyString where decodeTOML = fmap toMyString . decodeTOML-- with toml-parser instance DecodeTOML MyString where tomlDecoder = toMyString <$> tomlDecoderUltimately,
Decoderis just a newtype aroundValue -> DecodeM a, so we could always go back to it. Originally, I wanted to do something likejordan, where this interface is required due to the way it parses and deserializes at the same time, but this isn't possible with TOML due to the way TOML needs to be normalized.