The LaTeX3 module l3msg provides some nice functions for messaging, errors, warnings and the like and is truly flexible. The workflow is different from what we have been using for LaTex2e packages. What are the best practices in this respect?

The way I see, some good practices (as also used in other computer languages) are:

  1. Wrap your own error, warning functions etc, on top of the l3msg ones.
  2. Create some booleans to signal errors and warnings (even if you are not using them).
  3. Unsure as to how best to arrange internationalizing messages, one way is to aggregate all the \msg_new etc in files and load them based on a language switch. This would then be similat to gettext. A second idea is to store them as token lists (strings) and use a translator type mechanism to translate them. A preferred way is to have a higher hierachy function:

For example instead of writing this:

    \msg_set:nnnn {mypackage} { old-version }
            { LaTeX~source~files~more~than~5~year~old.~ Is~dated~

the strings should be in tranlation files, marked in directories en etc. with a format such as


In the German file maybe something like:

   __str{old-version}{LaTeX olde ...}}

Then the \msg_set function could be modified to call:

    \msg_set:nnnn {mypackage} { old-version }

Partial MWE shown below (just to experiment).

    % Error message example
    % simulate LaTeX2e \fmversion

    % create error boolean
      \bool_new:N \l_mypackage_error_bool

    % redirect package errors here
      \cs_new_protected:Npn \mypackage_warning:nxx #1#2#3 {
          \bool_set_true:N \l_mypackage_error_bool
          \msg_info:nnxx { mypackage } {#1} {#2} {#3}

    % define some error messages     
       \msg_set:nnnn {mypackage} { old-version }
       { LaTeX~source~files~more~than~5~year~old.~ Is~dated~(year:#1~date:#2-#3) }
       { Please~update~your~distribution~visit~ctan } %(*@\label{test}@*)

    % check version number          
        \cs_new:Npn \mypackage_check_version:n #1 {
           \exp_after:wN \l_mypackage_check_version_aux:w #1\q_stop

    % check version number auxiliary                        
        \cs_new:Npn \l_mypackage_check_version_aux:w #1/#2/#3\q_stop {
            \int_compare:nNnTF  { (\tex_year:D-#1)*12 + (\tex_month:D-#2) } > { 65 }
                { FAIL\\ \mypackage_warning:nxx {old-version} {#1} {#1/#2/#3} }
                { PASS\\ } 

      \mypackage_check_version:n \fmversion 
      \mypackage_check_version:n \fmtversion 
  • Just a comment: expl3 also tries to globalize the indentation and spaces in code, I think you should try go follow that. The nice thing is that one gets quite fast at reading “well spaced/indented code”.
    – Manuel
    May 25 '15 at 10:13
  • @Manuel Thanks. Old habits die hard:) Can't get used to { some } etc easily. Will you edit if you can and fix it a bit. May 25 '15 at 10:19
  • @Manuel I've got to say that I like OTB-style much better than GNU; the latter is harder to enforce from an editor-support perspective with a language like TeX. But, it is what it is :) May 25 '15 at 15:02
  • On the coding style: the position we have is a compromise between different views of the team. The key idea was that we need some kind of 'official' position: its better to have some agreed style than nothing at all.
    – Joseph Wright
    May 26 '15 at 7:38

As you note, l3msg is designed rather differently from the LaTeX2e messaging system. There were two key design ideas behind this

  • Message text should not be needed where the message is used. This is primarily possible as we have lots of 'space' available in TeX today, something that was not true when LaTeX2e was written. As such, expl3 generally encourages using lots of small functions/variables rather than monolithic code. The outcome for messages is that it makes much more sense if a function has the concept 'use message X here' rather than all of the text. There are knock-out effects of this, for example the potential for localisation but also the fact that each message has a defined name.

  • The number/nature of messages should be controllable by the end user. Not everyone wants to have warnings about stuff they don't care about, etc., so this is an important ability and really needs to be built in.

In terms of best practice, provided you keep the above in mind you should be fine. On the idea of wrappers, I'd say it's down to the programmer. Using the kernel level code directly

\msg_error:nn { mymodule } { drat }

is fine for a one-off and is immediately clear, but there is advantage for larger modules in keeping things shorter

\mymod_error:n { drat } % Or \@@_error:n { draft } using l3docstrip

Setting up boolean return values very much depends on the meaning of errors/warnings/... in the context of the code involved: that's one reason we did not add a 'generic' mechanism. One thing to watch here is the point about message filtering: just because you have it down as an error doesn't mean the user won't turn it off. If you are using a boolean return system then it's likely you'll also want a wrapper, of course.

On the language of messages, whilst this was an obvious benefit of having a named message system it's not the key reason for it. As such, this area is as-yet under-explored so the following is somewhat speculative. I'd say a translator-like mechanism is probably not the way to go here. Message localisation should apply across an entire run, so there should not be a need to have them dynamically change. As such, one might imagine a set of messages in different files or similar (one per language), where the text is set using \msg_set:nnn(n). This might look for example like

% Core messages always in English
\msg_new:nnn { mymodule } { drat } { Drat! }
\file_input:n { mymodule-messages- \l_interface_language_tl }


% mymodule-messages-de

\msg_set:nnn { mymodule } { drat } { Achje! }

% mymodule-messages-fr

\msg_set:nnn { mymodule } { drat } { Zut! }

Importantly, changing the entire content of a message means more than swapping the words (which is what translator does): grammar changes and so forth make life more complex.

A long-term aim is that messages should be documented both to enable localisation and automated parsing (one could turn off the 'user' text entirely and just return the message name and arguments). What I think would be ideal is a listing specifying the name of the message, the context it is used in and the meaning of the argument(s) it takes. This can then be used for a variety of purposes.

One area not mentioned in the question but important to consider is 'trace' code. Originally we provided a trace message class, but this was removed. The logic here was that for very low-level tracing, the rather 'rich' message redirection system in l3msg is the wrong approach (there is a performance cost). As such, if you do wish to provide very detailed trace data you should be using the low-level \iow_log:n with a suitable wrapper

% If not tracing
\cs_new_eq:NN \mymodule_trace:n \use_none:n
% If tracing
\cs_new_eq:NN \mymodule_trace:n \iow_log:n
  • Thanks! In the second bullet messages should be controlled by end user. I guess end user if author the package should have a key such as keepsilent? A simpler approach is to not interface it at all but just set count255 to trigger \batchmode, again via key or command. On the localization I am not sure if the approach or my suggestions for that matter are solving the issues. May 26 '15 at 11:10
  • The trace class is an interesting idea. Most languages have a logger object. This should be better as a separate module. It can also decongest some of the info currently developers put in the .log file. If only we were not limited on the number of writes. May 26 '15 at 11:12
  • @YiannisLazarides We've not provided a document-level interface at present, but the point about the 'end user' is that messages are well-defined (have names) and can be controlled using a documented (code level) interface. At some stage we may provide a document-level version of the same. (`batchmode` doesn't prevent material being written to the log.)
    – Joseph Wright
    May 26 '15 at 12:32
  • @YiannisLazarides On logging, the whole point of the .log file is it contains everything :-) I'm not sure I see value is doing trace work in a separate module as the whole point of pointing to the low-level code is that tracing needs to be fast. (Indeed, with DocStrip one might produce an entirely separate trace build, although for users that is possibly more effort than its worth.) Note that in the main I'm thinking of trace output for developers: it's rare that it's useful to an end user.
    – Joseph Wright
    May 26 '15 at 12:35

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.