Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Declaring local variables #410

Closed
EvanAad opened this issue Oct 18, 2017 · 49 comments
Closed

Declaring local variables #410

EvanAad opened this issue Oct 18, 2017 · 49 comments
Labels
enhancement New feature or request

Comments

@EvanAad
Copy link
Contributor

EvanAad commented Oct 18, 2017

The document The expl3 package and LaTeX3 programming (v. 2017/09/18) states on p. 7:

The LaTeX3 coding convention is that all variables must be declared before use.

Similarly, the document The LaTeX3 Interfaces (v. 2017/09/18) states on p. 10:

... in contrast to variables, which must always be declared

expl3 provides no facilities to declare a variable locally; only globally. However, it is still possible to create a local variable implicitly using the various \..._set:N... functions.

It is a principle of structured programming that dumping variables into the global scope unnecessarily should be avoided. The TeX language, like all high-level programming languages that I know (including C, C#, Fortran, Java, Lisp, Pascal, and Python), provides a scoping construct for creating local variables, namely groups. Moreover, the LaTeX3 programming language itself, thankfully, supports the creation of local variables, despite what the manuals suggest.

In my opinion, the admonitions quoted above should be deleted from the manuals, and it should be explained that variables can, and should, be created locally whenever possible.

Moreover, if it is indeed desired to encourage a programming style in which all variables are declared before they are used (I personally think this is a matter of programming style that should be left to the individual taste of the programmer), functions need to be provided for declaring local variables just as there are functions for declaring global variables.

@josephwright
Copy link
Member

josephwright commented Oct 18, 2017

The current position follows a number of experiments by the team to establish a pattern which works with the TeX fundamentals supporting expl3. In particular, it's important to note that variables may be implemented using macros or using registers, and to bear in mind how TeX grouping works.

When using registers (e.g. for the int type) we need an allocator to link the register number and cs we are using for access. In contrast, for macro-based storage that is not needed. Thus whilst \cs_set_eq:NN can be used to generate new tl names (for example), it will fail with anything using registers:

\int_set_eq:NN \l_undeclared_int \l_tmpa_in

will raise an error.

It is possible to create a local register allocator, and we have tried that out in the past for e.g. \int_local_new:N. (See the etex package for one such implementation, or look back through the expl3 history.) The team eventually decided against this as it did not seem to 'fit' well with TeX scoping. Key here is that TeX grouping is not based on declarations but rather on explicit groups, and that a local variable exists within any nested groups:

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

When we tried local creation of variables, it seemed that there was a danger of obscuring how TeX grouping worked, and that programmers could be misled.

We therefore decided to go with 'all global' declaration of variables, but with both local and global setting of such variables (the \l_... _versus \g_... convention). This localises the important thing: the value. We therefore recommend that all declarations are at the top level

\tl_new:N \l_my_tl
\int_new:N \l_my_int

...
\cs_new_protected:Npn \my_func:nn #1#2
  {
    \group_begin:
      \tl_set:Nx \l_my_tl { \tl_lower_case:n {#1} }

To-date, this pattern appears to work well for the tasks expl3 has been used for.

@u-fischer
Copy link
Member

In tex even locally defined variables are not completly destroyed after the end of the group. They still use some string space: https://tex.stackexchange.com/questions/316999/release-space-in-the-string-pool. So imho it is better to view variables as global objects.

@josephwright
Copy link
Member

@u-fischer Good point: not one we actually considered at the time but worth bearing in mind.

@EvanAad
Copy link
Contributor Author

EvanAad commented Oct 18, 2017

In my opinion this approach is mistaken. It goes against the principles of structured programming, and it goes against TeX, which enables the creation of local variables. It also makes LaTeX3 inconsistent, since some data types accommodate local variables and some don't.

I would like to request that you re-institute local creation of variables of all types. This gives the programmers a choice: those who prefer to define all variables globally can do so, and those who wish to define some globally and some locally can do so too.

If there are caveats, memory-wise or otherwise, they can be mentioned in the documentation.

@davidcarlisle
Copy link
Member

davidcarlisle commented Oct 18, 2017 via email

@EvanAad
Copy link
Contributor Author

EvanAad commented Oct 18, 2017

The fact that Fortran requires variables to be declared at the start of a function/subroutine does not mean that those variables are global. If you insist on encouraging a programming style where all variables must be declared, so be it, but then provide functions for declaring local variables corresponding to the ones for declaring global ones.

The rationale for scoping and local variables is fore and foremost conceptual. The fact that the underlying TeX engine does not release some of the resources associated with local variables is not to be dismissed, but, in my opinion, this cannot be the reason for obliterating the notion of scoping and local variables. Except for power programmers, the underlying implementation is of no concern; and for power programmers, tips and caveats can be offered in the documentation.

appealing to general purpose languages isn't a good use case.

I would say that diverging from the principles of structured programming which have been implemented in virtually every programming language from the 1960s till today, including TeX, should be something that the LaTeX3 team should have very compelling reasons for doing. The burden of proof should be on those who wish to abolish what is ubiquitously considered good programming practice, and what already exists in TeX; not on those who wish to preserve it.

@eg9
Copy link
Contributor

eg9 commented Oct 18, 2017

@EvanAad Token list, comma list sequence and property list variables are actually TeX macros, but variables of different kind aren't. Let's assume declaring an integer variable locally is allowed. This consumes a register and it's necessary to globally take care of this so as the allocation doesn't touch an already assigned register.

Say your local integer variable \x is assigned register 100 which is already taken by variable \y at an upper level; using \y at the same level where \x is locally defined would be simply disastrous. Thus global bookkeeping of assigned registers is needed, making it very hard to release locally assigned registers. Reserving a block of registers for local assignments is not a solution.

I'm not saying it can't be done, but I don't think it's worth the pain.

@davidcarlisle
Copy link
Member

davidcarlisle commented Oct 18, 2017 via email

@EvanAad
Copy link
Contributor Author

EvanAad commented Oct 18, 2017

The crucial point is that, as Joseph and David mentioned, allowing ints to be defined locally can be (and has been) done. I think it is worth the pain in order to create a language that presents a consistent abstraction layer that is in agreement with the principles of structured programming.

However, saying it is worth the pain is easy for me to say, because I am not the one subjected to the pain of implementing the LaTeX3 language. So let's take a merciful approach, and say that it is in fact not worth the pain. Fine. In this case divide the data types into two categories: those that allow for the creation of local variables, and those that don't. And describe these categories in the manuals. Don't say: all variables must be declared before use. Say: "There are two categories of data types. The first, consisting of cs, tl, clist, ... accommodates local variables, and the second, consisting of int, ... does not." And explain why the second category exists. This way the documentation is true to the facts, and the programmers are well informed and have a choice. Some programs do not need to use data types of the second category at all, and in these cases why should the programmer have to declare global variables?

@josephwright
Copy link
Member

@EvanAad I suspect this is because I'm used to TeX programming (including the 'traditional' restrictions of TeX90), but I'm not sure at present what the issue is with the current approach. We do support and indeed encourage locally-assigned variables: they are very common and indeed part of the syntax we have for variable naming (\l_.../\g_...). The fact they are allocated/'reserved' globally doesn't interfere with that.

BTW, we don't want to tie interfaces to implementation: for example, the prop data type has had at least a couple different implementations I know of, one of which used registers and the current one using macros.

@wspr
Copy link
Contributor

wspr commented Oct 18, 2017

To add one more note to this, the straw that broke the camel's back (in my memory) that the local register/variable allocation system fell down was because of inconsistency. Because macros and registers behaved differently, you ended up with different behaviour when writing

\group_begin:
  \int_new_local:N \l_tmpa_int
  \int_gset:Nn \l_tmpa_int {7}
\group_end:
% `\l_tmpa_int` undefined

vs

\group_begin:
  \tl_new_local:N \l_tmpa_tl
  \tl_gset:Nn \l_tmpa_tl {7}
\group_end:
% `\l_tmpa_tl` defined as `7`

And the only way to resolve this problem would be to write an allocation system for macros, which was never seriously considered due to overhead. (TeX slows down the more variables are defined, and so doubling the number of token lists could have affected performance noticeably.)

I still think that expl3 did the right thing by abstracting macros and registers into functions that look and feel the same — and if the downside is that the syntax doesn't naturally support truly local variables and registers, which have seen extremely limited use in TeX due to the reasons outlined above, then from my perspective that's an acceptable trade-off.

@wspr wspr closed this as completed Oct 18, 2017
@EvanAad
Copy link
Contributor Author

EvanAad commented Oct 18, 2017

@wspr But the examples you cited as the ones the broke the camel's back are examples where the programmer abused the language by assigning globally to a local variable. This is on the programmer. The current system doesn't solve the problem of abuse of language, as a programmer can still use an \l_... variable as though it was global.

@FrankMittelbach
Copy link
Member

FrankMittelbach commented Oct 18, 2017 via email

@EvanAad
Copy link
Contributor Author

EvanAad commented Oct 18, 2017

@wspr : Why did you close this issue like a bully? It is anything but settled.

@wspr
Copy link
Contributor

wspr commented Oct 18, 2017

@EvanAad — just trying to keep things tidy. Discussion can certainly continue and if warranted we will re-open.

@wspr wspr reopened this Oct 18, 2017
@latex3 latex3 locked and limited conversation to collaborators Oct 18, 2017
@wspr
Copy link
Contributor

wspr commented Oct 18, 2017

I'm locking these issues temporarily due to some heated comments.

@blefloch
Copy link
Member

I think this is a relevant question. One outcome of this discussion should at least be that we describe more carefully in the doc why we chose global declarations only. @EvanAad I don't think you quite appreciate the limitations of the TeX system, but let us step back and ignore that issue for a moment. Could you give an example (of 10-20 lines, say) using a hypothetical \int_local_new:N or a different system of your choice with the semantics of your choice? It would help frame the discussion in a more concrete setting, and we can hash out benefits/drawbacks.

Presumably we have to wait for the lock to lift, I don't know how to do that. In any case most people in this conversation are going to be sleeping for a few hours. (By the way, I don't think copying #410 into #411 as a jumbled discussion was very reasonable.)

@latex3 latex3 unlocked this conversation Oct 19, 2017
@josephwright
Copy link
Member

josephwright commented Oct 19, 2017

Looking over the fact this is quite a detailed discussion, I think it's worth summarising the technical and social history that got us to where we are.

At a technical level, TeX provides registers and macros for storage. Macros can just be 'created' using \def, but to use registers by name we need some allocator to link the global register number, e.g. \count40, to some name, e.g. \mycount. That has been provided from 'day one', with the plain TeX \newcount, etc. providing the model. In plain, \newcount allocates globally and, despite the name, doesn't do any checking. Other formats, most notably LaTeX2e and ConTeXt, have both adopted this approach in general terms. They have also generally adopted the idea that individual registers are then assigned either locally or globally, as this prevents save-stack buildup.

TeX90 provided only 256 registers of the common types, so register re-use in local contexts was vital. One sees that in e.g. graphics where to keep the code 'sane' a large number of registers are given new names inside a group, and used purely locally. With e-TeX we have a lot more registers, so this approach is less necessary: we can freely allocate more registers (and macros) for dedicated purposes. In particular, it means that we are nothing like as pressurised to 'recycle' registers under multiple names.

The code base of expl3 has been in development for a long time, and originally did not require e-TeX. Development of expl3 is also done primarily 'on top' of LaTeX2e, with a general principal that expl3 doesn't pollute the document name space nor change core LaTeX2e behaviours.

In particular, we have to bear in mind that LaTeX2e's \newcount is similar to plain's: it allocates globally and does not checks. Thus in particular

\def\foo{%
  \begingroup
    \newcount\localfoocnt

is 'bad style' as if \foo is called multiple times then we will use up registers which are never released.

For expl3, the team have tried to avoid tying the documented behaviour of variables to implementation of macros or registers. (I do note that we allow use of tl without an accessor, which ultimately does rely on them being macros.) We also still make use of registers for integers, dimens, etc. as they are available and offer better performance and 'self termination' than doing everything in macros. (That would be doable using e-TeX and the various \<thing>expr primitives.) As such, we need to have an allocator system, and to be consistent we allocate all variable types, not just those which are based on registers. As @EvanAad has observed, when checking is not active one can do e.g. \tl_set_eq:NN \l_new_tl \l_existing_tl with no error, but this is not the supported behaviour (checking will flag it up, for example).

As part of the expl3 allocator, the decision was made that new would check for existence, in contrast to \newcount, so

\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_new:N \l_my_tl

will raise an error if \my_foo: is used repeatedly. This pushes one in the direction which has been standard TeX practice since the first days of plain: allocations go outside of macro. (Note that in plain, \newcount is \outer so is 'forbidden' inside macros.)

\tl_new:N \l_my_tl
\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_clear:N \l_my_tl

The etex package introduced many years ago a register allocator which can do local as well as global allocation of register, \loccount versus \globcount, _etc. For quite some time, expl3 loaded etex and it was used to provide functionality for \int_local_new:N and similar. Given the fact that most TeX programmers are used to doing global allocation, it is perhaps unsurprising that this was not taken up so widely. However, the team were also concerned about possible misunderstandings. With something like

\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_local_new:N \l_my_tl

we have the question of what happens on nested calls to \my_foo:, or more likely places where some 'simple' name such as \l_my_tmp_tl is to be used. Based on the general idea that we want new to do checks, that should be an error. Programmers coming from a lot of other languages might find that somewhat odd. Of course, it does fit with TeX's scoping rules.

Notably, changes to the LaTe2e kernel in recent years mean we'd no longer want to load etex (indeed, we've worked hard to make expl3 'self-contained'). So any new local allocator would have to be written to work with LaTeX2e without 'disturbing' behaviours for those not using expl3: doable but not entirely trivial.

For something like \l_my_tmp_tl one might of course still do global allocation, but then one has to know which \l_... variables are locally allocated in the current group and which are globally allocated but locally assigned.

I've not got test data to hand, but it is probably worth noting that allocating a variable is more work than simply setting it (certainly when checking is not active), so using a local allocator would be slightly slower than a global allocator plus local assignments.

Thus for a mix of technical reasons and 'fitting with history' we decided to stick to strictly global allocation. This doesn't in any way prevent local _assignment of variables, which are encouraged and very widely used.

That takes us to the 'social' element. Take up of expl3 in recent years has been strongly aided by making parts 'broadly' stable. At some stage the team have to make a decision on stuff, partly so people can use it and partly so we move on to other tasks. That doesn't prevent us revisiting things, but more established conventions need a good reason to be altered.

Here, at present I guess I'm not seeing what is wrong with the 'usual' approach of

\tl_new:N \l_my_tl
\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_clear:N \l_my_tl

at least to the point where it's required that we go back and change the current set up.

@EvanAad
Copy link
Contributor Author

EvanAad commented Oct 19, 2017

@blefloch Yes, I agree that my reaction to @wspr 's closing the thread was unreasonable and I apologize to him and to the rest of you. I felt I was being dismissed and silenced, and I reacted with a tantrum. This is unacceptable. Sorry about that.

@josephwright thank you for the detailed reply. After giving the matter some thought, I've realized that if I only use \<module>_clear_new:N, then in effect all my variables will behave as though they were local to the surrounding group, as long as I assign using \<module>_set... rather than \<module>_gset.... Moreover, I think this practice agrees with the LaTeX3 convention of declaring a variable before use, so I won't be flagged if the checks are on.

Following this practice, I would rewrite @josephwright last example thus:

\cs_new_protected:Npn \my_foo:
{
    \group_begin:
        \tl_clear_new:N \l_my_tl

The only data type that doesn't fit this schema is cs, but as far as I know it is legal to create variables of this type with \cs_set:Npn et al. without first declaring them, because LaTeX3 doesn't really treat cs as a normal data type on par with the others (which is a separate issue I'd like to take up with you guys, but I'll leave it to another thread). Using this approach, cs variables too can be created locally. If I want to keep syntax consistent, I can always write my own \cs_clear_new:N wrapper.

So, as far as I'm concerned, now you can close this issue if you want to, @wspr .

@josephwright
Copy link
Member

josephwright commented Oct 19, 2017

@EvanAad On \<thing>_clear_new:N, notice that the declaration is global where the variable doesn't already exist. So

\cs_new_protected:Npn \my_foo:
{
    \group_begin:
        \tl_clear_new:N \l_my_tl
    \group_end:
}
\my_foo:
\tl_show:N \l_my_tl

will show that \l_my_tl is defined and empty, assuming you had not previously set it to something else.

@EvanAad
Copy link
Contributor Author

EvanAad commented Oct 19, 2017

I'd like to join @blefloch in hoping this discussion will lead to better documentation. In particular, I think that when you write that the programmer "must" follow a certain pattern of coding, as in the sentences I quoted in my original post, the documentation should elucidate what this "must" mean. What will follow if the pattern is not adhered to?

  1. Will the language's behavior be undefined?
  2. Is non-adherence currently supported, but may not be so in future versions?
  3. Will the engine report an error?
  4. Will the engine report an error, but only when the checks are on?
  5. Will some minor inconveniences result?
  6. Will the LaTeX3 team grumble in discontent, but no error or loss of functionality result?

As an example for 5, take the convention of appending argument specifications to the end of a function's name. As far as I can tell, the only function that won't work properly if this convention is not adhered to is \cs_new:Nn, but the rest, including `\cs_new:Npn' will work perfectly fine.

As an example for 6, take the convention of marking local and global variables with \g_... and \l_.... As far as I know there will be absolutely no repercussions to not following this convention. Everything will work properly.

@wspr
Copy link
Contributor

wspr commented Oct 19, 2017

@EvanAad — apology accepted, and I'm sorry in my part for closing the issue before the discussion was complete.

@josephwright — as one teeny tiny advantage of benefits to local allocation, if I write

\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_new_local:N \l_my_tl
      ...

then I know that \l_my_tl is not only free from interference from the outside, but unlike using a _clear function I know that all trace of the variable is gone outside of the use of the function. In other words, just by looking at the code I know that it cannot be used as a semi-global variable down the line. (And I can check that nothing odd is going on by using \tl_if_exist_p:N.)

(Must run but can continue later if there's any point discussing further.)

@josephwright
Copy link
Member

@wspr Yes but that's back with the fact it is based on TeX group scope. So it can be used 'semi-globally' in a construct of the form

\cs_new_protected:Npn \my_foo:
  {
    \group_begin:
      \tl_new_local:N \l_my_tl
      \tl_set:Nn \l_my_tl { foo }
      \__my_foo:
     ..
  }
\cs_new_protected:Npn \__my_foo:
  {
    \group_begin:
        \tl_use:N \l_my_foo % Definition from \my_foo: => "foo"
...
  }

which was at least part of our thinking.

@josephwright
Copy link
Member

I think before we leave this I would like to emphasise that at a technical level there are a number of ways to set up a local register allocator.

@wspr
Copy link
Contributor

wspr commented Oct 19, 2017

@josephwright — I’ve never seen that as a confusing example though; in Matlab for example they distinguish between sub functions which don’t share scope, and nested sub functions which do. So to my thinking that’s always been, well, why wouldn’t \__my_foo: inherit the scope of the “outer” function? It’s perfectly consistent with TeX’s grouping behaviour.

@wspr
Copy link
Contributor

wspr commented Oct 19, 2017

(Sorry to keep the discussion going. It's been a long day. Is anyone interested in actually continuing?)

@FrankMittelbach
Copy link
Member

FrankMittelbach commented Oct 19, 2017 via email

@wspr
Copy link
Contributor

wspr commented Oct 19, 2017

@FrankMittelbach — now you have me curious; is the etex.sty approach for registers really that inefficient? Or maybe you mean for tl variables, in which case I agree!

@EvanAad
Copy link
Contributor Author

EvanAad commented Oct 19, 2017

@FrankMittelbach In my opinion it is important to distinguish, and make it clear in the documentation, what is a best practice according to the LaTeX3 team vs. what is a formal requirement, either grammatical or semantic, of the rules of the LaTeX3 language.

The two rules that you cited, namely:

  • declare the name of a variable once (globally)
  • use the name convention \l_ \g_ to denote local and global variables

fall under this rubric of coding conventions that the LaTeX3 team deems advisable, but neither of them is mandated by the grammatical rules of the language, and not adhering to these conventions causes no error or loss of functionality.

In other words, following these rules is a matter of personal taste and coding style, and this should be made clear in the documentation, in my opinion.

@FrankMittelbach
Copy link
Member

FrankMittelbach commented Oct 19, 2017 via email

@EvanAad
Copy link
Contributor Author

EvanAad commented Oct 19, 2017

@FrankMittelbach Sure, if you define this to be part of the rules of the language than it is so by definition, but my point is that this should not be defined as part of the rules of the language, because it is never checked, and because not adhering to this convention does not in itself cause an error or loss of functionality.

It's the difference between saying "When you write English it is best to hold the pen in your right hand because this avoids smearing the ink." and passing a law that says that English must be written with the pen held in the right hand. Sure, you can pass such a law, and there is sound rationale to justify it, but ultimately the choice in which hand to hold the pen should be left to the individual writer. And there will be people who will choose not adhere to this rule, and still end up writing just as tidily as those who adhere to it.

@blefloch
Copy link
Member

I think @FrankMittelbach overstates the overhead of local declarations (\loccount manages counts locally, nothing it does is global, so that keeps the overhead fine).

I see no major issue with providing an \int_local:N and \tl_local:N (=\tl_set_eq:NN #1 \c_empty_tl) etc that would be very analogous to \int_zero_new:N and \tl_clear_new:N but would only do "new" locally. This requires a bit of work for registers but not excessively so.

@EvanAad you should know that not declaring a variable may bite you at some point (warning: this code produces infinitely many pages).

\documentclass{article}
\usepackage{expl3}
\begin{document}
\ExplSyntaxOn
\tl_put_left:cn { l_my_tl } { foobar \par }
\l_my_tl
\end{document}

@EvanAad
Copy link
Contributor Author

EvanAad commented Oct 19, 2017

@blefloch What I'm saying is, it's perfectly fine to declare a variable inside of a macro (with \<module>_clear_new), and you don't have to name it \l_amount_paid_int, you can simply call it \amount_paid or \amountPaid, like you would in any other programming language. The \l_..._int is a nice mnemonic to remind you that it's integer and that it's supposed to be used locally, but you should not be forced to use this, or any other mnemonic.

@FrankMittelbach
Copy link
Member

FrankMittelbach commented Oct 19, 2017 via email

@blefloch
Copy link
Member

(What my previous code example was pointing out is that even for tl it is essential to declare them before use. There is a checking option that tests declarations but in normal use we don't want such overheads.)

I agree with @EvanAad that names are just a convention. We can check that local and global assignments are not mixed even without the l_/g_ name convention: since the checking code is allowed to be somewhat slow it is quite reasonable to store the information about whether a given variable was used in a local/global assignment. For checking types the situation is similar, except for two pairs of types that cannot be distinguished (I'm not going to say which to avoid derailing the conversation).

@josephwright
Copy link
Member

On 'conventions', \amountPaid is a document command whilst \l_amount_paid_int isn't, and the latter is part of the amount namespace (by convention but a pretty important one in TeX). That doesn't apply to \l_/\g_, though I think many languages have a strong 'steer' on naming without it being enforced at a technical level.

@eg9
Copy link
Contributor

eg9 commented Oct 19, 2017 via email

@u-fischer
Copy link
Member

and you don't have to name it \l_amount_paid_int, you can simply call it \amount_paid like you would do in any other programming language. The \l_... is a nice mnemonic to remind you that it's supposed to be used locally, but don't have to use this, or any other mnemonic.

Sure. You could also use \amount:paid or \amount&paid (that's not a joke, I know a package which use &) or whatever your prefer. But even if the names are just a convention: it makes communication easier if people stick to such conventions. You have been asking quite a lot questions on tex.sx. What will you do if you have a problem with your code written in "personal style"? Translate it to standard style, ask a question and translate it back? Or expect everyone to be able to handle your personal style?

@EvanAad
Copy link
Contributor Author

EvanAad commented Oct 19, 2017

@eg9

For personal code one is entitled to do whatever they want

You wouldn't know this by reading the documentation, is all I'm saying.

If you want to locally define the variable \f of whatever type, you should be worried about the command being defined by some package

I disagree. If your code is inside a \group_begin: ... \group_end:, and if you define all local variables with \<module>_clear_new:N, and if you only assign to local variables with \<module>_set:N..., you needn't worry about your local variables name-clashing with other packages, unless your code uses another package.

@u-fischer

But even if the names are just a convention: it makes communication easier if people stick to such conventions.

I'm not saying there are no good reasons to stick to the conventions. All I'm saying is, these conventions should not be made language rules, and the documentation should clearly differentiate between conventions, for which the rationale should be stated, and rules. And the choice whether to follow the conventions should rest ultimately with the programmer.

@blefloch
Copy link
Member

@EvanAad You do need to worry about name-clashes even in the case you describe. Say you write

\cs_new_protected:Npn \evanaad_halve:n #1
  {
    \group_begin:
      \int_zero_new:N \f
      \int_set:Nn \f { (#1) / 2 }
      \iow_term:x { \int_use:N \f }
    \group_end:
  }

then a user of your package does

 \int_const:Nn \f {123}
 \evenaad_halve:n { \f }

They will be surprised to see 0 and not 62.

@blefloch
Copy link
Member

On the other hand if you stick with names such as \evanaad_f etc (or shorter \@@_f etc using l3docstrip magic) you should be safe.

@u-fischer
Copy link
Member

You wouldn't know this by reading the documentation, is all I'm saying.

Sorry but expl3.pdf uses the word "convention" around 20 times. In the case of public versus private command there is even the sentence "There is (nearly) no way to enforce this without severe computing overhead, so we implement it only through a naming convention,".

@EvanAad
Copy link
Contributor Author

EvanAad commented Oct 19, 2017

@blefloch Good point.

@u-fischer OK, fair enough. What about the convention that a function's name should end with an argument specifier? This is not entirely a convention, because the functions of section 3.3 inspect the argument specifier, but these functions are merely "syntactic sugar", and if you don't use them, there's no hindrance using function names like \mymodule_myfunc, but you wouldn't know it from the manual.

@eg9
Copy link
Contributor

eg9 commented Oct 19, 2017 via email

@blefloch
Copy link
Member

blefloch commented Oct 19, 2017 via email

@EvanAad
Copy link
Contributor Author

EvanAad commented Oct 19, 2017

@blefloch I see your point, and it is a good one, but still, in my opinion if all you want is to write a document command, you should know that you can do so by defining a function like

\ExplSyntaxOn
\cs_new:Npn \MyDocumentCommand {Hello,~world!}
\ExplSyntaxOff

and you don't have to first define a "shadow" function \mymodule_my_document_command:, and then copy it with

\cs_new_eq:NN \MyDocumentCommand \mymodule_my_document_command:

@EvanAad
Copy link
Contributor Author

EvanAad commented Oct 19, 2017

@blefloch And, by the way, beside \cs_generate_variant:Nn, which @eg9 mentioned, do any other functions of the l3expan module make use of the argument specifier part of the function name?

@FrankMittelbach
Copy link
Member

FrankMittelbach commented Oct 19, 2017 via email

@josephwright
Copy link
Member

I think we've talked this one through: I'll close but of course will reopen if requested.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

8 participants