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:NpnGenerate 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.