Some rewording
Getty Ritter
7 years ago
65 | 65 | } deriving (Eq, Show) |
66 | 66 | ~~~ |
67 | 67 | |
68 |
In this case, we can't construct a `Config` value until we've parsed all three fields in two distinct subsections. One way of doing this is to return the intermediate values from our `section` parsers and construct the |
|
68 | In this case, we can't construct a `Config` value until we've parsed all three fields in two distinct subsections. One way of doing this is to return the intermediate values from our `section` parsers and construct the `Config` value at the end, once we have all three of its fields: | |
69 | 69 | |
70 | 70 | ~~~.haskell |
71 | 71 | configParser :: IniParser Config |
78 | 78 | return (Config host port user) |
79 | 79 | ~~~ |
80 | 80 | |
81 |
This is |
|
81 | This is unfortunately awkward and repetitive. An alternative is to flatten it out by repeating invocations of `section` like below, but this has its own problems, such as unnecessary repetition of the `"NETWORK"` string literal, unnecessarily repetitive table lookups, and general verbosity: | |
82 | 82 | |
83 | 83 | ~~~.haskell |
84 | 84 | configParser :: IniParser Config |
89 | 89 | return (Config host port user) |
90 | 90 | ~~~ |
91 | 91 | |
92 |
|
|
92 | In situations like these, you can instead use the `Data.Ini.Config.St` module, which provides a slightly different abstraction: the functions exported by this module assume that you start with a default configuration value, and parsing a field allows you to _update_ that configuration with the value of a field. The monads exported by this module have an extra type parameter that represents the type of the value being updated. The easiest way to use this module is by combining lenses with the `.=` and `.=?` operators, which take a lens and a normal `SectionParser` value, and produce a `SectionStParser` value that uses the lens to update the underlying type: | |
93 | 93 | |
94 | 94 | ~~~.haskell |
95 | 95 | makeLenses ''Config |
103 | 103 | cfUser .= fieldMb "user" |
104 | 104 | ~~~ |
105 | 105 | |
106 |
In order to use this parser, we will need to provide an existing value of `Config` so we can apply our updates to it. This is |
|
106 | In order to use this parser, we will need to provide an existing value of `Config` so we can apply our updates to it. This is the biggest downside to this approach: in this case, even though the `host` and `port` fields are obligatory and will be overwritten by the parser, we still need to provide dummy values for them. | |
107 | 107 | |
108 | 108 | ~~~.haskell |
109 | 109 | myParseIni :: Text -> Either String Config |
111 | 111 | where defaultConfig = Config "unset" 0 Nothing |
112 | 112 | ~~~ |
113 | 113 | |
114 |
The |
|
114 | The `IniStParser` implementation isn't tied to lenses, and many of the functions exported by `Data.Ini.Config.St` expected any generic function of the type `a -> s -> s`, and not a lens specifically. If we didn't want to use lenses, we can still take advantage of this library, albeit in a more verbose way: | |
115 | 115 | |
116 | 116 | ~~~.haskell |
117 | 117 | configParser :: IniStParser Config () |