Hello guilers, I'm developing Emacsy[1], and I'm trying to design a good way of grouping and exposing interactive commands. My interest is specifically related to Emacsy, but for guilers in general the question is, How should one provide exports of a different flavor? We have only "public" exports. In my case I want a "command" flavor of export. But I might want to export "customizable" variables, or "minor-mode"s. I'm not trying to tackle the general task here.
In Emacs all commands are in the same namespace. Hit M-x TAB, and you'll probably see thousands of them (I've got ~6k). A lot of the commands may only be appropriate in certain modes, so I'm playing around with how best we can go about grouping and exposing commands. My idea is that M-x only shows commands that are in the current COMMAND_PATH, somewhat like a bash PATH. To show all commands, one can type C-u M-x. A command in Emacsy is a procedure that requires no arguments. It's written for the user to invoke on command M-x or from a key binding. When called interactively, it'll ask the user for information. When called with all arguments provided, it will run non-interactively. Suppose I have the following code which creates a command called 'echo': (define-module (my-great-mode) #:use-module (ice-9 optargs) #:use-module (emacsy) #:export (some-other-proc)) (define (some-other-proc) "I am not a command." #f) (define* (echo #:optional (text (read-from-minibuffer "Echo what: "))) (message text)) (export-command echo) This module exports the procedure "some-other-proc" and I want it to _somehow_ export a command called "echo". The question is how to export this other thing, this command? I have a couple of ideas, most based off grouping some subset of procedures as commands inside a module, and using modules to group the commands. 1. Create a command-interface module similar to the public-interface that modules have. So instead of 'resolve-interface' and 'export', I'd have 'resolve-command-interface' and 'export-command'. A (resolve-command-interface '(my-great-mode)) would return a module (%module-command-interface) that contained bindings for all that module's commands. This is the module that COMMAND_PATH would use for commands. PRO: One could export commands and procedures entirely independently. Also, one can use the module mechanisms of selecting a subset, algorithmic renaming, etc. on these command modules. CON: It seems like a lot of magic, but it's using the same mechanisms that public interfaces use. 2. Have (export-command) just keep a list of commands, but just export a custom public interface. So it might look like this internally: (define (export-command-proc names) (set! %command-set (append! %command-set names))) (define (resolve-command-interface module-name) (resolve-interface module-name #:select %command-set)) PRO: Less magic-y. CON: One must export a command to both the public interface and the command; maybe that's ok even preferable that they're not independent. 3. Have (export-command) create a new sub-module (my-great-mode %command). PRO: Not too tricky. CON: Pollutes the module namespace a little bit. 4. Still use modules, but make the user segregate their procedures and commands into separate modules manually. So the user would have to define modules (my-great-mode) and (my-great-mode commands). PRO: No magic. Programmer does everything. CON: Everything is managed by convention. Separating the commands and procedures might be an awkward boundary. Consider an internal procedure in (my-great-mode) that a command wants to use in (my-great-mode commands). Do you expose it as public? Move it around? Use @@ to get to it? 5. Don't use modules at all. Make the user collect and manage <command-set>s the same way they manage keymaps, for instance. PRO: Nothing tricky, doesn't use modules at all. No magic. CON: It has to work with the command being redefined; we can't just keep the command procedure; we have to keep the variable which points to modules possibly being a better solution. 6. Tag commands as special procedures perhaps by adding something to their procedure properties. Implement a "command?" procedure. Export commands and procedures to the same module. Then just pluck the commands out of all the procedures by using command?. PRO: No new modules. No new exports. CON: Adding something to the procedure makes wrapping commands in other lambdas awkward. Might necessitate a define-command, lambda-command, method-command, which I'd prefer to avoid. * * * Thanks for indulging my scheme design question. What's the right thing to do? What would you do? -Shane [1]: https://github.com/shanecelis/emacsy