From: "Bobby D. Bryant" <bdbryant@mail.utexas.edu>
Subject: Re: Config_Files proposal {long}
Date: Sat, 29 Jun 2002 05:03:17 -0600
Date: 2002-06-29T05:03:17-06:00 [thread overview]
Message-ID: <afk460$9f5$1@geraldo.cc.utexas.edu> (raw)
In-Reply-To: uwuswy0qr.fsf@gsfc.nasa.gov
On Tue, 18 Jun 2002 11:07:08 -0600, Stephen Leake wrote:
> I've posted another example spec and implementation, at
>
> http://users.erols.com/leakstan/Stephe/Ada/Config_Files/config_files.html
I'm sorry that I haven't had time to follow these discussions
carefully, but I'd still like to offer some thoughts on it. (Please
pardon anything that has already been discussed.)
"11. Additional files may be opened for read-only simultaneously in
one Config_File object, using an append-read-only operation. Keys
are searched for first in the writeable file, then in the additional
read-only files. Keys that are created or modified are written to
the writeable file when flushed."
I'm not sure I understand that. If it's saying "allow a config file
to chain in other config files", great. If it's saying something
else, then please add what I just said to your list of requirements.
"22. Provide a way to read and write opaque binary values (ie
bitmaps for icons)."
Do you really want binary data in-line with text data? I think a
better solution would be for the config file to just give the
filename for binary data, so that after fetching the filename from
the config file the program would use special-purpose library
functions to read/write binary data.
People are going to need to specify filenames in config files from
time to time anyway, so what I'm suggesting won't require any new
mechanisms, and it would let you completely off-load responsibility
for arbitrary binary formats.
"23. Provide a way to write comments. Comments are associated with a
key (possibly a hierarchy level), and are preserved thru open and
flush. Comments are intended to guide manually editing the file."
I agree with this, but I want to point out that it is _extremely_
problematic. If you support comments then when people hand-edit a
config file they will occasionaly (and rightly) add a comment
explaining why that value is there. But if a program later changes
that value, how is it to know when the comment has been vitiated?
Add:
I would like to see direct, high-level support for loading/saving 2-D
tables, with the tables laid out in 2-D fashion (with row and column
headers) in the config file. For instance, suppose the program in
question is a wargame. Most such games require a table of Unit_Type
x Terrain_Type --> Movement_Cost (and many other 2-D tables as well),
and it would make life very easy for the scenario designer if s/he
could simply type the data into the config file _as_a_2-D_table_.
Also, it would be nice to support a certain type of dynamism in such
tables. To continue the wargame example, it is becoming
increasingly popular to design games that are just minimalist
engines driven by externally specified data. So the scenario
designer might wish to enumerate the unit and terrain types in the
config file and then follow them with the table of movement costs.
It would be a very useful feature, IMO, if the table-loading were
able to do some sanity checking as it loaded the table. (Minimally,
ensure that the table has the correct dimensions; optimally, have
it actually verify that the row and column headers have the correct
values; ideally, by direct reference to the values of the defining
fields elsewhere in the config file.)
Higher-dimensional tables are sometimes needed as well, though
seemingly less often. They are also somewhat difficult to
represent in a text file. So I would suggest that
higher-dimensional tables be built up by "stacking"
lower-dimensional tables appearing iteratively in the config file.
(The same logic could of course be applied to the construction of
2-D tables, but they are a frequent and natural data type for
humans, and they _can_ be specified in tabular fashion in a text
file, so I think it would make good sense to support them directly
with a high-level API.)
Other food for thought:
I think I mentioned a couple of months ago that I am using GUILE
(Scheme) for my config files. I won't go so far as recommend
adopting that as a standard, but I would like mention a couple of
things about it to provoke thought.
First off, compare the Scheme-like syntax to XML. Here's the
example from your page:
<?xml version="1.0"?> <Config>
<Numeric>
<Interfaces>
<C>
<An_Unsigned> 124076833</An_Unsigned>
<An_Int> 2</An_Int>
</C>
</Interfaces>
<Float>
<A_Float> 3.14159E+00</A_Float>
</Float>
</Numeric>
<Strings>
<Quoted> he said "hi there & goodbye" </Quoted>
<Violins>Stradivarious</Violins>
</Strings>
</Config>
And here's how the same thing would look in one of my config files
right now:
(configuration
(numeric
(interfaces
(C
(An_Unsigned 124076833)
(An_Int 2)
)
)
(Float
(A_Float 3.14159E+00)
)
)
(strings
(Quoted he said "hi there & goodbye")
(Violins Stradivarious)
)
)
The latter is, IMO, *much* easier to read and comprehend. It's
also about (guessingly) 40% smaller. While I don't advocate
smallness for smallness' sake (and I think that "bloat" is usually
merely a slur that people invoke against software systems that they
don't like but can't come up with any cogent criticism of), the
lean syntax can be very important in config files because it
promotes readability -- partly through reduced clutter, and partly
because there will be fewer times when non-semantic line wraps are
required.
[Per my example, an actual Scheme representation for the "Quoted"
field would need to be expressed somewhat differently; I gave
instead what I think it would look like if you adopted the
parentheses syntax for what you're trying to do. The only thing
that would need special treatment would be parentheses -- in fact,
only close-parentheses in data that did not also include
matching open parentheses.]
The only disadvantage I can think of is that the lack of labeled
end markers makes it hard to see where very long lists end. When
this becomes a problem for me I merely address it with a cosmetic
comment, thus:
...
(Floats
(F1 1.2)
(F2 2.3)
(F3 4.2)
(F4 1.9)
...
(F468 7.2)
(Comment: End of "Floats" section.)
)
...
As a side note, notice also that using Scheme for config files
means that there is no formal distinction between config files and
script files, since code and data are represented the same way in
Scheme. However, this requres a run-time system such as GUILE to
make it work, and is thus far out of the scope of what you are
trying to do. OTOH, if you *are* thinking about support for
scripting sometime in the future, give the mechanism some thought
now. I find it extremely convenient to include certain kinds of
code in my config files, thus:
...
(Penalty_Function (log n))
...
Finally, if I may say so without stepping on any toes, I would like
to call attention to the curiously "flat" examples that you have
for the Java and XML sections on your Web page, and the emphasis on
data types rather than on semantics. For comparison, here is what
one of my real config files looks like (substantially reduced and
re-commented for the purposes of this post) -
(configuration
(Comment: The next three values are symbolic names that will be
used as keys for lookups in 1-D tables further down.)
(use-problem legion-i)
(use-workers first-five)
(use-solution-strategy fixed-size-250)
(worker-plot-colors
"RGB:FF/00/00"
"RGB:00/BF/00"
"RGB:00/00/FF"
)
(problems
(Comment: "problems" is a list of problem-specific data, to
be selected by the "use-probem" key defined above.
I.e., the program grabs "use-problem" and extracts
its value, then grabs "problems" and uses the
previously obtained value of "use-problem" to
fetch the proper record out of "problems".
Due to the nature of the application, the data
varies from problem to problem, and the program
must decide what fields to ask for based on the
value of the key. I.e., if "use-problem" is
"phalanx-1" it will not ask for "barbarian-rate".
That is hard-coded behavior embedded in a case
statement in the application program, and I don't
see any other obvious way to do it.)
(legion-i
(Comment: "use-map" is a key that will be looked up in a
table below.)
(use-map 21x21+3cities)
(number-of-legions 5)
(barbarian-rate 1.0)
(game-length 200)
(movement-granularity 10.0)
(games-per-generation 3)
(population-size 500)
)
(phalanx-1
(use-map 41x31+5cities)
(units-per-side 24)
(turns-per-game 30)
(games-per-generation 5)
(population-size 10)
)
)
(maps
(Comment: "maps" is another lookup table. The key is
obtained from the problem definition, above.)
(21x21+3cities
(map-width 21)
(map-height 21)
(number-of-cities 3)
)
(41x31+5cities
(map-width 41)
(map-height 31)
(number-of-cities 5)
)
)
(worker-configurations
(Comment: "worker-configurations" is another lookup table.)
(first-five
(Comment: Notice that "worker-random-seeds" is a list that
my program reads into an array. Currently I do
this by iterating within the program, but it
would be nice if the config-file parser did that
for me.)
(worker-random-seeds 1 2 3 4 5)
(minimum-workers-required-to-start 1)
)
(three-require-three
(worker-random-seeds 1 2 3)
(minimum-workers-required-to-start 3)
)
)
(solution-strategies
(quick-test
(hof-size 20)
(children-each 20)
(min-initial-hidden-units 1)
(epochs
(epoch
(generations 2)
(weight-mutation-rate .01)
(size-penalty none)
)
)
)
(fixed-size-250
(hof-size 50)
(children-each 10)
(min-initial-hidden-units 2)
(epochs
(Comment: Very importantly, notice that this is a
*list* of records of the same type. The
config-file parser must not simply grab
the first and ignore the rest; rather,
it must give them to me sequentially on
demand.)
(epoch
(generations 50)
(weight-mutation-rate .01)
(size-penalty none)
)
(epoch
(generations 100)
(weight-mutation-rate .05)
(size-penalty none)
)
(epoch
(generations 100)
(weight-mutation-rate .10)
(size-penalty none)
)
)
)
)
)
In addition to the raw GUILE bindings I have a package of
higher-level support routines that makes life easier for
the programmer. Included are a "Lookup" function and an
overloaded-by-return-type "Second" function for extracting the
values. For example, after appropriate variable declarations
and with the config file already loaded but not processed, my
code has things like this:
Problem := Lookup("use-problem", Config);
Map := Lookup("use-map", Problem);
X := Second(Lookup("map-width", Map));
Please look at some of the things I'm doing in the example, and
consider how you would be able to do it (and what it would look
like) with the various proposed syntaxes.
FWIW, if I were doing it myself I would probably start with an
X-like syntax and remove the need for "end" markers by making the
indentation semantically significant. I used GUILE instead because
it saved me writing my own parser (and because I may need the
scripting capabilities later), but the GUILE/Scheme syntax, when
pretty-printed, is very similar to X-like with semantic
indentations. To repeat your XML example yet again, you would get
something like:
configuration
numeric
interfaces
C
An_Unsigned 124076833
An_Int 2
Float
A_Float 3.14159E+00
strings
Quoted he said "hi there & goodbye"
Violins Stradivarious
Most human-friendly of all, IMO, but possibly prone to error, and
surely difficult for syntax-highlighting editors unless you chose
to limit the field names to a predefined set.
Sorry for the length; hopefully someone will find an interesting
thing or two somewhere in there.
Bobby Bryant
Austin, Texas
next prev parent reply other threads:[~2002-06-29 11:03 UTC|newest]
Thread overview: 66+ messages / expand[flat|nested] mbox.gz Atom feed top
2002-06-18 17:07 Config_Files proposal Stephen Leake
2002-06-18 21:55 ` Darren New
2002-06-19 16:11 ` Stephen Leake
2002-06-19 16:51 ` Darren New
2002-06-19 18:39 ` Stephen Leake
2002-06-19 19:48 ` Darren New
2002-06-20 14:03 ` Stephen Leake
2002-06-20 16:36 ` Darren New
2002-06-20 17:49 ` Jacob Sparre Andersen
2002-06-20 20:00 ` Stephen Leake
2002-06-20 20:16 ` Darren New
2002-06-20 20:45 ` Stephen Leake
2002-06-21 2:06 ` Ted Dennison
2002-06-21 12:55 ` Marin David Condic
2002-06-24 13:20 ` Stephen Leake
2002-06-21 15:29 ` Darren New
2002-06-24 13:16 ` Stephen Leake
2002-06-24 15:06 ` Darren New
2002-06-24 17:09 ` Stephen Leake
2002-06-24 17:57 ` Darren New
2002-06-24 18:53 ` Stephen Leake
2002-06-24 21:24 ` Darren New
2002-06-29 3:15 ` Ted Dennison
2002-06-28 23:21 ` Randy Brukardt
2002-06-29 3:01 ` Ted Dennison
2002-07-01 20:58 ` Randy Brukardt
2002-07-02 0:31 ` Ted Dennison
2002-07-02 4:43 ` Randy Brukardt
2002-07-05 21:51 ` Robert I. Eachus
2002-06-20 19:54 ` Stephen Leake
2002-06-19 6:53 ` Dr. Michael Paus
2002-06-19 15:18 ` Ted Dennison
2002-06-19 15:08 ` Ted Dennison
2002-06-19 16:18 ` Robert I. Eachus
2002-06-19 16:53 ` Darren New
2002-06-19 18:22 ` Stephen Leake
2002-06-20 13:12 ` Marin David Condic
2002-06-24 13:09 ` Stephen Leake
2002-06-24 15:08 ` Darren New
2002-06-24 17:13 ` Stephen Leake
2002-06-24 17:59 ` Darren New
2002-06-24 19:04 ` Stephen Leake
2002-06-24 21:29 ` Darren New
2002-06-25 12:52 ` Georg Bauhaus
2002-06-25 12:45 ` Georg Bauhaus
2002-06-19 21:32 ` Georg Bauhaus
2002-06-19 18:20 ` Stephen Leake
2002-06-21 20:04 ` Robert I. Eachus
2002-06-24 13:39 ` Stephen Leake
2002-06-19 16:48 ` Marin David Condic
2002-06-20 13:04 ` Georg Bauhaus
2002-06-20 13:53 ` Marin David Condic
2002-06-20 14:19 ` Stephen Leake
2002-06-20 15:37 ` Ted Dennison
2002-06-19 18:15 ` Stephen Leake
2002-06-20 1:35 ` Ted Dennison
2002-06-20 14:10 ` Stephen Leake
2002-06-20 20:50 ` Jacob Sparre Andersen
2002-06-20 20:58 ` Stephen Leake
2002-06-21 2:21 ` Ted Dennison
2002-06-24 13:22 ` Stephen Leake
2002-06-20 1:37 ` Ted Dennison
2002-06-29 11:03 ` Bobby D. Bryant [this message]
2002-06-29 12:17 ` Config_Files proposal {long} Bobby D. Bryant
2002-06-29 13:07 ` Mark Biggar
2002-07-03 14:10 ` Georg Bauhaus
replies disabled
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox