[darcs-devel] Darcs Argument Handling
Ben Franksen
ben.franksen at online.de
Sun May 4 00:02:24 UTC 2014
Let me continue this thread by taking a slightly broader perspective.
Why is the darcs UI code such a mess?
=====================================
One reason is the DarcsFlag type.
One the one hand, DarcsFlag is designed to closely correspond to the actual
words given on the command line or in a defaults file. (The correspondence
is almost 1:1, except for aliases.) This is not yet a problem per se.
But on the other hand, [DarcsFlag] permeates throughout all the command
implementations, and this is indeed a problem. Because as a result the
command internal logic is fraught with and mixed up with details of *how*
the user specified options, instead of concentrating on *what* has been
specified.
Just to illustrate my point, look at the functions hasEditMetadata and
getAuthor in Darcs.UI.Commands.Amend: they do low-level list handling to
extract information about what to do from the raw list of flags.
The mix-up of the "how" and "what" of command options has one additional
disadvantage: there is no single place where we can find what a command does
by default. It's all finely dispersed throughout the command
implementations:
> grep '`elem` opts' src/Darcs/UI/Commands/*.hs | wc -l
110
Consider a simple option of the yes/no type, such as EditDescription /
NoEditDescription. A command implementation could take decisions, based on
the outcome of
- EditDescription `elem` opts
- NoEditDescription `elem` opts
There is no way to predict from the outside which of them the implementation
choses and therefore no way to predict what happens in case neither option
is in the list, or both of them are.
What can we do about it?
========================
The design space is large, but let me present a strawman proposal so you
have something to pick apart.
We replace the use of [DarcsFlag] in command implementations with one common
large record type that contains an entry for each possible option. A common
name for such a thing is Config, and it would look like this:
data Config = Config {
help :: Bool,
listOptions :: Bool,
test :: Bool,
changesToAllFiles :: Bool,
leaveTestDir :: Bool,
timings :: Bool,
verbosity :: Verbosity
...
author :: Maybe String
...
editDescription :: Bool,
...
diffAlgorithm :: DiffAlgorithm
...
}
with some helper data types e.g.
data DiffAlgorithm = Myers | Patience
-- are these in fact meant to be mutually exclusive?
data Verbosity = Quiet | NormalVerbosity | Verbose
| Debug | DebugHTTP | DebugVerbose
Stuff like prompting for an author or selecting it from a list (if not given
on the command line) should be done in helper functions outside of command
implementations, if possible.
It may be difficult to come to a conclusion how to represent each option,
considering that some of the options may be interpreted differently by some
commands. But I don't regard that as a deficiency of the proposal. Quite the
opposite, in fact: it encourages us to think about, review, and then define
option semantics once and for all in a systematic way. For instance, we will
have to decide how to handle conflicting options given at the same level of
precedence. This is currently more or less undefined, see example [1] below.
The next step is to define one(!) function
defaultConfig :: Command -> Config
that returns the hard-coded defaults for a given command (most of the
returned values will just be references to a common default, with one or two
exceptions, where commands have differing defaults for the same option).
Command line arguments and defaults from the two configuration files are
each independently converted to a [DarcsFlag]:
cmdlineArgs :: IO (Command, [DarcsFlag], ExtraArgs)
userDefaults, repoDefaults :: Command -> IO [DarcsFlag]
and the resulting three lists of DarcsFlag are combined using a single
function
applyFlags :: Command -> [DarcsFlag] -> Config -> Config
I am willing to engage in an effort to design the Config record and replace
[DarcsFlags] with Config in the command implementations, but I would like to
hear what you guys think before I start with this in earnest. It is also
necessary to clarify resp. reach consensus about which of the many multiple-
choice option groups (see Darcs.UI.Arguments) are in fact meant to be
mutually exclusive (I guess most are), and which are really meant to be
multiple-choice. And how to represent the latter ones in the Config type.
Cheers
Ben
[1] Currently we have
darcs send --sign
= darcs send --sign --no-sign
= darcs send --no-sign --sign
so in this case --sign wins over --no-sign. This is an artefact of the
implementation, which uses code like
if Sign `elem` flags then ...
If it did instead
if NoSign `elem` flags ...
it would be the other way around.
--
"Make it so they have to reboot after every typo." -- Scott Adams
More information about the darcs-devel
mailing list