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
Documentations
- 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
\cs_set:Npn
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.