toml-reader: TOML format parser compliant with v1.0.0.

[ bsd3, configuration, library, text, toml ] [ Propose Tags ]

TOML format parser compliant with v1.0.0. See for more details.

[Skip to Readme]


Maintainer's Corner

For package maintainers and hackage trustees


  • No Candidates
Versions [RSS]
Change log
Dependencies base (>=4.9 && <5), containers (>= && <0.7), deepseq (>= && <1.5), megaparsec (>=7.0.5 && <9.3), parser-combinators (>=1.1.0 && <1.4), text (>= && <1.3), time (>= && <1.12) [details]
License BSD-3-Clause
Author Brandon Chinn <>
Maintainer Brandon Chinn <>
Category TOML, Text, Configuration
Home page
Bug tracker
Source repo head: git clone
Uploaded by brandonchinn178 at 2022-06-07T06:40:49Z
Distributions NixOS:, Stackage:
Downloads 25 total (25 in the last 30 days)
Rating (no votes yet) [estimated by Bayesian average]
Your Rating
  • λ
  • λ
  • λ
Status Docs available [build log]
Last success reported on 2022-06-07 [all 1 reports]

Readme for toml-reader-

[back to package description]


TOML format parser compliant with v1.0.0 (verified with the toml-test tool).


data MyConfig = MyConfig
  { field1 :: Int
  , field2 :: Bool

instance DecodeTOML MyConfig where
  tomlDecoder =
      <$> 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 = 1 line could be written in a number of ways:

    a.b.c = 1
    a.b = { c = 1 }
    a = { b.c = 1 }
    b.c = 1
    b = { c = 1 }
    c = 1

    Since reading/writing isn't an idempotent operation, this library won't even pretend to provide DecodeTOML/EncodeTOML typeclasses that imply that they're inverses of each other.

    Hopefully some other toml-writer library may come along to make it easy to specify how to format your data in TOML (e.g. a combinator for table vs inlineTable), or you could use tomland.

  • This library defines DecodeTOML with an opaque Decoder a as opposed to a Value -> DecodeM a function, like aeson does. 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 ->
          <$> o .: "field1"
          <*> (o .: "field2" >>= (.: "field3"))
    -- with toml-parser
    instance DecodeTOML MyConfig where
      tomlDecoder :: Decoder MyConfig
      tomlDecoder =
          <$> 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 <$> tomlDecoder

    Ultimately, Decoder is just a newtype around Value -> DecodeM a, so we could always go back to it. Originally, I wanted to do something like jordan, 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.