Tips for LaTeX3 Beginners

Tips for LaTeX3 Beginners

August 28, 2022
Last updated February 12, 2023
Some tips for LaTeX3 beginners as well as useful reference to other tutorials and documents.
Feng Kaiyu
While the current stable version is LaTeX2e, LaTeX3 is already delivered as expl3 as the “L3 programming layer”. It provides a unified programming layer for package authors and experienced users.
This blog post will not be a thorough tutorial, but rather a complement.

Where to Start?

Before using LaTeX3, you should have a basic idea of LaTeX such as syntax, toolchains, packages et al. Then, the following materials are what you need now.

Some Introduction blog posts


  • expl3: Referred when needed.
  • xprase: Help to define user command like \newcommand.

Minimal Example for Class Author

% main.tex \documentclass{mylib} \begin{document} \hello \end{document}
% mylib.cls \RequirePackage{expl3} \ProvidesExplClass{test}{2022-01-01}{0.1.0}{Minimal Example Class} % Expl3 syntax starts from here. \NewDocumentCommand { \hello } { } { hello,~world! \par 你好,世界! } % For CJK users. Or you may load `book` \LoadClass{ctexbook}

Global Variables and Local Variables

Global variables should be assigned globally and may get the proper value whenever you want.
Local variables do not have this assumption.

Equivalent to \renewcommand

Although xparse provides \RenewDocumentCommand as equivalent to renew a user command. But I suggest you only use it at the global level and with \NewDocumentCommand, in order to provide a user interface. In other circumstances, for example, to redefine a variable provided by other packages, then \cs_set:Npn should be your choice.
\RenewDocumentCommand sometimes does work as expected, while \cs_set:Npn always does.

Generate Variation

Usually, expl3 only ship functions with common variants: there is \tl_if_empty:NTF and also \tl_if_empty:nTF . But if you want to expand the first variable, then you don’t have \tl_if_empty:xTF by default.
\cs_generate_variant:Nn is the one you need to generate these variations.
\tl_new:N \tmp_a_tl \tl_new:N \tmp_b_tl \tl_set:Nn \tmp_a_tl { \tmp_b_tl } \cs_generate_variant:Nn \tl_if_empty:nTF {x} \tl_if_empty:nT { \aaa } {hi} \tl_if_empty:xT { \aaa } {hello} % The output will be `hello`.

l3keys usage

In expl3 , module l3keys provides a consistent approach for defining and processing the key-value interface.
In practice, it’s recommended to provide a \setup{} for all your configuration. Users can call this interface anywhere they like.
% main.tex \documentclass{mylib} \Setup{ author = Feng } \begin{document} \hello % `hello I'm Feng` { \Setup{ author = Kaiyu } \hello % `hello I'm Keiyu` } \hello % `hello I'm Feng` \end{document}
% mylib.cls \RequirePackage{expl3} \ProvidesExplClass{test}{2022-01-01}{0.1.0}{Minimal Example Class} % Expl3 syntax start from here. \keys_define:nn { mylib } { author .tl_set:N = \l_mylib_author_tl } \NewDocumentCommand \hello { } { hello~I'm~\l_mylib_author_tl } \NewDocumentCommand \Setup { m } { \keys_set:nn {mylib} {#1} } % For CJK user, or you may load `book` \LoadClass{ctexbook}
As the example above, leveraging the scope mechanism (\group_begin: \group_end:) empowers the user to alter the config locally.
l3keys can create an interface with multi-level keys. Please read the document for the information.

Processing Document Options before loading the class

xparse package provides a high-level interface for producing document-level commands. However, these commands can only be used in the preamble and text region.
Sometimes, we want to configure other packages or the underneath class we loaded. That’s where document options come in handy.
% main.tex % \documentclass[]{mylib} \documentclass[oneside]{mylib} \usepackage{layout} \begin{document} % Print oneside layout. \layout \end{document}
% mylib.cls \RequirePackage{expl3,l3keys2e} \ProvidesExplClass{mylib}{2022-01-01}{0.1.0}{Minimal Example Class} % Expl3 syntax start from here. \keys_define:nn { mylib } { oneside .bool_set:N = \l_mylib_option_oneside_bool } % Process document options via l3keys. \ProcessKeysOptions { mylib } \bool_if:NT \l_mylib_option_oneside_bool { \PassOptionsToClass{oneside}{ctexbook} } % For CJK user, or you may load `book` \LoadClass{ctexbook}

Local Variable Initialization

It should be noted that l3keys will automatically create variables for you so there is no need to use \xx_new:N When defining keys. But in other cases, skipping from the initialization will lead to Undefined Control Sequence error.
In the edge cases, users might not call \Setup at all. So it’s up to you to initial the variable or leave it in an invalid state.
You should also be aware of writing pure function initial code in case of being called multiple times.

Special Thanks

If you are familiar with Chinese, I recommend you refer to fduthesis when you want to see more best practices in production code. It really helped me a lot.