Write with expl3 | 1) How to properly write the initialization part with expl3 syntax?

As stated in a previous question, I wish to switch my class written with LaTeX2e to the expl3 syntax. I will try to do this by asking a series of questions, collecting code examples, making the transition gradually (quoting @PhelypeOleinik). In this way, I'll be able to reduce the risk of violating expl3 conventions.

I begin with the initialization part, for example processing options and loading classes. What is the proper way to achieve the effect of the following code with expl3 syntax?

% (Package initialization)
\NeedsTeXFormat{LaTeX2e}
\ProvidesPackage{packagename}
[2021/03/09 ...]
\RequirePackage{kvoptions}
\SetupKeyvalOptions{%
family = packagename,
prefix = packagename@
}
\DeclareBoolOption[false]{off}

\ProcessKeyvalOptions*\relax

% (Class initialization)
\NeedsTeXFormat{LaTeX2e}
\ProvidesClass{classname}
[2021/03/09 ...]

\RequirePackage{kvoptions}
\RequirePackage{etoolbox}

\SetupKeyvalOptions{
family = classname,
prefix = classname@,
setkeys= \kvsetkeys,
}
\DeclareStringOption[some-string]{option-name}
\DeclareStringOption{option-with-no-default-value}
\DeclareVoidOption{other-string}{\kvsetkeys{classname}{option-name = other-string}}

\DeclareDefaultOption{\PassOptionsToClass{\CurrentOption}{\classname@baseclass}}
\ProcessKeyvalOptions*\relax



Maybe there're better ways with expl3, I'm just including these LaTeX2e code as a comparison.

• you are starting at the wrong place. Most of these commands have either no expl3 version or are (like the keys) not trivial to replace. Mar 9, 2021 at 16:15

Here's something to give you a start. That part of the code is mostly option handling, so I suggest you take a look at the l3keys module documentation which covers the available key handlers. I'll explain very briefly, because there are a lot of small concepts here in this piece of code.

First we start by declaring the format needed, and loading l3keys2e for the class option parsing:

\NeedsTeXFormat{LaTeX2e}
\RequirePackage{l3keys2e}


then we declare the class (or package):

\ProvidesExplClass{classname}
{2021/03/09} {1.0} {My class to learn expl3}


The command \ProvidesExpl<thing> already does \ExplSyntaxOn for you, and takes care of switching it off at the end of the class/package, so this is the proper way to start an expl3-based class.

Now define a constant string (constant means it shouldn't be changed with \str_set:Nn or something like that) to hold the base class name. The prefix \c_... indicates it is a constant (l is local and g is global), and the suffix ..._str means it's a string variable:

\str_const:Nn \c__classname_base_class_str { book }


Now we'll define some options for your class called classname:

\keys_define:nn { classname }
{


First what kvoptions calls a string option (one which takes a value):

    % \DeclareStringOption
, option-name .tl_set:N = \l__classname_option_name_tl
, option-name .initial:n = { some-string }


you use it as option-name and the value given to it will be stored in \l__classname_option_name_tl (a local tl variable). The initial value is some-string. One peculiarity of \keys_define:nn is that it already creates the variables for you, so you don't need to do \tl_new:N \l__classname_option_name_tl beforehand (but it doesn't hurt either).

Now what kvoptions calls a void option, one which just executes a piece of code:

    % \DeclareVoidOption
, other-string .code:n =
{ \keys_set:nn { classname } { option-name = other-string } }


When used, the other-string option will do the same as option-name=other-string.

Now a boolean option, whose initial value is false:

    % \DeclareBoolOption
, off .bool_set:N = \l__classname_off_bool
, off .initial:n = { false }


And finally, you tell l3str how to handle unknown options:

    % \DeclareDefaultOption
, unknown .code:n =
{
\iow_term:x
{
Passing~option~\CurrentOption \c_space_tl to~
\str_use:N \c__classname_base_class_str
}
\PassOptionsToClass { \CurrentOption } { \c__classname_base_class_str }
}
}


It will print "Passing option to " in the terminal. \iow_term:n writes to the terminal, and \iow_term:x does an "exhaustive expansion" of the argument before passing it to the underlying n variant.

Now process the options and load the base class:

\ProcessKeysOptions { classname }


As an example code, you can use \bool_if:NTF to test a boolean variable:

\bool_if:NTF \l__classname_off_bool
{ \iow_term:n { off~true } }
{ \iow_term:n { off~false } }


and again \iow_term:x to write to the terminal:

\iow_term:x { Value~of~option-name:~\tl_use:N \l__classname_option_name_tl }


Putting it all together:

\begin{filecontents}{classname.cls}
\NeedsTeXFormat{LaTeX2e}
\RequirePackage{l3keys2e}
\ProvidesExplClass{classname}
{2021/03/09} {1.0} {My class to learn expl3}

\str_const:Nn \c__classname_base_class_str { book }

\keys_define:nn { classname }
{
% \DeclareStringOption
, option-name .tl_set:N = \l__classname_option_name_tl
, option-name .initial:n = { some-string }
% \DeclareVoidOption
, other-string .code:n =
{ \keys_set:nn { classname } { option-name = other-string } }
% \DeclareBoolOption
, off .bool_set:N = \l__classname_off_bool
, off .initial:n = { false }
% \DeclareDefaultOption
, unknown .code:n =
{
\iow_term:x
{
Passing~option~\CurrentOption \c_space_tl to~
\str_use:N \c__classname_base_class_str
}
\PassOptionsToClass { \CurrentOption } { \c__classname_base_class_str }
}
}
\ProcessKeysOptions { classname }