Hooks – Comparing Command Hooks vs Package Hooks for Best Practices

best practiceshookslthooks

In designing hook interfaces for my package. I found out that I have two methods of defining hooks:

\documentclass{article}

\begin{document}

\section*{Method 1: Use Command Hooks}

\NewDocumentCommand\MyPkgSomeHook{}{}

\AddToHook{cmd/MyPkgSomeHook/before}{hello}
\AddToHook{cmd/MyPkgSomeHook/before}{world}

\UseHook{cmd/MyPkgSomeHook/before}

\section*{Method 2: Use Package Hooks}

\NewHook{mypkg/somehook}

\AddToHook{mypkg/somehook}{hello}
\AddToHook{mypkg/somehook}{world}

\UseHook{mypkg/somehook}

\end{document}

Which method is better (in speed and others)?

Best Answer

Generic hooks (such as cmd hooks) are used in very specific places (cmd hooks are used when a command is called) and should not be manually used elsewhere. Nothing stops you from doing so, but that may cause confusion. Generic hooks are treated differently, since their definition (they are defined implicitly when you do \AddToHook{some/generic/hook}) to their usage (used automatically inside another command or environment), so you shouldn't use it as a normal hook.

The second option is the correct one: You create a normal hook called mypkg/something, and use it normally with \UseHook.

Also, using mypkg/somehook is a bit faster than using a generic cmd hook because the latter tries to patch a command at \begin{document}, which takes some (negligible) time, so there is no reason to misuse a generic cmd hook.


CAVEAT

Generic cmd hooks exist for a reason, of course! Suppose you wrote a nice package for typesetting tables, and you have a command like this:

\NewDocumentCommand \MagicTable { m m }
  {
    \begin{tabular}{rl}
      #1 & #2 \\
    \end{tabular}
  }

(not really impressive, I know :) and you want to, say, provide a hook for adding a header to that impressive table. If a user just did \AddToHook{cmd/MagicTable/before}{<header>}, the contents would be put outside of the tabular environment, and obviously not work. You could then provide for such users:

\NewDocumentCommand \MagicTable { m m }
  {
    \begin{tabular}{rl}
      \UseHook{cmd/MagicTable/before}
      #1 & #2 \\
    \end{tabular}
  }
\NewHook{cmd/MagicTable/before}

In this case, you would “make the generic hook normal”, and the hook system wouldn't try to patch the hook into \MagicTable because you already told it the hook exists (by doing \NewHook).

This example is a bit obvious because tabular material simply doesn't work outside of the tabular environment, but in other cases, the package author could fine-tune the position of the cmd/.../before and cmd/.../after hooks after people have been using such hooks.

But if you are providing new functionality that is not simply “run this bunch of code before that command”, the correct alternative is to use a normal hook name.

Related Question