What is the difference between `put` and `gput` in `\tl_new:N` token lists?

Why is there one function to define (globally) e.g. a token list \tl_new:N, but there are two functions to put an element into it: \tl_put_left:Nn and \tl_gput_left:nN

Because all variables are supposed to be declared globally once, then used as needed. There is few to no use of a variable that is declared within a scope and ceases to exist afterwards. Such a variable would be a temporary variable, so it can be created globally (\tl_new:N \l_my_temp_tl, for instance) and then used locally for temporary storage. In fact, LaTeX2e's variables (counters, lengths, skips, IO streams, etc.) are all defined globally as well.

There are two functions for assignments, because differently from the declaration of a variable, its usage might need to be global or local, and that's why there are two commands. And global variables (whose name start with \g_...) should always use the global variants of the functions and so for the local ones. That is so because mixing them is bad practice and may cause TeX to deplete its save stack memory.

What does this g at the beginning of gput mean? “Global”?

Yes :-)

See section 3.2.3 Variables: scope and type of The expl3 package and LaTeX programming (texdoc expl3).

How can I put a local item in a global list (or vice versa)?

You can't. You actually can, but you definitely should not!

Now at the point of writing this question: can I make list modifications which don’t survive the current grouping with “local” modifications or what?

Yes. TeX's scoping model is the same in expl3. For instance this:

\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\tl_new:N \l_my_global_tl
\tl_set:Nn \l_my_global_tl { 0~ }
\tl_use:N \l_my_global_tl
\group_begin:
  \tl_set:Nn \l_my_global_tl { 1~ }
  \tl_use:N \l_my_global_tl
\group_end:
\tl_use:N \l_my_global_tl
\ExplSyntaxOff
\end{document}

prints 0 1 0, whereas the same document, replacing \tl_set:Nn by \tl_gset:Nn and \l_my_global_tl by \g_my_global_tl prints 0 1 1.


I see that Phelype has covered the key points: here I'll try to give some context for the approach.

To understand the ideas here, it's probably best to start with how TeX grouping works. As a macro expansion language, TeX has no concept of being 'inside' a macro, and so definitions do not form scopes. Instead, scopes are created explicitly by for example the primitives \begingroup/\endgroup, in expl3 referred to as \group_begin: and \group_end:.

This also means that local variables do not work quite the same as in some other languages. For example, with something like

\begingroup
  \def\foo{abc}
  \begingroup
  \show\foo

or in expl3 terms

\group_begin:
  \tl_set:Nn \foo { abc }
  \group_begin:
  \tl_show:N \foo

we see that \foo is defined and expands to abc: nested groups 'inherit' all local variables from their parent.

This is important as it influences what abstractions do and do not make sense on top of TeX itself. The LaTeX team at one point explored variable declaration at local scope: token lists are easy, but things like int or dim variables are slightly more complicated. (This stems from the fact that TeX registers are used for the latter cases.) However, in use it became clear that this didn't really work: the fundamentals of TeX grouping mean that you always need a \group_begin:/\group_end: pair and something like \tl_new_local:N was potentially misleading.

The result is that all variables are declared globally in expl3, but they are then used as either always-local or always-global storage as appropriate. (Mixing local and global assignment may lead to save stack issues.) By convention, variable names at the code level start with either l or g to indicate which usage they are intended for. Similarly, most functions for setting variables have versions where the 'action' part has a g prepended: these are the global versions. For example, we have set and gset, put and gput, remove and gremove. Where a variable is being used but not set, there is no split: for example, \tl_use:N can be applied to both local and global token lists.

The net result is that one should declare a variable once, noting it's intended scope in the name, then use the matching 'setting' functions.

\tl_new:N \l_my_tl
\tl_new:N \g_my_tl
\group_begin:
  \tl_set:Nn \l_my_tl { a }
  \tl_gset::Nn \g_my_tl { b }
  % ...
\group_end:
% \l_my_tl back to empty, \g_my_tl still 'b'