add tag
निरंजन
I am trying to play with [this](https://tex.stackexchange.com/a/725771) answer of Skillmon and most of the desired results are obtained, but some things are very unexpected. I am playing with expl3 for the first time, so a little puzzled with the results. Have a look at the following commented MWE. There are some in situ questions, remarks etc.:

```#
\begin{filecontents*}[overwrite]{\jobname.sty}
\ExplSyntaxOn
% Copied from Skillmon's answer. I don't yet understand why
% to make a copy and why not to use \tl_put_right:Ne
% directly?
\cs_new_eq:NN \my@tmp@add \tl_put_right:Ne
\ExplSyntaxOff
\newwrite\tmp@write
\immediate\openout\tmp@write=tmpfile.tex\relax
% Have added two \expandafter-s in the hope that \write and
% \tmp@write are put on hold before the expansion of the
% next thing is completed. Seems to be working.
\expandafter\expandafter
\write\tmp@write
% After skipping two commands, I expect \my@tmp@add to be
% expanded. Not very confident with expl3, but I was
% expecting \babelprovide to stay un-manipulated and behave
% normally, whereas I expect \mylang to be expanded.
\my@tmp@add\babelprovide\mylang
\end{filecontents*}
\documentclass{article}
\def\mylang{marathi}
\usepackage{babel}
\usepackage{\jobname}
\babelfont[marathi]{rm}[%
  Script   = {Devanagari},%
  Renderer = {HarfBuzz}%
]{Mukta-Regular.ttf}

\begin{document}
hello world
\foreignlanguage{marathi}{नमस्कार}
\end{document}
```

The PDF output is correct!! But this is the resultant (and subsequently commented) `.sty`:

```
\__kernel_tl_set:Nx%! Who is this guy?
\babelprovide{% Seems to be pretty un-manipulated.
  \@protected@testopt%! Who is this guy?
  \babelprovide%! Why again?
  \\babelprovide%! What?!? WHY again, that too in text mode?
  {}marathi% \mylang expanded well, but how to put it in {}?
}
% I expect a `clean' file having JUST the following line:
% \babelprovide{marathi}
```
Top Answer
Skillmon
There are multiple (in part really short) questions involved. I'll try to tackle them all, but it might be I miss some, just leave a comment.

# Why copy `\tl_put_right:Ne` instead of using it directly?

The answer to this is rather simple: because the rest of the code you provided was using 2e-syntax so I stuck with it. I could as well have defined it directly (`\protected\long\def\my@tmp@add#1#2{\edef#1{\unexpanded\expandafter{#1}\unexpanded\expanded{{#2}}}}` isn't that complicated to do -- but maybe to understand, see somewhere down below). I just don't like mixing the two syntaxes.

So the alternative (in my personal book) would've been translating all code to `expl3` where possible.

# Your `\write` usage

Note that your syntax is incorrect. `\write` expects the file index (your `\tmp@write`) and after that balanced text (so stuff in braces). If you compile your example file without ignoring errors you'll see the following output to the terminal (and log):

```none
./jobname.sty:18: Missing { inserted.
<to be read again> 
                   \__kernel_tl_set:Nx 
l.18 \my@tmp@add\babelprovide\mylang
                                    
? 
```

Whenever TeX scans for balanced text it'll expand tokens until it finds the opening brace, which is why you see `\__kernel_tl_set:Nx` which leads us to the next subquestion down the list. For now we'll tackle the `\write` (though Ulrike already explained the gist of it in the comments):

`\write` is one of the cases in which TeX expands the contents inside what is called `e`-expansion in `expl3`. Basically this means full expansion in which you don't have to double `#`.


## Interlude: What the kern is `\__kernel_tl_set:Nx`?

In `expl3` *internal* commands start with `__` followed by the module name, in this case `kernel`. So we know this is an internal command defined and used by the LaTeX kernel. It'll do `tl_set` and accept two arguments, a single token (`N`) and a balanced argument that is fully expanded (`x`). In this instance one could look up its definition (`latexdef __kernel_tl_set:Nx` on the command line, or by taking a look at `texdoc source3`) and find that it's actually another name for the TeX primitive `\edef` (which TeX doesn't expand further inside of `\write` and that is no opening brace, which yields to the error above).

## Interlude: Understanding the jumpy nature of `\expandafter`

The `\expandafter` primitive will do the following: It defers the next token (whatever it is) and keeps it for later. It then does a *single* step of expansion of the token following that, whatever it is. If it's an unexpandable token (a character token or an unexpandable primitive -- note that even `\protected` macros get expanded here) it is left unchanged. In every other case the result of that expansion step is left in the input stream and the deferred token from the start is reinserted. Afterwards `\expandafter` is done and disappears. All these steps are a *single expansion step*.

So what happens in your code? Back to "Your `\write` usage":

0. step
    ```
    input list:     \expandafter\expandafter\write\tmp@write\my@tmp@add\babelprovide\mylang<EOF>
    ```
    `\expandafter` will trigger and defer the second `\expandafter`

1. step (in internal steps, not expansion steps)
    ```
    executed:       \expandafter
    deferred:       \expandafter
    to be expanded: \write
    input list:     \tmp@write\my@tmp@add\babelprovide\mylang<EOF>
    ```
    `\write` isn't an expandable token so nothing happens to it

2. step (first expansion step is over)
    ```
    input list:     \expandafter\write\tmp@write\my@tmp@add\babelprovide\mylang<EOF>
    ```
    `\expandafter` will trigger and defer `\write`

3. step
    ```
    executed:       \expandafter
    deferred:       \write
    to be expanded: \tmp@write
    input list:     \my@tmp@add\babelprovide\mylang<EOF>
    ```
    `\tmp@write` is a `\chardef`ed token and as such not expandable, so nothing happens to it.

4. step (second expansion step is over)
    ```
    input list:     \write\tmp@write\my@tmp@add\babelprovide\mylang<EOF>
    ```
    TeX starts handling the `\write` primitive.

5. step (we're leaving the fully expandable area of TeX's mouth and are on our way to the stomach)
    ```
    executed:       \write
    TeX mode:       Scanning for number
    input list:     \tmp@write\my@tmp@add\babelprovide\mylang<EOF>
    ```
    TeX will now search for the number specifying the writing stream. It'll find the `\chardef`ed `\tmp@write` which it'll interpret as its numeric value, scanning for number will stop afterwards.

5. step
    ```
    executed:       \write
    absorbed:       \tmp@write
    TeX mode:       Read balanced text
    input list:     \my@tmp@add\babelprovide\mylang<EOF>
    ```
    Now TeX will try to read what it calls a balanced text. That is anything in `{}` with a balanced number of braces. While it searches for the opening brace it'll expand everything in its wake until it finds something unexpandable (`\relax` would be ignored by this process).

6. step
    ```
    executed:       \write
    absorbed:       \tmp@write
    TeX mode:       Read balanced text
    to be expanded: \my@tmp@add
    input list:     \babelprovide\mylang<EOF>
    ```
    Since `\my@tmp@add` is a macro it's expandable as far as TeX's current mode is considered (even though it's defined `\protected`), so the thing will expand to its definition (grabbing its arguments along the way)
    ```
    \protected\long macro:#1#2->\__kernel_tl_set:Nx #1{\__kernel_exp_not:w \exp_after:wN {#1}\__kernel_exp_not:w \tex_expanded:D {{#2}}}
    ```

7. step
    ```
    executed:       \write
    absorbed:       \tmp@write
    TeX mode:       Read balanced text
    input list:     \__kernel_tl_set:Nx\babelprovide{\__kernel_exp_not:w\exp_after:wN{\babelprovide}\__kernel_exp_not:w\tex_expanded:D{{\mylang}}}<EOF>
    ```
    Now TeX will face a low level error: `\__kernel_tl_set:Nx` is a non-expandable non-`\relax` primitive, but it searches for an opening brace... We're met with an error message that TeX didn't find a brace and inserted it for us. That leads to the following peculiar situation:

8. step
    ```
    executed:       \write
    absorbed:       \tmp@write{
    TeX mode:       Read balanced text
    input list:     \__kernel_tl_set:Nx\babelprovide{\__kernel_exp_not:w\exp_after:wN{\babelprovide}\__kernel_exp_not:w\tex_expanded:D{{\mylang}}}<EOF>
    ```
    Now TeX reads the input until the matching right brace. But there is none. We're greeted with the next low level error:
    ```none
    Runaway text?
    \__kernel_tl_set:Nx \babelprovide {\__kernel_exp_not:w \exp_after:wN \ETC.
    ! File ended while scanning text of \write.
    <inserted text> 
    }
    l.25 \babelfont
                 [marathi]{rm}[%
    ? 
    ```
    and it inserted our closing brace for us. Now `\write` read its balanced text and will execute the writing process, during which it'll fully expand its input.

9. step (I'll drop the `\write` and focus on the stuff being written here)
    ```
    input list:     \__kernel_tl_set:Nx\babelprovide{\__kernel_exp_not:w\exp_after:wN{\babelprovide}\__kernel_exp_not:w\tex_expanded:D{{\mylang}}}
    ```
    `\__kernel_tl_set:Nx` isn't expandable so passes through unchanged.

10. step
    ```
    written:        \__kernel_tl_set:Nx
    input list:     \babelprovide{\__kernel_exp_not:w\exp_after:wN{\babelprovide}\__kernel_exp_not:w\tex_expanded:D{{\mylang}}}
    ```
    `\babelprovide` is an expandable macro so will expand to its meaning
    ```
    macro:->\@protected@testopt \babelprovide \\babelprovide {}
    ```
    and the result is further expanded.

11. step (I'll drop whatever was written in the last step from now on)
    ```
    input list:     \@protected@testopt\babelprovide\\babelprovide{}{\__kernel_exp_not:w\exp_after:wN{\babelprovide}\__kernel_exp_not:w\tex_expanded:D{{\mylang}}}
    ```
    I'll step over a few steps, it suffices to say that in the current context `\@protected@testopt` will indeed protect the macro and its result will be to leave `\protect\babelprovide` while gobbling `\\babelprovide{}`.

12. step
    ```
    input list:     \protect\babelprovide{\__kernel_exp_not:w\exp_after:wN{\babelprovide}\__kernel_exp_not:w\tex_expanded:D{{\mylang}}}
    ```
    `\protect` will do exactly that and make sure `\babelprovide` doesn't expand using `\noexpand` in the current context.

13. step
    ```
    written:        \babelprovide
    input list:     {\__kernel_exp_not:w\exp_after:wN{\babelprovide}\__kernel_exp_not:w\tex_expanded:D{{\mylang}}}
    ```
    Now we'll just skim through the rest, `\__kernel_exp_not:w` is `\unexpanded`, so that is `\unexpanded\expandafter{\babelprovide}`, we already saw to what that expands, but `\@protected@testopt` can't do its thing because it's `\unexpanded`, hence written. Then we still got `\unexpanded\expanded{{\mylang}}`, that'll just do that, it'll first fully expand `\mylang` then protect it from further expanding. And `marathi` ends up being written.

## Interlude: What's up with that `\unexpanded\expanded`? That doesn't make sense!

Well yes, it does make sense. This is a low level trick to fully expand something inside an `\edef` context while also allowing `#` to be stored without needing to double it (since `#` doesn't need to be doubled in `\expanded`, and `\edef\foo{\unexpanded{#}}` works fine). Since `\unexpanded` searches for balanced text, it'll fully expand `\expanded` before doing its thing.

# What to do instead?

Well, we need to use balanced text after `\write` and just put what we want to be written there, protecting it from the expanding nature of `\write`. Since `\mylang` is user provided we play safe and expand it only once using `\unexpanded\expandafter` idiomaticly:

```
\begin{filecontents*}[overwrite]{\jobname.sty}
\ExplSyntaxOn
\cs_new_eq:NN \my@tmp@add \tl_put_right:Ne
\ExplSyntaxOff
\newwrite\tmp@write
\immediate\openout\tmp@write=\jobname_tmpfile.tex\relax
\write\tmp@write{\noexpand\babelprovide{\unexpanded\expandafter{\mylang}}}
\end{filecontents*}
\documentclass{article}
\def\mylang{marathi}
\usepackage{babel}
\usepackage{\jobname}
\babelfont[marathi]{rm}[%
  Script   = {Devanagari},%
  Renderer = {HarfBuzz}%
]{Mukta-Regular.ttf}

\begin{document}
hello world
\foreignlanguage{marathi}{नमस्कार}
\end{document}
```

Enter question or answer id or url (and optionally further answer ids/urls from the same question) from

Separate each id/url with a space. No need to list your own answers; they will be imported automatically.