1
{- arch-tag: ConfigParser main file
2
Copyright (C) 2004-2006 John Goerzen <jgoerzen@complete.org>
4
This program is free software; you can redistribute it and/or modify
5
it under the terms of the GNU Lesser General Public License as published by
6
the Free Software Foundation; either version 2.1 of the License, or
7
(at your option) any later version.
9
This program is distributed in the hope that it will be useful,
10
but WITHOUT ANY WARRANTY; without even the implied warranty of
11
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
GNU Lesser General Public License for more details.
14
You should have received a copy of the GNU Lesser General Public License
15
along with this program; if not, write to the Free Software
16
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20
Module : Data.ConfigFile
21
Copyright : Copyright (C) 2004-2006 John Goerzen
22
License : GNU LGPL, version 2.1 or above
24
Maintainer : John Goerzen <jgoerzen@complete.org>
25
Stability : provisional
28
Configuration file parsing, generation, and manipulation
30
Copyright (c) 2004-2006 John Goerzen, jgoerzen\@complete.org
32
This module contains extensive documentation. Please scroll down to the Introduction section to continue reading.
34
module Data.ConfigFile
45
-- * Configuration File Format
54
-- ** Case Sensitivity
63
-- ** Non-Monadic Usage
66
-- ** Error Monad Usage
69
-- ** Combined Error\/IO Monad Usage
74
SectionSpec, OptionSpec, ConfigParser(..),
75
CPErrorData(..), CPError,
80
-- * Configuring the ConfigParser
83
-- ** Access Functions
84
simpleAccess, interpolatingAccess,
88
readfile, readhandle, readstring,
92
sections, has_section,
97
set, setshow, remove_option,
98
add_section, remove_section,
106
import Data.ConfigFile.Types
107
import Data.ConfigFile.Parser
108
import Data.Map.Utils
109
import Data.Either.Utils
111
import qualified Data.Map as Map
113
import System.IO(Handle)
115
import Control.Monad.Error
117
-- For interpolatingAccess
118
import Text.ParserCombinators.Parsec.Error(ParseError, messageString,
119
errorMessages, Message(..))
120
import Text.ParserCombinators.Parsec(parse)
122
----------------------------------------------------------------------
123
-- Basic types / default values
124
----------------------------------------------------------------------
126
{- | The default empty 'Data.ConfigFile' object.
128
The content contains only an empty mandatory @DEFAULT@ section.
130
'optionxform' is set to @map toLower@.
132
'usedefault' is set to @True@.
134
'accessfunc' is set to 'simpleAccess'.
136
emptyCP :: ConfigParser
137
emptyCP = ConfigParser { content = fromAL [("DEFAULT", [])],
138
defaulthandler = defdefaulthandler,
139
optionxform = map toLower,
141
accessfunc = simpleAccess}
143
{- | Low-level tool to convert a parsed object into a 'CPData'
144
representation. Performs no option conversions or special handling
146
fromAL :: ParseOutput -> CPData
148
let conv :: CPData -> (String, [(String, String)]) -> CPData
149
conv fm sect = Map.insert (fst sect) (Map.fromList $ snd sect) fm
151
foldl conv Map.empty origal
153
{- | Default (non-interpolating) access function -}
154
simpleAccess :: MonadError CPError m =>
155
ConfigParser -> SectionSpec -> OptionSpec -> m String
156
simpleAccess cp s o = defdefaulthandler cp s (optionxform cp $ o)
158
{- | Interpolating access function. Please see the Interpolation section
159
above for a background on interpolation.
161
Although the format string looks similar to one used by "Text.Printf",
162
it is not the same. In particular, only the %(...)s format is supported.
163
No width specifiers are supported and no conversions other than s are supported.
165
To use this function, you must specify a maximum recursion depth for
166
interpolation. This is used to prevent a stack overflow in the event that
167
the configuration file contains an endless interpolation loop. Values of 10
168
or so are usually more than enough, though you could probably go into the
169
hundreds or thousands before you have actual problems.
171
A value less than one will cause an instant error every time you attempt
174
This access method can cause 'get' and friends to return a new 'CPError':
175
'InterpolationError'. This error would be returned when:
177
* The configuration file makes a reference to an option that does
180
* The maximum interpolation depth is exceeded
182
* There is a syntax error processing a %-directive in the configuration
185
An interpolation lookup name specifies an option only. There is no provision
186
to specify a section. Interpolation variables are looked up in the current
187
section, and, if 'usedefault' is True, in @DEFAULT@ according to the normal
190
To use a literal percent sign, you must place @%%@ in the configuration
191
file when interpolation is used.
193
Here is how you might enable interpolation:
195
>let cp2 = cp {accessfunc = interpolatingAccess 10}
197
The @cp2@ object will now support interpolation with a maximum depth of 10.
199
interpolatingAccess :: MonadError CPError m =>
201
ConfigParser -> SectionSpec -> OptionSpec
204
interpolatingAccess maxdepth cp s o =
206
then interError "maximum interpolation depth exceeded"
207
else do x <- simpleAccess cp s o
208
case parse (interpmain $ lookupfunc) (s ++ "/" ++ o) x of
209
Left y -> case head (errorMessages y) of
210
Message z -> interError z
211
_ -> interError (show y)
214
lookupfunc = interpolatingAccess (maxdepth - 1) cp s
215
interError x = throwError (InterpolationError x, "interpolatingAccess")
217
-- internal function: default handler
218
defdefaulthandler :: MonadError CPError m =>
219
ConfigParser -> SectionSpec -> OptionSpec -> m String
221
defdefaulthandler cp sectn opt =
223
lookup s o = do sect <- maybeToEither (NoSection s,
224
"get " ++ formatSO sectn opt) $
226
maybeToEither (NoOption o,
227
"get " ++ formatSO sectn opt) $
229
trydefault e = if (usedefault cp)
232
-- Use original error if it's not in DEFAULT either
233
`catchError` (\_ -> throwError e)
236
lookup sectn opt `catchError` trydefault
239
{- | Combines two 'ConfigParser's into one.
241
Any duplicate options are resolved to contain the value specified in
244
The 'ConfigParser' options in the resulting object will be set as they
245
are in the second one passed to this function. -}
246
merge :: ConfigParser -> ConfigParser -> ConfigParser
248
let conv :: String -> String
249
conv = optionxform dest
250
convFM :: CPOptions -> CPOptions
251
convFM = Map.fromList . map (\x -> (conv (fst x), snd x)) . Map.toList
252
mergesects a b = Map.union a b
254
dest { content = Map.unionWith mergesects
255
(content dest) (Map.map convFM (content src)) }
257
{- | Utility to do a special case merge. -}
258
readutil :: ConfigParser -> ParseOutput -> ConfigParser
259
readutil old new = merge old $ old { content = fromAL new }
261
{- | Loads data from the specified file. It is then combined with the
262
given 'ConfigParser' using the semantics documented under 'merge' with the
263
new data taking precedence over the old. However, unlike
264
'merge', all the options
265
as set in the old object are preserved since the on-disk representation
266
does not convey those options.
268
May return an error if there is a syntax error. May raise an exception if the file could not be accessed.
270
--readfile :: ConfigParser -> FilePath ->IO (CPResult ConfigParser)
271
readfile :: MonadError CPError m => ConfigParser -> FilePath -> IO (m ConfigParser)
273
readfile cp fp = do n <- parse_file fp
275
return $ readutil cp y
277
readfile cp fp = do n <- parse_file fp
278
return $ n >>= (return . readutil cp)
280
{- | Like 'readfile', but uses an already-open handle. You should
281
use 'readfile' instead of this if possible, since it will be able to
282
generate better error messages.
284
Errors would be returned on a syntax error.
286
--readhandle :: ConfigParser -> Handle -> IO (CPResult ConfigParser)
287
readhandle :: MonadError CPError m => ConfigParser -> Handle -> IO (m ConfigParser)
288
readhandle cp h = do n <- parse_handle h
289
return $ n >>= (return . (readutil cp))
291
{- | Like 'readfile', but uses a string. You should use 'readfile'
292
instead of this if you are processing a file, since it can generate
293
better error messages.
295
Errors would be returned on a syntax error.
297
readstring :: MonadError CPError m =>
298
ConfigParser -> String -> m ConfigParser
301
return $ readutil cp n
303
{- | Returns a list of sections in your configuration file. Never includes
304
the always-present section @DEFAULT@. -}
305
sections :: ConfigParser -> [SectionSpec]
306
sections = filter (/= "DEFAULT") . Map.keys . content
308
{- | Indicates whether the given section exists.
310
No special @DEFAULT@ processing is done. -}
311
has_section :: ConfigParser -> SectionSpec -> Bool
312
has_section cp x = Map.member x (content cp)
314
{- | Adds the specified section name. Returns a
315
'SectionAlreadyExists' error if the
316
section was already present. Otherwise, returns the new
317
'ConfigParser' object.-}
318
add_section :: MonadError CPError m =>
319
ConfigParser -> SectionSpec -> m ConfigParser
322
then throwError $ (SectionAlreadyExists s, "add_section")
323
else return $ cp {content = Map.insert s Map.empty (content cp)}
325
{- | Removes the specified section. Returns a 'NoSection' error if
326
the section does not exist; otherwise, returns the new 'ConfigParser'
329
This call may not be used to remove the @DEFAULT@ section. Attempting to do
330
so will always cause a 'NoSection' error.
332
remove_section :: MonadError CPError m =>
333
ConfigParser -> SectionSpec -> m ConfigParser
334
remove_section _ "DEFAULT" = throwError $ (NoSection "DEFAULT", "remove_section")
335
remove_section cp s =
337
then return $ cp {content = Map.delete s (content cp)}
338
else throwError $ (NoSection s, "remove_section")
340
{- | Removes the specified option. Returns a 'NoSection' error if the
341
section does not exist and a 'NoOption' error if the option does not
342
exist. Otherwise, returns the new 'ConfigParser' object.
344
remove_option :: MonadError CPError m =>
345
ConfigParser -> SectionSpec -> OptionSpec -> m ConfigParser
346
remove_option cp s passedo =
347
do sectmap <- maybeToEither (NoSection s,
348
"remove_option " ++ formatSO s passedo) $
349
Map.lookup s (content cp)
350
let o = (optionxform cp) passedo
351
let newsect = Map.delete o sectmap
352
let newmap = Map.insert s newsect (content cp)
353
if Map.member o sectmap
354
then return $ cp {content = newmap}
355
else throwError $ (NoOption o,
356
"remove_option " ++ formatSO s passedo)
358
{- | Returns a list of the names of all the options present in the
361
Returns an error if the given section does not exist.
363
options :: MonadError CPError m =>
364
ConfigParser -> SectionSpec -> m [OptionSpec]
365
options cp x = maybeToEither (NoSection x, "options") $
367
o <- Map.lookup x (content cp)
370
{- | Indicates whether the given option is present. Returns True
371
only if the given section is present AND the given option is present
372
in that section. No special @DEFAULT@ processing is done. No
373
exception could be raised or error returned.
375
has_option :: ConfigParser -> SectionSpec -> OptionSpec -> Bool
378
v = do secthash <- Map.lookup s c
379
return $ Map.member (optionxform cp $ o) secthash
382
{- | The class representing the data types that can be returned by "get".
385
{- | Retrieves a string from the configuration file.
387
When used in a context where a String is expected, returns that string verbatim.
389
When used in a context where a Bool is expected, parses the string to
390
a Boolean value (see logic below).
392
When used in a context where anything that is an instance of Read is expected,
393
calls read to parse the item.
395
An error will be returned of no such option could be found or if it could
396
not be parsed as a boolean (when returning a Bool).
398
When parsing to a Bool, strings are case-insentively converted as follows:
400
The following will produce a True value:
412
The following will produce a False value:
423
get :: MonadError CPError m => ConfigParser -> SectionSpec -> OptionSpec -> m a
425
instance Get_C String where
426
get cp s o = eitherToMonadError $ (accessfunc cp) cp s o
428
instance Get_C Bool where
431
instance (Num t, Read t) => Get_C t where
434
genericget cp s o = get cp s o >>= return . read
436
getbool :: MonadError CPError m =>
437
ConfigParser -> SectionSpec -> OptionSpec -> m Bool
440
case map toLower . strip $ val of
444
"enabled" -> return True
445
"true" -> return True
448
"off" -> return False
449
"disabled" -> return False
450
"false" -> return False
451
_ -> throwError (ParseError $ "couldn't parse bool " ++
452
val ++ " from " ++ formatSO s o, "getbool")
455
"(" ++ s ++ "/" ++ o ++ ")"
458
{- | Returns a list of @(optionname, value)@ pairs representing the content
459
of the given section. Returns an error the section is invalid. -}
460
items :: MonadError CPError m =>
461
ConfigParser -> SectionSpec -> m [(OptionSpec, String)]
462
items cp s = do fm <- maybeToEither (NoSection s, "items") $
463
Map.lookup s (content cp)
464
return $ Map.toList fm
466
{- | Sets the option to a new value, replacing an existing one if it exists.
468
Returns an error if the section does not exist. -}
469
set :: MonadError CPError m =>
470
ConfigParser -> SectionSpec -> OptionSpec -> String -> m ConfigParser
471
set cp s passedo val =
472
do sectmap <- maybeToEither (NoSection s, "set " ++ formatSO s passedo) $
473
Map.lookup s (content cp)
474
let o = (optionxform cp) passedo
475
let newsect = Map.insert o val sectmap
476
let newmap = Map.insert s newsect (content cp)
477
return $ cp { content = newmap}
479
{- | Sets the option to a new value, replacing an existing one if it exists.
480
It requires only a showable value as its parameter.
481
This can be used with bool values, as well as numeric ones.
483
Returns an error if the section does not exist. -}
484
setshow :: (Show a, MonadError CPError m) =>
485
ConfigParser -> SectionSpec -> OptionSpec -> a -> m ConfigParser
486
setshow cp s o val = set cp s o (show val)
488
{- | Converts the 'ConfigParser' to a string representation that could be
489
later re-parsed by this module or modified by a human.
491
Note that this does not necessarily re-create a file that was originally
492
loaded. Things may occur in a different order, comments will be removed,
493
etc. The conversion makes an effort to make the result human-editable,
494
but it does not make an effort to make the result identical to the original
497
The result is, however, guaranteed to parse the same as the original input.
499
to_string :: ConfigParser -> String
501
let gen_option (key, value) =
502
key ++ ": " ++ (replace "\n" "\n " value) ++ "\n"
503
gen_section (sect, valfm) = -- gen a section, but omit DEFAULT if empty
504
if (sect /= "DEFAULT") || (Map.size valfm > 0)
505
then "[" ++ sect ++ "]\n" ++
506
(concat $ map gen_option (Map.toList valfm)) ++ "\n"
509
concat $ map gen_section (Map.toList (content cp))
511
----------------------------------------------------------------------
513
----------------------------------------------------------------------
517
Many programs need configuration files. These configuration files are
518
typically used to configure certain runtime behaviors that need to be
519
saved across sessions. Various different configuration file formats
522
The ConfigParser module attempts to define a standard format that is
523
easy for the user to edit, easy for the programmer to work with, yet
524
remains powerful and flexible.
529
For the programmer, this module provides:
531
* Simple calls to both read /and write/ configuration files
533
* Call that can generate a string version of a file that is
534
re-parsable by this module (useful for, for instance, sending the
537
* Segmented configuration files that let you separate configuration
538
into distinct sections, each with its own namespace. This can be
539
used to configure multiple modules in one file, to configure
540
multiple instances of a single object, etc.
542
* On-the-fly parsing of integer, boolean, float, multi-line string values,
543
and anything else Haskell's read can deal with
545
* It is possible to make a configuration file parsable by this
546
module, the Unix shell, and\/or Unix make, though some feautres are,
547
of course, not compatible with these other tools.
549
* Syntax checking with error reporting including line numbers
551
* Implemented in pure Haskell. No dependencies on modules outside
552
the standard library distributed with Haskell compilers or interpreters.
553
All calls except those that read directly from a handle are pure calls
554
and can be used outside the IO monad.
556
* Comprehensive documentation
560
* Complete compatibility with Python's ConfigParser module, or my
561
ConfigParser module for OCaml, part of my MissingLib package.
563
For the user, this module provides:
565
* Easily human-editable configuration files with a clear, concise,
566
and consistent format
568
* Configuration file format consistent with other familiar formats
569
(\/etc\/passwd is a valid ConfigParser file)
571
* No need to understand semantics of markup languages like XML
576
This module is based on Python's ConfigParser module at
577
<http://www.python.org/doc/current/lib/module-ConfigParser.html>. I had
578
earlier developed an OCaml implementation as part of my MissingLib library
579
at <gopher://gopher.quux.org/devel/missinglib>.
581
While the API of these three modules is similar, and the aim is to preserve all
582
useful features of the original Python module, there are some differences
583
in the implementation details. This module is a complete, clean re-implementation
584
in Haskell, not a Haskell translation of a Python program. As such, the feature
585
set is slightly different.
590
The basic configuration file format resembles that of an old-style
591
Windows .INI file. Here are two samples:
594
>inputfile = /etc/passwd
595
>names = Peter, Paul, Mary, George, Abrahaham, John, Bill, Gerald, Richard,
599
This defines a file without any explicit section, so all items will
600
occur within the default section @DEFAULT@. The @debug@ option can be read
601
as a boolean or a string. The remaining items can be read as a string
602
only. The @names@ entry spans two lines -- any line starting with
603
whitespace, and containing something other than whitespace or
604
comments, is taken as a continuation of the previous line.
606
Here's another example:
611
># Options for the first file
613
>location: /usr/local
616
>optionaltext: Hello, this entire string is included
622
This file defines three sections. The @DEFAULT@ section specifies an
623
entry @hostname@. If you attempt to read the hostname option in any
624
section, and that section doesn't define @hostname@, you will get the
625
value from @DEFAULT@ instead. This is a nice time-saver. You can also
626
note that you can use colons instead of the = character to separate
627
option names from option entries.
632
Whitespace (spaces, tabs, etc) is automatically stripped from the
633
beginning and end of all strings. Thus, users can insert whitespace
634
before\/after the colon or equal sign if they like, and it will be
635
automatically stripped.
637
Blank lines or lines consisting solely of whitespace are ignored.
639
A line giving an option or a section name may not begin with white space.
640
This requirement is necessary so there is no ambiguity between such lines
641
and continuation lines for multi-line options.
647
Comments are introduced with the pound sign @#@ or the semicolon @;@. They
648
cause the parser to ignore everything from that character to the end
651
Comments /may not/ occur within the definitions of options; that is, you
652
may not place a comment in the middle of a line such as @user: Fred@.
653
That is because the parser considers the comment characters part
654
of the string; otherwise, you'd be unable to use those characters in
655
your strings. You can, however, \"comment out\" options by putting the
656
comment character at the start of the line.
662
By default, section names are case-sensitive but option names are
663
not. The latter can be adjusted by adjusting 'optionxform'. -}
667
Interpolation is an optional feature, disabled by default. If you replace
668
the default 'accessfunc' ('simpleAccess') with 'interpolatingAccess',
669
then you get interpolation support with 'get' and the other 'get'-based functions.
671
As an example, consider the following file:
675
>filename = test_%(arch)s.c
676
>dir = /usr/src/%(filename)s
679
With interpolation, you would get these results:
681
>get cp "DEFAULT" "filename" -> "test_i386.c"
682
>get cp "DEFAULT" "dir" -> "/usr/src/test_i386.c"
683
>get cp "DEFAULT" "percent" -> "5%"
685
For more details on interpolation, please see the documentation for the
686
'interpolatingAccess' function.
691
The basic theory of working with ConfigParser is this:
693
1. Parse or build a 'ConfigParser' object
695
2. Work with it in one of several ways
697
3. To make changes, you discard the original object and use a new one.
698
Changes can be "chained" through one of several monads.
700
The default 'ConfigParser' object that you always start with is 'emptyCP'.
701
From here, you load data into it (merging data into the empty object),
702
set up structures yourself, or adjust options.
704
Let's take a look at some basic use cases.
709
You'll notice that many functions in this module return a
710
@MonadError 'CPError'@ over some
711
type. Although its definition is not this simple, you can consider this to be
712
the same as returning @Either CPError a@.
714
That is, these functions will return @Left error@ if there's a problem
715
or @Right result@ if things are fine. The documentation for individual
716
functions describes the specific circumstances in which an error may occur in
719
Some people find it annoying to have to deal with errors manually.
720
You can transform errors into exceptions in your code by using
721
'Data.Either.Utils.forceEither'. Here's an example of this style of programming:
723
> import Data.Either.Utils
725
> val <- readfile emptyCP "/etc/foo.cfg"
726
> let cp = forceEither val
727
> putStrLn "Your setting is:"
728
> putStrLn $ forceEither $ get cp "sect1" "opt1"
730
In short, you can just put @forceEither $@ in front of every call that returns
731
something that is a MonadError.
732
This is still a pure functional call, so it can be used outside
733
of the IO monads. The exception, however, can only be caught in the IO
736
If you don't want to bother with 'forceEither', you can use the error monad. It's simple and better... read on.
741
The return type is actually defined in terms of the Error monad, which is
742
itself based on the Either data type.
744
Here's a neat example of chaining together calls to build up a 'ConfigParser'
748
> cp <- add_section cp "sect1"
749
> cp <- set cp "sect1" "opt1" "foo"
750
> cp <- set cp "sect1" "opt2" "bar"
753
The return value of this little snippet is @Right [\"opt1\", \"opt2\"]@.
754
(Note to beginners: unlike the IO monad, you /can/ escape from the Error
757
Although it's not obvious, there actually was error checking there. If
758
any of those calls would have generated an error, processing would have
759
stopped immediately and a @Left@ value would have been returned. Consider
763
> cp <- add_section cp "sect1"
764
> cp <- set cp "sect1" "opt1" "foo"
765
> cp <- set cp "sect2" "opt2" "bar"
768
The return value from this is @Left ('NoSection' \"sect2\", \"set\")@. The
769
second call to 'set' failed, so the final call was skipped, and the result
770
of the entire computation was considered to be an error.
772
You can combine this with the non-monadic style to get a final, pure value
775
>forceEither $ do let cp = emptyCP
776
> cp <- add_section cp "sect1"
777
> cp <- set cp "sect1" "opt1" "foo"
778
> cp <- set cp "sect1" "opt2" "bar"
781
This returns @[\"opt1\", \"opt2\"]@. A quite normal value.
785
{- $usageerroriomonad
787
You've seen a nice way to use this module in the Error monad and get an Either
788
value out. But that's the Error monad, so IO is not permitted.
789
Using Haskell's monad transformers, you can run it in the combined
790
Error\/IO monad. That is, you will get an IO result back. Here is a full
791
standalone example of doing that:
793
>import Data.ConfigFile
794
>import Control.Monad.Error
799
> cp <- join $ liftIO $ readfile empty "/etc/passwd"
801
> liftIO $ putStrLn "In the test"
802
> nb <- get x "DEFAULT" "nobody"
803
> liftIO $ putStrLn nb
804
> foo <- get x "DEFAULT" "foo"
805
> liftIO $ putStrLn foo
809
On my system, this prints:
812
>x:65534:65534:nobody:/nonexistent:/bin/sh
813
>Left (NoOption "foo","get")
815
That is, my @\/etc\/passwd@ file contains a @nobody@ user but not a @foo@ user.
817
Let's look at how that works.
819
First, @main@ always runs in the IO monad only, so we take the result from
820
the later calls and put it in @rv@. Note that the combined block
821
is started with @runErrorT $ do@ instead of just @do@.
823
To get something out of the call to 'readfile', we use
824
@join $ liftIO $ readfile@. This will bring the result out of the IO monad
825
into the combined monad and process it like usual. From here on,
826
everything looks normal, except for IO calls. They are all executed under
827
@liftIO@ so that the result value is properly brought into the combined
828
monad. This finally returns @\"done\"@. Since we are in the Error monad, that means that the literal value is @Right \"done\"@. Since we are also in the IO
829
monad, this is wrapped in IO. So the final return type after applying
830
@runErrorT@ is @IO (Either CPError String)@.
832
In this case, there was an error, and processing stopped at that point just
833
like the example of the pure Error monad. We print out the return value,
834
so you see the error displayed as a @Left@ value.
836
It all works quite easily.
842
You may notice that the 'ConfigParser' object has some configurable parameters,
843
such as 'usedefault'. In case you're not familiar with the Haskell syntax
844
for working with these, you can use syntax like this to set these options:
846
>let cp2 = cp { usedefault = False }
848
This will create a new 'ConfigParser' that is the same as @cp@ except for
849
the 'usedefault' field, which is now always False. The new object will be
850
called @cp2@ in this example.
855
You can use these functions to read data from a file.
857
A common idiom for loading a new object from stratch is:
859
@cp <- 'readfile' 'emptyCP' \"\/etc\/foo.cfg\"@
861
Note the use of 'emptyCP'; this will essentially cause the file's data
862
to be merged with the empty 'ConfigParser'.
867
The code used to say this:
869
>type CPResult a = MonadError CPError m => m a
870
>simpleAccess :: ConfigParser -> SectionSpec -> OptionSpec -> CPResult String
872
But Hugs did not support that type declaration. Therefore, types are now
875
>simpleAccess :: MonadError CPError m =>
876
> ConfigParser -> SectionSpec -> OptionSpec -> m String
878
Although it looks more confusing than before, it still means the same.
879
The return value can still be treated as @Either CPError String@ if you so