Greetings,
As I’ve been learning Guix, one of the things I’ve found somewhat
unpleasant is the lack of consistency within the guix CLI tool.
It feels a bit Git-like, with not much consistency, commands that
non-obvioulsy perform more than operation, related commands in
different places in the tree, etc.
Just so you know where I’m coming from: I’ve found that compliex
CLI tooling benefits from organization and consistency. The Linux
ip(8) command is a good example of this kind of organization: to
add an IP address, you use `ip address add'. To show address, `ip
address show', and to remove one `ip address del'. When options
are needed, they get added after the verb or branch in the verb
tree; the final verb may take positional arguments as well as
--long or -s (short)-form options.
Some examples of where I think Guix could do better. This is an
illustrative list, not an exhaustive one.
Inconsistent organization
=========================
Most package-related commands are under `guix package', but many
are sibling commands. Examples are `guix size', `guix lint',
`guix hash', etc.
Inconsistency between verbs and options
=======================================
Some verbs are bare-word positional arguments, and others are
flags to related verbs. IMO, this is the biggest problem, and
makes it very difficult to find all the things the CLI can do.
`guix package' is a major offender in this area, as it mixes verbs
and verb-specific options into the same level. For example,
installing a package is `guix package -i foo' rather than `guix
package install foo', removing is `guix package -r foo' rather
than `guix package remove foo', and listing installed packages is
`guix package -I' rather than `guix package installed' (or
similar).
This means that users can express commands which *seem* like they
should work, but do not. For example `guix package -i emacs -r
emacs-pgtk -I' represents a command to 1) install emacs 2) remove
emacs-pgtk 3) list installed packages (which would verify the
previous two operations occurred). This is a valid command within
the accepted organization of `guix package', and doesn’t cause an
error, but doesn’t work: the install and remove steps are ignored.
A thing I’ve found throughout my career is that designing systems
so it’s *impossible* to represent unsupported, nonsensical, or
undefined things is an extremely valuable technique to avoid
errors and pitfalls. I think Guix could get a lot of mileage out
of adopting something similar.
This causes a related problem of making it impossible to know what
options are valid for what verbs. Will `guix package --cores=8 -r
emacs' remove the package while using eight cores of my system?
Will `guix system -s i686 switch-generation 5' switch me to a
32-bit version of generation 5? If verbs are organized better,
and have their own options, this ambiguity vanishes.
More inconsistency
==================
Other parts of guix have the opposite problem: `guix system
docker-image' probably ought to be an option to `guix system
image' rather than a separate verb.
Inconsistency between similar commands
======================================
There are generations of both the system (for GuixSD) and the user
profile, however, they work differently. For the system, there’s
`guix system list-generations' and `guix system
switch-generation', but for the user profile, you need `guix
package --list-generations' and `guix package
--switch-generation=PATTERN'. Additionally, no help is available
for either of the system commands: `guix system switch-generations
--help' gives the same output as `guix system --help' -- no
description of the supported ways of expressing a generation are
available.
Flattened verbs
===============
Related, the generation-related commands under `guix system' ought
to be one level deeper: `guix system generation list', `guix
system generation switch' etc.
Repeated options
================
Many commands (`guix package', `guix system', `guix build', `guix
shell') take -L options, to add Guile source to their load-path.
This probably ought to be an option to guix itself, so you can do
`guix -L~/src/my-channel build ...'.
Suggestions
===========
All commands should be organized into a tree of verbs.
Verbs should have common aliases (`rm' for `remove', etc).
Verbs should be selected by specifying the minimum unambiguous
substring. For example `guix sys gen sw' could refer to `guix
system generation switch'.
Options should be applicable to each level of the tree, ex `guix
-L~/src/my-channel' would add that load-path, which would be
visible to any command.
Requesting help is a verb. Appending "help" to any level of the
verb tree should show both options applicable to that verb, and
its child verbs. `guix help' would show global options and all
top-level verbs (package, system, generation, etc); `guix package
help' would show package-specific options and package-specific
verbs; and so on.
Conclusion
==========
I have no idea if anyone feels similarly about this, or whether
there’s appetite to change the CLI, but I think it’d be time
well-spent. Having some kind of agreed-upon standard for how the
CLI stuff is organized seems like a good thing to me, even if it
just stops or slows the addition of more commands with unique
expressions.
It seems like a lot of work to change, and backwards compatibility
also is an issue. One option I could see working is shoving the
entire existing structure under a command in the new tool, so you
could do `guix old package -I' or similar. An alternate approach
would be using an environment variable to change behaviors.
What do you all think?
Thanks,
— Ian