This post announces and showcases yamlparse-applicative
.
I am a long-time user of optparse-applicative
and am very happy and impressed with it. I particularly like that it does not just handle the parsing of arguments, but it also takes care of documenting the parser that you write automatically. Here is an example output for optparse-applicative
:
Usage: intray COMMAND [--config-file FILEPATH] [--url URL] [--cache-dir FILEPATH] [--data-dir FILEPATH] ([--no-sync] | [--sync]) Available options: -h,--help Show this help text --config-file FILEPATH Give the path to an altenative config file --url URL The url of the server. --cache-dir FILEPATH The directory to use for caching --data-dir FILEPATH The directory to use for data --no-sync Do not try to sync. --sync Definitely try to sync.
It is beautiful and self-evident to write arguments that would satisfy this parser. However, when it comes to parsing the (Yaml) config files that the same program would read, things were not so easy. After I had written the yaml parser, I would quickly forget how to write a configuration file that would satisfy the parser.
I procrastinated writing documentation for my config file format for a while. At some point my procrastination in writing documentation just became too much for me to handle, and I made yamlparse-applicative
instead.
A parser with two functions
Instead of using FromJSON
to parse your config file using yaml
and then writing documentation for your format, yamlparse-applicative
combines those two functions into one type.
A yamlparse-applicative
parser allows both actual parsing and generating documentation about what it does. In code, there exists a type Parser i o
and these two functions:
data Parser i o where
...]
[
implementParser :: Parser i o -> (i -> Data.Yaml.Parser o)
explainParser :: Parser i o -> Schema
Now you can use a single value of type Parser Yaml.Value FooBar
to both parse a FooBar
from a Yaml.Value
, and to generate a schema that describes how to write a Yaml value that will satisfy this parser.
Usage example
Let's look at an example of a configuration file that is parsed into a value of type Configuration
:
data Configuration
confPort :: Int
{ confHost :: Maybe Text
, }
I will not even describe what the fields of this type do here, because we will document them as part of the implementation of the yaml parser:
instance YamlSchema Configuration where
=
yamlSchema "Configuration" $ -- Configuration is an object within the yaml file
objectParser Configuration
<$> optionalFieldWithDefault "port" 8000 "The port to serve web requests on"
<*> optionalField "host" "The host to serve web requests on"
Now we can get a FromJSON
instance for free:
instance FromJSON Configuration where
= viaYamlSchema parseJSON
We can generate the following documentation automatically using prettySchemaDoc
:
# Configuration
: # optional, default: 8000
port# The port to serve web requests on
<number>
: # optional
host# The host to serve web requests on
<string>
We can even get syntax-highlighted documentation using prettyColourisedSchemaDoc
:
References
This package can be used via Hackage, Stackage and the source is on GitHub.