Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request: define a user-level nomenclature that reflects what transient does from a user experience perspective #239

Closed
Trevoke opened this issue Mar 1, 2023 · 4 comments
Labels
enhancement New feature or request

Comments

@Trevoke
Copy link

Trevoke commented Mar 1, 2023

I like transient. I think it's fantastic. I'm using it in one of my packages and proud to have it as a dependency.

(fallback request: please add a nomenclature page to the documentation so I can see all the new words I have to learn at once, instead of having to parse sentences with unknown words and have my brain forced to figure out a meaning when all the important words are words that don't have meaning for me)

(fallback-fallback request: create a new introduction that's an actual introduction to what transient does and rename the existing introduction to "lexicon ", or something like that)

This being said, every time I open the documentation, which ... isn't all that often, I have to relearn it, which, for me, is made more difficult by the language, which seems to be internal API language:

  • what's infix?
  • what's suffix?
  • what's prefix?
  • what's group?
  • what's children?
  • what's obj?

So basically reading documentation becomes a game of reading a sentence, making a list of the questions I need to answer in order to make sense of the sentence, and cycling through this until I can start popping the stack of questions to get back to what I was reading in the first place.

When I think about transient, I think of it in terms of:

  1. interactive command that the user can call to bring up the menu
  2. sections of the menu
  3. key bindings for the commands
  4. modifiers for the commands
  5. descriptions for the commands
  6. commands

So I think that, after reading the documentation for 15 minutes:

  • infix is probably a modifier
  • suffix is probably a command
  • command includes a function, a key binding, a description
  • group is probably a section of the menu
  • the prefix itself is the interactive transient function called to pop up the menu

Of course this isn't perfect because everything can be defined in terms of everything else, so when I read something like:

Note that an infix is a special kind of suffix. Depending on context “suffixes” means “suffixes (including infixes)” or “non-infix suffixes”.

I translate it to "I might be reading about modifiers and I might be reading about a function to be executed" and if this isn't correct, well, I need to read more documentation to correct it, but ....

Frankly, maybe I'm just stupid, but I think I shouldn't be struggling this much with understanding this many sentences of the documentation.
Hence my request.


So if I decided to rewrite the introduction with this new style of words, I would find something like this:

Taking inspiration from prefix keys and prefix arguments, Transient implements a similar abstraction involving a menu, modifiers and commands. We could call this abstraction a “transient command”, but because it always involves at least two commands (a menu and a command) we prefer to call it just a “transient”.

Transient keymaps are a feature provided by Emacs. Transients as implemented by this package involve the use of transient keymaps.

Emacs provides a feature that it calls prefix commands. When we talk about “menu commands” in this manual, then we mean our own kind of “prefix commands”, unless specified otherwise. To avoid ambiguity we sometimes use the terms transient menu for our kind and “regular prefix command” for the Emacs’ kind.

When the user calls a transient menu, a transient (temporary) keymap is activated, which binds the transient’s modifiers and commands, and functions that control the transient state are added to pre-command-hook and post-command-hook. The available commands and modifiers and their state are shown in a popup buffer until the transient state is exited by invoking a command.

Calling a modifier causes its value to be changed. How that is done depends on the type of the modifiers. The simplest case is a modifers that represents a command-line argument that does not take a value. Invoking such a modifier causes the switch to be toggled on or off. More complex modifiers may read a value from the user, using the minibuffer.

Calling a command usually causes the transient to be exited; the transient keymaps and hook functions are removed, the popup buffer no longer shows information about the (no longer bound) commands, the values of some public global variables are set, while some internal global variables are unset, and finally the function is actually called. commands can also be configured to not exit the transient.

A command can, but does not have to, use the modifiers in much the same way any command can choose to use or ignore the menu arguments. For a command that was invoked from a transient, the variable transient-current-commands and the function transient-args serve about the same purpose as the variables menu-arg and current-menu-arg do for any command that was called after the menu arguments have been set using a command such as universal-argument.

I think my translation is probably not perfect, but I think this makes it way easier to read and understand, without having to translate transient's internal language into what I want to do with it.

Anyway, thoughts? Would this kind of adjustment in the documentation break anything major and meaningful that I don't understand because, well, I don't understand the manual?

@tarsius
Copy link
Member

tarsius commented Mar 4, 2023

Yes, to lots of that.

I am trying to take things a bit easy for a while and focus on important things in my live, see https://www.reddit.com/r/emacs/comments/11f7urf/thanks_for_your_support_and_patience/.

Improving transient's documentation is something that will have to come later, but among the delayed work, it's one of the things I plan to tackle early when I return recharged.

Also see the third-party resource mention on the first page of the manual.

@tarsius tarsius added the enhancement New feature or request label Mar 4, 2023
@tarsius
Copy link
Member

tarsius commented Mar 13, 2023

A major difficulty in writing an introduction for this package is that different users have vastly different levels of understanding and thus needs. The current description is being criticized as assuming too much prior understanding; while a future description will likely be perceived as way too verbose.

(Edit: Another risk that comes with discussing everything in great detail upfront, is that that could lead to the impression of accidental complexity. I do not think that perception is/would be justified, the complexity is due to, and enables, the power and flexibility of the abstraction. But it certainly would be overwhelming to cover everything upfront. It is challenging to find the right balance between "lying for students/users" and describing every term and concept in great detail (and without assuming prior knowledge and reflection on similar concepts) as soon as it is first mentioned.)

The current description was written with people in mind who already used the predecessor magit-popup to implement similar interfaces. A major goal that guided how the introduction was written, was to convince them to migrate from magit-popup to transient. It describes the basic building blocks in a way that, back when this package was introduced, helped those readers understand that the building blocks are not the same as in the predecessor.

I.e., when it talks about prefix/infix/suffix commands, then it does this because it is important to understand that all these "things" are commands, and in what relation they stand to each other. In magit-popup that wasn't the case; it used inferior mechanism that restricted what it could do.

Obviously this should be rewritten now, but it isn't clear to me how to do that yet. Feedback can help--I will probably ask you to provide feedback on a draft or two.

The current introduction begins with:

Taking inspiration from prefix keys and prefix arguments, ...

It is important to reflect on how these features are implemented at this point. Everything that follows depends on the reader understanding what those features do and how they are implemented. (Obviously I cannot expect that from every user of Transient/Magit, so this will have to be updated.)

Speaking of feedback, please give it a try. Think about Emacs' "prefix commands/keys" and "prefix arguments" for a few minutes. Maybe even lookup the documentation. Then reread Transient's introduction. Does it make more sense now?

Also, have you looked at the diagrams in "Comparison With Prefix Keys and Prefix Arguments"? Does the introduction make more sense after staring at those for a while?

I think my translation is probably not perfect, but I think this makes it way easier to read and understand, without having to translate transient's internal language into what I want to do with it.

Anyway, thoughts? Would this kind of adjustment in the documentation break anything major and meaningful that I don't understand because, well, I don't understand the manual?

I am afraid the answer to that is yes. E.g., "infix command" is precise; it describes that this is implemented as a command and "infix" means that it is invoked after the prefix command and before the suffix command. But it is assumes that reader makes a connection between Emacs' "prefix arguments" (implemented as commands) and Transient's "infix arguments" (also implemented as commands). "Modifier" on the other hand means everything and nothing.

Finally, have you seen the link at the end of the description: https://github.com/positron-solutions/transient-showcase? Does that document make things clearer? I do intend to take inspiration from that.

Ps: It might seem that I am too much focused on defending the existing documentation. That isn't my goal, I am aware it is no good, but writing this also helps me to reflect on how it could be improved and sharing those thoughts might or might not help others along the way.

@Trevoke
Copy link
Author

Trevoke commented Mar 15, 2023

Hi Jonas, thanks for the detailed response. I'll do my best to add to it constructively.

Reference to emacs documentation

Maybe even lookup the documentation.
If this is important then there should be a link to it in the documentation itself -- "don't read this until you have read that".

Reference to other parts of the transient documentation

This ends up creating a bit of a catch-22 where I need to have read other parts of the transient documentation to read the introduction, which seems wrong. This being said, let's do it!

Your requests for "look at X and see if it helps" -- it will be tricky for me to answer effectively, because I have now completed my own internal translation map to "menu", "modifier", and "command", so I will be exploring all of this as a completely theoretical comparison. Your best UX tests for this will come from folks who haven't built their own internal translation for what the manual means.

Prefix keys

A "prefix key" is basically any non-final key in a key chord. C-x C-i C-o means C-x and C-i are prefix keys. Prefix keys can bind to completely separate keymaps, and I think they technically do, but they can be implicitly defined by just binding a command to a specific key chord (I am not 100% sure of this, actually).

If a key sequence invokes a command, we call it a complete key; for example, C-f, C-x C-f and C-x 4 C-f are all complete keys. If a key sequence isn’t long enough to invoke a command, we call it a prefix key

Prefix arguments

A "prefix argument" is ... Actually called a "numeric argument" in the emacs documentation table of contents anyway.

You can give any Emacs command a numeric argument (also called a prefix argument).

it is what we do most often with C-u, which is called the universal argument, to most commands, though it can be done with M-[number]. It's an emacs-provided behavior that we can hook into with user-defined code by leveraging a parameter called ARG, and through the (interactive) call.
Amusingly, C-u also has this special "multiply by four" behavior, but now we're too far off topic.

So, in short:

  • a prefix key is an incomplete key combination
  • a prefix argument is an emacs provided hook to pass further information into a command

I kind of imagine that maybe the first one is a transient prefix, and the second one is a transient infix, but then do you see how I'm confused when I try to figure out what a prefix is?

Okay, so, moving on to your suggestion to go look at the comparison, in the transient documentation

I discover MORE DOCUMENTATION

You have links to emacs documentation here which I did not uncover when I searched the documentation that I opened! For reference, I opened this: https://www.gnu.org/software/emacs/manual/html_mono/emacs.html

A prefix key is a key sequence whose binding is a keymap.

Okay! This sentence is equally clear about the definition of a prefix key as the ones I found, but makes it more clear why you defined the "command that opens the menu" as a "prefix".

I see that you actually referred to the Elisp manual, not the Emacs manual - I am looking at emacs as a user while you are looking at is as a coder, which is probably one of the first rifts in how we are looking at the world here.

The prefix argument is at all times represented by a value, which may be nil, meaning there is currently no prefix argument. Each command may use the prefix argument or ignore it.

Okay, well, again, looking at emacs through the lens of elisp vs the lens of a user changes what makes sense.

Infix is precise, modifier means almost anything

From the Merriam Webster, "infix" means

: a derivational or inflectional affix appearing in the body of a word (such as Sanskrit -n- in vindami "I know" as contrasted with vid "to know")

So an infix is an affix

one or more sounds or letters occurring as a bound form attached to the beginning or end of a word, base, or phrase or inserted within a word or base and serving to produce a derivative word or an inflectional form

The affix in the word "attendance" is "-ance."

Now, at this point, it's getting pretty clear that you're not using "infix" under this lens, so you're using it under some other lens. I search the emacs lisp documentation for the word "infix" and I can only find "infix operator", by which I then suppose that, if 3 + 4 means + is the infix operator, then using transient wording, 3 is the prefix operand and 4 is the suffix operand.
This probably is also not what you mean, because here the prefix and suffix are certainly not transitive and swappable.

So all of this leads me to a question: Where did you get the word "infix" from ? I can't figure it out and it is a large part of what confuses me to no end. I can't hang my thoughts on any mental model, so for me it's floating -- and "it's between the prefix and the suffix" doesn't help me because that doesn't give it meaning.

"Modifier", on the other hand, which could mean anything, is at least a "thing" for my mental model to hang on to. A "modifier" modifies the executed command. Within the context of "a menu that pops up and lets you customize and execute a command", I think that, while "modifier" could mean almost anything, it ends up meaning just enough.

Does the introduction make more sense after staring at those a while?

No, unfortunately, because I think of the command loop as the place where I could call a self-insert-command, and while I am willing to believe that the "Regular prefix arguments" diagram fits this properly, I end up confused by why there's so many arrows in it. I'm also not completely certain why it's relevant to mark in the diagram that the user chooses to do something -- because I assume that if we're in the command loop, the user can in fact choose to do something, so my brain trips when I see both, I end up checking for something I'm missing beyond what I think is just repetition.

Relative meanings?

Maybe the difference here is one of absolutes. I don't believe that I need to define "absolutely new language" for transient, I think I need to define internally self-consistent language.
After all, the "infix commands" are implemented as commands and are run between prefix and suffix, which also means everything and nothing; I suppose it's true that the infix commands don't need to be modifiers, they could just print out messages, or do nothing, but that's not their intended usage, right? Their intended usage is to provide additional context and customization to the command we are eventually trying to run when we hit the key that closes the menu and executes the command the user wants to run.

As a car mechanic, I know what a wrench is, but I might ask for the tool that lets me remove the tire; asking for the wrench is the universal tool, and I need to know it's a wrench if I'm gonna work on more than just the tires, but if my only context is working on the tires, then the tool has such a restricted use that it's more useful to the context to define it within the context (hence : menu, command modifier, command).

The internals of transient vs the externals of transient

I think transient deserves a public-facing API that provides a clear and unambiguous context to its users. While I am clearly partial to the words it took me all of four seconds to come up with, I also gladly agree that they're probably not great :D

I do think, however, that reserving a section of the documentation to the internal of transient would be very valuable (e.g. "under the hood, transient maps these keys to a keymap it creates for this specific menu", etc.)

The special case of a command which is a menu (a suffix which is a prefix)

This is of course not a special case at all, but I suspect we probably have to be able to pass state from one menu to the next, from one prefix to the next prefix through the suffix that summoned the prefix, along with of course whatever state was added by the infixes between the first prefix and the first suffix which set up the next prefix.

Okay, I've been writing this for an hour and a half, my brain is dead, if I missed something, forgive me and please remind me of what it is you wanted me to reply to or explore that I didn't.

And .... Please take care of yourself.

@tarsius
Copy link
Member

tarsius commented Oct 15, 2023

Closing in favor of #127.

@tarsius tarsius closed this as completed Oct 15, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants