Latex3: Déclarer des variables locales

Créé le 18 oct. 2017  ·  49Commentaires  ·  Source: latex3/latex3

Le document Le package expl3 et la programmation LaTeX3 (v. 2017/09/18) indique à la p. 7:

La convention de codage LaTeX3 est que toutes les variables doivent être déclarées avant utilisation.

De même, le document The LaTeX3 Interfaces (v. 2017/09/18) indique à la p. dix:

... contrairement aux variables, qui doivent toujours être déclarées

expl3 ne fournit aucune facilité pour déclarer une variable localement ; seulement globalement. Cependant, il est toujours possible de créer implicitement une variable locale en utilisant les différentes fonctions \..._set:N... .

C'est un principe de programmation structurée qu'il faut éviter de vider inutilement des variables dans la portée globale. Le langage TeX, comme tous les langages de programmation de haut niveau que je connais (y compris C, C#, Fortran, Java, Lisp, Pascal et Python), fournit une construction de portée pour créer des variables locales, à savoir des groupes. De plus, le langage de programmation LaTeX3 lui-même, heureusement, prend en charge la création de variables locales, malgré ce que suggèrent les manuels.

À mon avis, les avertissements cités ci-dessus devraient être supprimés des manuels, et il devrait être expliqué que les variables peuvent, et devraient, être créées localement chaque fois que possible.

De plus, si l'on souhaite effectivement encourager un style de programmation dans lequel toutes les variables sont déclarées avant d'être utilisées (je pense personnellement que c'est une question de style de programmation qui doit être laissée au goût individuel du programmeur), les fonctions doivent être fourni pour déclarer des variables locales tout comme il existe des fonctions pour déclarer des variables globales.

expl3 feature-request

Tous les 49 commentaires

La position actuelle fait suite à un certain nombre d'expériences menées par l'équipe pour établir un modèle qui fonctionne avec les principes fondamentaux de TeX prenant en charge expl3 . En particulier, il est important de noter que les variables peuvent être implémentées à l'aide de macros ou de registres, et de garder à l'esprit le fonctionnement du groupement TeX.

Lors de l'utilisation de registres (_par exemple_ pour le type int ), nous avons besoin d'un allocator pour lier le numéro de registre et les cs que nous utilisons pour l'accès. En revanche, pour le stockage basé sur des macros, ce n'est pas nécessaire. Ainsi, alors que \cs_set_eq:NN peut être utilisé pour générer de nouveaux noms tl (par exemple), il échouera avec tout ce qui utilise des registres :

\int_set_eq:NN \l_undeclared_int \l_tmpa_in

déclenchera une erreur.

Il est possible de créer un répartiteur de registre local, et nous l'avons déjà essayé pour _e.g._ \int_local_new:N . (Voir le package etex pour une de ces implémentations, ou regardez en arrière dans l'historique de expl3 .) La clé ici est que le regroupement TeX n'est pas basé sur des déclarations mais plutôt sur des groupes explicites, et qu'une variable locale existe dans tous les groupes imbriqués :

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

Lorsque nous avons essayé la _création_ locale de variables, il semblait qu'il y avait un danger d'obscurcir le fonctionnement du groupement TeX, et que les programmeurs pouvaient être induits en erreur.

Nous avons donc décidé d'opter pour une _déclaration_ 'tout globale' des variables, mais avec un _réglage_ à la fois local et global de ces variables (la convention \l_... _versus \g_... ). Cela localise l'important : la valeur. Nous recommandons donc que toutes les déclarations soient au plus haut niveau

\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} }

À ce jour, ce modèle semble bien fonctionner pour les tâches pour lesquelles expl3 a été utilisé.

Dans tex, même les variables définies localement ne sont pas complètement détruites après la fin du groupe. Ils utilisent toujours un espace de chaîne : https://tex.stackexchange.com/questions/316999/release-space-in-the-string-pool. Donc à mon humble avis, il est préférable de visualiser les variables comme des objets globaux.

@u-fischer Bon point : pas celui que nous avons réellement envisagé à l'époque mais qui mérite d'être pris en compte.

À mon avis, cette approche est erronée. Cela va à l'encontre des principes de programmation structurée, et cela va à l'encontre de TeX, qui permet la création de variables locales. Cela rend également LaTeX3 incohérent, car certains types de données s'adaptent aux variables locales et d'autres non.

Je voudrais vous demander de ré-instituer la création locale de variables de tous types. Cela donne le choix aux programmeurs : ceux qui préfèrent définir toutes les variables globalement peuvent le faire, et ceux qui souhaitent en définir certaines globalement et certaines localement peuvent le faire aussi.

S'il y a des mises en garde, au niveau de la mémoire ou autre, elles peuvent être mentionnées dans la documentation.

Le 18 octobre 2017 à 21h17, EvanAad [email protected] a écrit :

À mon avis, cette approche est erronée et je voudrais demander que
vous ré-instituez la création locale de variables. Cela donne aux programmeurs une
choix : ceux qui préfèrent définir toutes les variables globalement peuvent le faire, et
ceux qui souhaitent définir certains globalement et certains localement peuvent le faire aussi.

pour les variables basées sur un type de registre, vous allouez des ressources à partir d'un
pool global fixe, donc l'allocation globale du nom est en fait bien plus
Naturel. Étant donné que nous supposons etex, il y a donc plus de 256 registres de
chaque type, le comportement d'allocation peut peut-être être caché plus qu'il
pourrait avec le tex classique mais c'est toujours une caractéristique fondamentale du
système TeX sous-jacent. expl3 ne peut jamais être une programmation complètement générale
langage : il doit fonctionner avec le système TeX sous-jacent. Vous ne devriez pas être
comparaison avec C# mais avec d'autres langages basés sur TeX et en particulier avec
\newcount et amis.

Il n'est pas non plus vraiment vrai de dire que tous les autres langages permettent aux variables d'être
déclarés dans des portées locales, fortran par exemple ne permet que des variables d'être
déclaré au début d'une fonction/sous-programme.

Rien de ce qui précède ne signifie que nous n'ajouterions certainement pas de déclaration locale
système, mais faire appel à des langages à usage général n'est pas un bon cas d'utilisation.
Il faudrait des cas d'utilisation raisonnables dans la composition tex où
l'utilisation d'un système global de déclaration était un problème.

Le fait que Fortran exige que les variables soient déclarées au début d'une fonction/sous-routine ne signifie pas que ces variables sont globales. Si vous insistez pour encourager un style de programmation où toutes les variables doivent être déclarées, qu'il en soit ainsi, mais alors fournissez des fonctions pour déclarer des variables locales correspondant à celles pour déclarer des variables globales.

La justification de la portée et des variables locales est avant tout conceptuelle. Le fait que le moteur TeX sous-jacent ne libère pas une partie des ressources associées aux variables locales n'est pas à écarter, mais, à mon avis, cela ne peut pas être la raison d'effacer la notion de portée et de variables locales. À l'exception des programmeurs puissants, la mise en œuvre sous-jacente n'est pas préoccupante ; et pour les programmeurs puissants, des astuces et des mises en garde peuvent être proposées dans la documentation.

faire appel à des langages à usage général n'est pas un bon cas d'utilisation.

Je dirais que s'écarter des principes de programmation structurée qui ont été implémentés dans pratiquement tous les langages de programmation des années 1960 à aujourd'hui, y compris TeX, devrait être quelque chose que l'équipe LaTeX3 devrait avoir des raisons très impérieuses de le faire. La charge de la preuve devrait incomber à ceux qui souhaitent abolir ce qui est omniprésent considéré comme une bonne pratique de programmation, et ce qui existe déjà dans TeX ; pas sur ceux qui souhaitent le préserver.

La liste de jetons globalement pour que l'allocation ne touche pas un registre déjà assigné.

Supposons que votre variable entière locale \x soit affectée au registre 100 qui est déjà pris par la variable \y à un niveau supérieur ; utiliser \y au même niveau où \x est défini localement serait tout simplement désastreux. Ainsi, une comptabilité globale des registres attribués est nécessaire, ce qui rend très difficile la libération des registres attribués localement. Réserver un bloc de registres pour des affectations locales n'est pas une solution.

Je ne dis pas que cela ne peut pas être fait, mais je ne pense pas que cela en vaille la peine.

vous manquez le fait que \newcount n'est pas simplement localement (ou globalement)
déclarer un nom, c'est associer un nom à un
Ressource. il n'y a qu'un seul registre de comptage 42 et tous les noms qui
sont déclarés signifier count42 font référence au même registre, que ce soit
l'attribution de nom est locale ou globale. Comme je l'ai mentionné, les comparaisons avec d'autres
les langues ne sont pas très utiles mais si vous voulez une comparaison, vous devriez
comparer l'allocation de flux de fichiers ou autres : vous ne pouvez pas toujours isoler
déclarations locales lorsqu'elles s'interfacent avec un
Ressource.

Le 18 octobre 2017 à 21h43, EvanAad [email protected] a écrit :

Le fait que Fortran exige que les variables soient déclarées au début d'un
fonction/sous-routine ne signifie pas que ces variables sont globales. Comme je
écrit, si vous insistez pour encourager un style de programmation où toutes les variables
doit être déclaré, qu'il en soit, mais ensuite fournir des fonctions pour déclarer local
variables correspondant à celles pour déclarer les globales.

La justification de la portée et des variables locales est avant tout
conceptuel. Le fait que le moteur TeX sous-jacent ne libère pas certains
des ressources associées aux variables locales n'est pas à écarter,
mais, à mon avis, cela ne peut pas être la raison d'effacer la notion
de la portée et des variables locales. À l'exception des programmeurs de puissance, le
la mise en œuvre sous-jacente n'est pas préoccupante ; et pour les programmeurs de puissance, des conseils
et des mises en garde peuvent être proposées dans la documentation.

-
Vous recevez ceci parce que vous avez commenté.
Répondez directement à cet e-mail, consultez-le sur GitHub
https://github.com/latex3/latex3/issues/410#issuecomment-337721923 , ou couper le son
le fil
https://github.com/notifications/unsubscribe-auth/ABNcAimMfBDqA-e96Q7tkS-ERr5fv_2Mks5stmLqgaJpZM4P-Mpq
.

Le point crucial est que, comme Joseph et David l'ont mentionné, permettre à int s d'être défini localement peut être (et a été ) fait. Je pense qu'il vaut la peine afin de créer une langue qui présente une couche d'abstraction cohérente qui est en accord avec les principes de la programmation structurée.

Cependant, dire que cela en vaut la peine est facile à dire pour moi, car je ne suis pas celui qui est soumis à la douleur de l'implémentation du langage LaTeX3. Alors adoptons une approche miséricordieuse et disons qu'en fait, cela n'en vaut pas la peine. Amende. Dans ce cas, divisez les types de données en deux catégories : celles qui permettent la création de variables locales et celles qui ne le permettent pas. Et décrivez ces catégories dans les manuels. Ne dites pas : toutes les variables doivent être déclarées avant utilisation. Dites : « Il existe deux catégories de types de données. La première, constituée de cs , tl , clist , ... accueille les variables locales, et la seconde, constituée de int , ... non." Et expliquez pourquoi la deuxième catégorie existe. De cette façon, la documentation est fidèle aux faits, et les programmeurs sont bien informés et ont le choix. Certains programmes n'ont pas du tout besoin d'utiliser des types de données de la deuxième catégorie, et dans ces cas, pourquoi le programmeur devrait-il déclarer des variables globales ?

@EvanAad Je soupçonne que c'est parce que je suis habitué à la programmation TeX (y compris les restrictions «traditionnelles» de TeX90), mais je ne sais pas actuellement quel est le problème avec l'approche actuelle. Nous prenons en charge et encourageons les variables attribuées localement : elles sont très courantes et font partie de la syntaxe que nous avons pour la dénomination des variables ( \l_... / \g_... ). Le fait qu'ils soient alloués/'réservés' globalement n'interfère pas avec cela.

BTW, nous ne voulons pas lier les interfaces à l'implémentation : par exemple, le type de données prop a eu au moins deux implémentations différentes à ma connaissance, dont l'une utilisait des registres et l'actuelle utilisant des macros.

Pour ajouter une note supplémentaire à cela, la goutte d'eau qui a fait déborder le vase (dans ma mémoire) que le registre local/système d'allocation de variables est tombé en panne était à cause d'une incohérence . Parce que les macros et les registres se comportaient différemment, vous vous êtes retrouvé avec un comportement différent lors de l'écriture

\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`

Et la seule façon de résoudre ce problème serait d'écrire un système d'allocation pour les macros, qui n'a jamais été sérieusement envisagé en raison de la surcharge. (TeX ralentit au fur et à mesure que plus de variables sont définies, et donc le fait de doubler le nombre de listes de jetons aurait pu affecter sensiblement les performances.)

Je pense toujours que expl3 fait la bonne chose en faisant abstraction des macros et des registres dans des fonctions qui ont la même apparence et la même sensation - et si l'inconvénient est que la syntaxe ne prend pas naturellement en charge les variables et les registres vraiment locaux, qui ont vu utilisation extrêmement limitée dans TeX pour les raisons décrites ci-dessus, alors de mon point de vue, c'est un compromis acceptable.

@wspr Mais les exemples que vous avez cités comme ceux qui ont cassé le dos du chameau sont des exemples où le programmeur a abusé du langage en attribuant globalement à une variable locale. C'est sur le programmateur. Le système actuel ne résout pas le problème de l'abus de langage, car un programmeur peut toujours utiliser une variable \l_... comme si elle était globale.

... qui est en accord avec les principes de la programmation structurée.

c'est une déclaration assez audacieuse étant donné qu'il y a plus d'un ensemble de
principes et étant donné que les tentatives d'unir des principes différents dans
les sur-ensembles ont généralement créé des langages volumineux mais inutiles dans le passé.

Le point crucial n'est pas nécessairement que quelque chose puisse être fait (comme
dans les bases complètes de Turing, toutes choses sont égales à ce niveau) mais
si quelque chose peut être fait de manière efficace et cohérente, etc.

@wspr : Pourquoi avez-vous fermé ce problème comme un tyran ? C'est tout sauf réglé.

@EvanAad - essayant juste de garder les choses en ordre. La discussion peut certainement continuer et si cela est justifié, nous rouvrirons.

Je bloque temporairement ces problèmes en raison de commentaires passionnés.

Je pense que c'est une question pertinente. L'un des résultats de cette discussion devrait au moins être que nous décrivions plus soigneusement dans le document pourquoi nous avons choisi uniquement des déclarations globales. @EvanAad Je ne pense pas que vous \int_local_new:N hypothétique ou un système différent de votre choix avec la sémantique de votre choix ? Cela aiderait à encadrer la discussion dans un cadre plus concret, et nous pouvons en tirer des avantages/inconvénients.

Vraisemblablement, nous devons attendre que le verrou se lève, je ne sais pas comment faire. Dans tous les cas, la plupart des personnes dans cette conversation vont dormir pendant quelques heures. (Au fait, je ne pense pas que copier le n° 410 dans le n° 411 en tant que discussion confuse était très raisonnable.)

En regardant le fait qu'il s'agit d'une discussion assez détaillée, je pense qu'il vaut la peine de résumer l'histoire technique et sociale qui nous a amenés là où nous en sommes.

Au niveau technique, TeX fournit des registres et des macros pour le stockage. Les macros peuvent simplement être "créées" en utilisant \def , mais pour utiliser des registres par nom, nous avons besoin d'un allocator pour lier le numéro de registre global, _e.g._ \count40 , à un nom, par exemple \mycount . Cela a été fourni dès le « premier jour », avec le simple TeX \newcount , _etc._ fournissant le modèle. En clair, \newcount alloue globalement et, malgré son nom, ne fait aucune vérification. D'autres formats, notamment LaTeX2e et ConTeXt, ont tous deux adopté cette approche en termes généraux. Ils ont également généralement adopté l'idée que les registres individuels sont ensuite _assignés_ localement ou globalement, car cela empêche l'accumulation de sauvegardes.

TeX90 ne fournissait que 256 registres des types courants, la réutilisation des registres dans des contextes locaux était donc vitale. On voit que dans _e.g._ graphics où pour garder le code « sain », un grand nombre de registres reçoivent de nouveaux noms à l'intérieur d'un groupe, et sont utilisés uniquement localement. Avec e-TeX, nous avons _lot_ plus de registres, donc cette approche est moins nécessaire : nous pouvons librement allouer plus de registres (et de macros) à des fins dédiées. En particulier, cela signifie que nous ne sommes en rien sous pression pour « recycler » des registres sous plusieurs noms.

La base de code de expl3 est en développement depuis _long_ temps, et à l'origine ne nécessitait pas e-TeX. Le développement de expl3 est également effectué principalement "au-dessus" de LaTeX2e, avec un principe général selon lequel expl3 ne pollue pas l'espace de nom du document et ne modifie pas les comportements de base de LaTeX2e.

En particulier, nous devons garder à l'esprit que le \newcount LaTeX2e est similaire à celui de plain : il alloue globalement et ne vérifie pas. Ainsi notamment

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

est « mauvais style » comme si \foo était appelé plusieurs fois, nous utiliserions des registres qui ne seraient jamais libérés.

Pour expl3 , l'équipe a essayé d'éviter de lier le comportement documenté des variables à l'implémentation de macros ou de registres. (Je note que nous autorisons l'utilisation de tl sans accesseur, ce qui en fin de compte dépend du fait qu'il s'agit de macros.) Nous utilisons également des registres pour les entiers, les dimensions, _etc._ car ils sont disponibles et offrent de meilleures performances et « auto-arrêt » que de tout faire dans les macros. (Ce serait faisable en utilisant e-TeX et les diverses primitives \<thing>expr .) En tant que tel, nous devons avoir un système d'allocation, et pour être cohérent, nous allouons tous les types de variables, pas seulement celles qui sont basées sur des registres . Comme @EvanAad l' a observé, lorsque la vérification n'est pas active, on peut faire _par exemple_ \tl_set_eq:NN \l_new_tl \l_existing_tl sans erreur, mais ce n'est pas le comportement pris en charge (la vérification le signalera, par exemple).

Dans le cadre de l'allocateur expl3 , il a été décidé que new _vérifierait_ l'existence, contrairement à \newcount , donc

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

lèvera une _erreur_ si \my_foo: est utilisé à plusieurs reprises. Cela pousse un dans la direction qui a été la pratique standard de TeX depuis les premiers jours de plain : les allocations sortent de la macro. (Notez qu'en clair, \newcount est \outer donc est 'interdit' dans les macros.)

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

Le package etex introduit il y a de nombreuses années un répartiteur de registre qui peut faire une répartition locale et globale du registre, \loccount _versus_ \globcount , _etc. Pendant un certain temps, expl3 chargé etex et il a été utilisé pour fournir des fonctionnalités pour \int_local_new:N et similaires. Étant donné que la plupart des programmeurs TeX sont habitués à faire de l'allocation globale, il n'est peut-être pas surprenant que cela n'ait pas été utilisé aussi largement. Cependant, l'équipe était également préoccupée par d'éventuels malentendus. Avec quelque chose comme

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

nous avons la question de ce qui se passe sur les appels imbriqués à \my_foo: , ou plus probablement aux endroits où un nom "simple" tel que \l_my_tmp_tl doit être utilisé. Basé sur l'idée générale que nous voulons que new fassent des vérifications, cela devrait être une erreur. Les programmeurs venant de beaucoup d'autres langages pourraient trouver cela quelque peu étrange. Bien sûr, cela correspond aux règles de portée de TeX.

Notamment, les modifications apportées au noyau LaTe2e ces dernières années signifient que nous ne voudrions plus charger etex (en effet, nous avons travaillé dur pour rendre expl3 'autonome'). Ainsi, tout nouvel allocateur local devrait être écrit pour fonctionner avec LaTeX2e sans comportement 'perturbant' pour ceux qui n'utilisent pas expl3 : faisable mais pas entièrement trivial.

Pour quelque chose comme \l_my_tmp_tl on peut bien sûr toujours faire une allocation globale, mais il faut alors savoir quelles variables \l_... sont allouées localement dans le groupe actuel et lesquelles sont allouées globalement mais localement.

Je n'ai pas de données de test à portée de main, mais il vaut probablement la peine de noter qu'allouer une variable est plus de travail que de simplement la définir (certainement lorsque la vérification n'est pas active), donc l'utilisation d'un allocator local serait légèrement plus lente qu'un allocator global plus missions locales.

Ainsi, pour un mélange de raisons techniques et de « concordance avec l'histoire », nous avons décidé de nous en tenir à une _allocation_ strictement globale. Cela n'empêche en aucun cas l'assignation locale de variables, qui sont encouragées et très largement utilisées.

Cela nous amène à l'élément « social ». La consommation de expl3 au cours des dernières années a été fortement facilitée en rendant les pièces « largement » stables. À un moment donné, l'équipe doit prendre une décision sur des choses, en partie pour que les gens puissent l'utiliser et en partie pour que nous passions à d'autres tâches. Cela ne nous empêche pas de revisiter les choses, mais les conventions plus établies ont besoin d'une bonne raison pour être modifiées.

Ici, à l'heure actuelle, je suppose que je ne vois pas ce qui ne va pas avec l'approche « habituelle » de

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

au moins au point où il est nécessaire de revenir en arrière et de changer la configuration actuelle.

@blefloch Oui, je suis d'accord que ma réaction à la fermeture du fil par @wspr était déraisonnable et je m'excuse auprès de lui et du reste d'entre vous. J'ai senti que j'étais rejeté et réduit au silence, et j'ai réagi avec une crise de colère. C'est inacceptable. Désolé pour ça.

@josephwright merci pour la réponse détaillée. Après avoir réfléchi à la question, j'ai réalisé que si j'utilisais uniquement \<module>_clear_new:N , alors toutes mes variables se comporteront comme si elles étaient locales au groupe environnant, tant que j'affecte en utilisant \<module>_set... plutôt que \<module>_gset... . De plus, je pense que cette pratique est conforme à la convention LaTeX3 de déclaration d'une variable avant utilisation, je ne serai donc pas signalé si les vérifications sont activées.

En suivant cette pratique, je réécrirais le dernier exemple de @josephwright ainsi :

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

Le seul type de données qui ne correspond pas à ce schéma est cs , mais pour autant que je sache, il est légal de créer des variables de ce type avec \cs_set:Npn et al. sans les déclarer d'abord, car LaTeX3 ne traite pas vraiment cs comme un type de données normal à égalité avec les autres (ce qui est un problème distinct que j'aimerais aborder avec vous, mais je vais laisser à un autre fil). En utilisant cette approche, les variables cs peuvent également être créées localement. Si je veux garder une syntaxe cohérente, je peux toujours écrire mon propre wrapper \cs_clear_new:N .

Donc, en ce qui me concerne, vous pouvez maintenant fermer ce problème si vous le souhaitez, @wspr .

@EvanAad Sur \<thing>_clear_new:N , notez que la déclaration _est_ globale où la variable n'existe pas déjà. Donc

\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

montrera que \l_my_tl est défini et vide, en supposant que vous ne l'ayez pas précédemment défini sur autre chose.

J'aimerais rejoindre @blefloch en espérant que cette discussion conduira à une meilleure documentation. En particulier, je pense que lorsque vous écrivez que le programmeur "doit" suivre un certain schéma de codage, comme dans les phrases que j'ai citées dans mon message d'origine, la documentation devrait expliquer ce que cela "doit" signifier. Que se passera-t-il si le modèle n'est pas respecté ?

  1. Le comportement de la langue sera-t-il indéfini ?
  2. La non-adhésion est-elle actuellement prise en charge, mais peut-être pas dans les futures versions ?
  3. Le moteur signalera-t-il une erreur ?
  4. Le moteur signalera-t-il une erreur, mais uniquement lorsque les contrôles sont activés ?
  5. Y aura-t-il des désagréments mineurs ?
  6. L'équipe LaTeX3 grognera-t-elle de mécontentement, mais aucune erreur ou perte de fonctionnalité n'en résultera ?

Comme exemple pour 5, prenons la convention d'ajouter des spécifications d'argument à la fin du nom d'une fonction. Pour autant que je sache, la seule fonction qui ne fonctionnera pas correctement si cette convention n'est pas respectée est \cs_new:Nn , mais le reste, y compris `cs_ new:Npn ', fonctionnera parfaitement.

Comme exemple pour 6, prenons la convention de marquer les variables locales et globales avec \g_... et \l_... . Autant que je sache, il n'y aura absolument aucune répercussion à ne pas suivre cette convention. Tout fonctionnera correctement.

@EvanAad - excuses acceptées, et je suis désolé pour ma part d'avoir clos le problème avant la fin de la discussion.

@josephwright - comme un tout petit avantage des avantages de l'allocation locale, si j'écris

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

alors je sais que \l_my_tl n'est pas seulement exempt d'interférences de l'extérieur, mais contrairement à l'utilisation d'une fonction _clear , je sais que toute trace de la variable est partie en dehors de l'utilisation de la fonction. En d'autres termes, rien qu'en regardant le code, je sais qu'il ne peut pas être utilisé comme variable semi-globale sur toute la ligne. (Et je peux vérifier qu'il ne se passe rien d'étrange en utilisant \tl_if_exist_p:N .)

(Doit être exécuté mais peut continuer plus tard s'il est utile de discuter davantage.)

@wspr Oui, mais cela revient au fait qu'il est basé sur la portée du groupe TeX. Il peut donc être utilisé « semi-globalement » dans une construction de la forme

\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"
...
  }

ce qui faisait au moins partie de notre réflexion.

Je pense qu'avant de quitter ceci, je voudrais souligner qu'à un niveau technique, il existe un certain nombre de façons de mettre en place un répartiteur de registre local.

@josephwright - Je n'ai jamais vu cela comme un exemple déroutant; dans Matlab, par exemple, ils font la distinction entre les sous-fonctions qui ne partagent pas la portée et les sous-fonctions imbriquées qui le font. Donc , à ma façon de penser qui a toujours été, eh bien, pourquoi pas \__my_foo: Hériter la portée de la fonction « externe »? C'est parfaitement cohérent avec le comportement de regroupement de TeX.

(Désolé de poursuivre la discussion. La journée a été longue. Est-ce que quelqu'un souhaite continuer ?)

Am 19.10.2017 à 09:23 schrieb Joseph Wright :

Je pense qu'avant de quitter ceci, je voudrais souligner qu'à un
niveau technique, il existe plusieurs manières de mettre en place un registre local
répartiteur.

oui mais assez proche de l'argument de Turing, c'est-à-dire une telle implémentation
va être très inefficace au moment de l'exécution car le moteur sous-jacent
gère globalement le stockage des registres. Et expl3 (à ce niveau)
doit rester « raisonnablement efficace ».

c'est comme, disons, les variables globales et locales et leur mutateur. Plutôt
que de tester dans chaque fonction si elle effectue une opération globale sur un
variable locale, le concept n'est par défaut présent que dans les noms,
par exemple \l_... est censé être une variable locale et ne doit pas être utilisé
avec une fonction globale comme ..._ gset:Nn mais nous ne testons pas cela à
Durée

Cependant, nous proposons un module de vérification (qui s'exécute combien de fois plus lentement)
qui garantit que toutes ces conventions sont effectivement respectées.

Donc pour revenir aux variables globales/locales

le concept standard de expl3 est

  • déclarer le nom d'une variable une fois (globalement) -- plusieurs car à
    au moins certains des types n'ont que des bacs de stockage globaux

  • utilisez la convention de nom \l_ \g_ pour désigner les variables locales et globales

qui vous donne à la fois des globals et des locaux mais a les restrictions qui

  • vous ne déclarez pas une variable locale au début de sa portée,
    à la place, vous utilisez _set ou _clear_new à ce stade et ce dernier
    peut signifier que le nom de la variable peut venir à ce point globalement
    dans l'existence

  • en dehors de la portée, la variable existe toujours avec la valeur par défaut
    valeur du type (par exemple _int étant 0 etc) afin que vous n'obteniez pas de compilateur
    erreur si vous faites référence à une telle variable "par erreur" en dehors de sa destination
    portée

Donc, fondamentalement, la seule chose que vous n'obtenez pas, c'est de pouvoir déclarer un
variable locale de sorte que son nom disparaisse en dehors de la portée (et produise
une erreur indéfinie d'une certaine sorte au moment de l'exécution si elle est mentionnée en dehors du
portée déclarée).

Pour ce faire (ce qui est possible), expl3 aurait besoin de maintenir son propre
pool de ressources bien au-delà d'une simple association entre un nom et un global
piscine offerte par le moteur et cela signifierait que tout accès serait
fait très notablement ralenti - ce qui va à l'encontre des critères de conception d'expl3.

@FrankMittelbach - maintenant vous m'avez curieux; L'approche etex.sty pour les registres est-elle vraiment si inefficace ? Ou peut-être voulez-vous dire pour les variables tl, auquel cas je suis d'accord !

@FrankMittelbach À mon avis, il est important de distinguer et de préciser dans la documentation ce qui est une meilleure pratique selon l'équipe LaTeX3 par rapport à ce qui est une exigence formelle, grammaticale ou sémantique, des règles du langage LaTeX3.

Les deux règles que vous avez citées, à savoir :

  • déclarer le nom d'une variable une fois (globalement)
  • utilisez la convention de nom \l_ \g_ pour désigner les variables locales et globales

relèvent de cette rubrique de conventions de codage que l'équipe LaTeX3 juge souhaitable, mais aucune d'entre elles n'est mandatée par les règles grammaticales du langage, et le non-respect de ces conventions ne provoque aucune erreur ni perte de fonctionnalité.

En d'autres termes, suivre ces règles est une question de goût personnel et de style de codage, et cela devrait être précisé dans la documentation, à mon avis.

c'est un peu comme dire que le signe 50mh n'est pas une règle mais une conduite
convention et c'est une question de goût si un conducteur y adhère (juste
car il n'est pas immédiatement vérifié la plupart du temps)

si vous utilisez une variable \l_ avec _gset alors vous programmez toujours en TeX mais
vous avez cessé d'obéir aux règles grammaticales de l'expl3
Langue. Est-ce que ça casse immédiatement votre code ? probablement pas, mais vous
générer une accumulation de sauvegardes (voir l'index TeXbook)

dites-vous que ce n'est qu'une règle grammaticale si nous la vérifions à
temps d'exécution, disons, chaque mardi ?

@FrankMittelbach Bien sûr, si vous définissez cela comme faisant partie des règles de la langue, ce n'est pas le cas par définition, mais mon point est que cela ne devrait

C'est la différence entre dire "Lorsque vous écrivez en anglais, il est préférable de tenir le stylo dans votre main droite car cela évite de salir l'encre." et passer une loi qui dit que l'anglais doit être écrit avec le stylo tenu dans la main droite. Bien sûr, vous pouvez adopter une telle loi, et il y a de bonnes raisons de la justifier, mais en fin de compte, le choix de la main dans laquelle tenir le stylo devrait être laissé à l'auteur individuel. Et il y aura des gens qui choisiront de ne pas adhérer à cette règle, et finiront quand même par écrire aussi proprement que ceux qui y adhèrent.

Je pense que @FrankMittelbach surestime la surcharge des déclarations locales ( \loccount gère les décomptes localement, rien de ce qu'il fait n'est global, ce qui maintient la surcharge correcte).

Je ne vois pas de problème majeur à fournir un \int_local:N et \tl_local:N (= \tl_set_eq:NN #1 \c_empty_tl ) etc qui serait très analogue à \int_zero_new:N et \tl_clear_new:N mais ne ferait que "nouveau" localement. Cela demande un peu de travail pour les registres mais pas trop.

@EvanAad il faut savoir que ne pas déclarer une variable peut vous mordre à un moment donné (attention : ce code produit une infinité de pages).

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

@blefloch Ce que je dis, c'est que c'est parfaitement bien de déclarer une variable à l'intérieur d'une macro (avec \<module>_clear_new ), et vous n'avez pas besoin de la nommer \l_amount_paid_int , vous pouvez simplement appeler it \amount_paid ou \amountPaid , comme vous le feriez dans n'importe quel autre langage de programmation. Le \l_..._int est un bon mnémonique pour vous rappeler qu'il s'agit d'un entier et qu'il est censé être utilisé localement, mais vous ne devriez pas être obligé de l'utiliser, ou tout autre mnémonique.

parce qu'elle n'est jamais vérifiée, et parce qu'elle n'adhère pas à cette convention
ne provoque pas en soi une erreur ou une perte de fonctionnalité.

mais c'est le but

a) cela cause des dommages selon les circonstances -- à savoir lorsque vous mélangez
affectations globales et locales à la même variable

b) nous vérifions sur demande (et pour le moment, ce code peut ne pas être
fonctionnel mais c'était et sera probablement encore à cause de a))

ps oui j'ai compris votre point de vue sur la tenue du stylo (être gaucher) et oui je
J'ai dépassé la limite de vitesse (sur mon vélo) mais je considère quand même que c'est un
la règle de la circulation n'est pas une convention de circulation et oui on peut désobéir à cela
sans dommage, mais on peut aussi en mourir ou au moins se retrouver avec une amende

(Ce que mon exemple de code précédent soulignait, c'est que même pour tl, il est essentiel de les déclarer avant utilisation. Il existe une option de vérification qui teste les déclarations, mais en utilisation normale, nous ne voulons pas de tels frais généraux.)

Je suis d'accord avec @EvanAad que les noms ne sont qu'une convention. Nous pouvons vérifier que les affectations locales et globales ne sont pas mélangées même sans la convention de nom l_/g_ : puisque le code de vérification peut être quelque peu lent, il est tout à fait raisonnable de stocker les informations indiquant si une variable donnée a été utilisée dans un environnement local/global mission. Pour la vérification des types, la situation est similaire, à l'exception de deux paires de types qui ne peuvent pas être distinguées (je ne vais pas dire lequel éviter de faire dérailler la conversation).

Sur 'conventions', \amountPaid est une commande de document alors que \l_amount_paid_int ne l'est pas, et cette dernière fait partie de l'espace amount noms \l_ / \g_ , bien que je pense que de nombreuses langues ont une forte « orientation » sur le nommage sans que cela ne soit appliqué à un niveau technique.

Le 19 octobre 2017, à 16h58, Joseph Wright [email protected] a écrit :

Sur les 'conventions', montantPaid est une commande de document alors que \l_amount_paid_int ne l'est pas, et ce dernier fait partie de l'espace de noms de montant (par convention mais assez important dans TeX). Cela ne s'applique pas à \l_/\g_, bien que je pense que de nombreuses langues ont une forte « orientation » sur le nommage sans que cela ne soit appliqué à un niveau technique.

Le pire cauchemar de la programmation LaTeX2e est de définir une commande
dans "l'espace de niveau utilisateur", disons \foo, pour découvrir qu'il se heurte
avec une commande un autre package défini pour une utilisation _interne_ (donc
non documenté dans le manuel).

Est-ce vraiment arrivé? Oui, et pas qu'une seule fois. S'en tenir au
\La convention
de tels problèmes.

Bien sûr, des conflits au niveau de "l'espace de niveau utilisateur" peuvent également se produire,
mais ils sont beaucoup plus faciles à repérer et à résoudre. Quand il s'agit de la
internes, il est souvent nécessaire de chasser les extensions à un très
niveau profond.

Contrairement aux législateurs, nous ne pouvons pas imposer d'amendes ou de prison
qui n'adhère pas aux lois de la programmation LaTeX3. Mais étaient
une communauté et tout le monde devrait.

Nos directives sur les conventions de nommage devraient aider à ne jamais trouver
les commandes internes d'un paquet pour entrer en conflit avec d'autres.

Pour le code personnel on a le droit de faire ce qu'on veut : il y a
aucune loi interdisant à quelqu'un de faire de la vitesse dans sa propriété privée, mais
il y en a un sur le fait de le faire sur une voie publique.

Si vous souhaitez définir localement la variable \f de quelque type que ce soit,
vous devriez vous inquiéter de la définition de la commande par certains
paquet et, par la loi de Murphy, se terminant exactement par l'argument
à une fonction utilisant la variable \f. Pouvez-vous imaginer pire
scénario?

Ciao
Enrico

et vous n'avez pas besoin de le nommer \l_amount_paid_int, vous pouvez simplement l'appeler montant_payé comme vous le feriez dans n'importe quel autre langage de programmation. Le \l_... est un bon mnémonique pour vous rappeler qu'il est censé être utilisé localement, mais vous n'avez pas à l'utiliser, ni aucun autre mnémonique.

Sûr. Vous pouvez également utiliser md5-fc693aa157832059d7daeeb61c55 cddb:paid ou amount&paid (ce n'est pas une blague, je connais un package qui utilise &) ou ce que vous préférez. Mais même si les noms ne sont qu'une convention : cela facilite la communication si les gens s'en tiennent à de telles conventions. Vous avez posé pas mal de questions sur tex.sx. Que ferez-vous si vous rencontrez un problème avec votre code écrit en "style personnel" ? Traduire dans le style standard, poser une question et la traduire en retour ? Ou vous attendez-vous à ce que tout le monde soit capable de gérer votre style personnel ?

@eg9

Pour le code personnel on a le droit de faire ce qu'on veut

Vous ne le sauriez pas en lisant la documentation, c'est tout ce que je dis.

Si vous souhaitez définir localement la variable \f de quelque type que ce soit, vous devriez vous inquiéter de la définition de la commande par un package

Je ne suis pas d'accord. Si votre code est à l'intérieur d'un \group_begin: ... \group_end: , et si vous définissez toutes les variables locales avec \<module>_clear_new:N , et si vous n'affectez qu'aux variables locales avec \<module>_set:N... , vous n'avez pas à vous soucier de vos variables locales entrent en conflit avec d'autres packages, à moins que votre code n'utilise un autre package.

@u-fischer

Mais même si les noms ne sont qu'une convention : cela facilite la communication si les gens s'en tiennent à de telles conventions.

Je ne dis pas qu'il n'y a pas de bonnes raisons de s'en tenir aux conventions. Tout ce que je dis, c'est que ces conventions ne devraient pas être des règles de langage, et la documentation devrait clairement différencier les conventions, pour lesquelles la justification devrait être énoncée, et les règles. Et le choix de suivre ou non les conventions devrait appartenir en fin de compte au programmeur.

@EvanAad Vous devez vous soucier des conflits de noms même dans le cas que vous décrivez. Dis que tu écris

\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:
  }

alors un utilisateur de votre package fait

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

Ils seront surpris de voir 0 et non 62.

D'un autre côté, si vous vous en tenez à des noms tels que \evanaad_f etc (ou plus court \@@_f etc en utilisant la magie l3docstrip ), vous devriez être en sécurité.

Vous ne le sauriez pas en lisant la documentation, c'est tout ce que je dis.

Désolé mais expl3.pdf utilise le mot "convention" environ 20 fois. Dans le cas d'une commande publique contre une commande privée, il y a même la phrase « Il n'y a (presque) aucun moyen d'appliquer cela sans une surcharge de calcul importante, nous ne l'implémentons donc que via une convention de nommage ».

@blefloch Bon point.

@u-fischer OK, d'accord. Qu'en est-il de la convention selon laquelle le nom d'une fonction doit se terminer par un spécificateur d'argument ? Ce n'est pas entièrement une convention, car les fonctions de la section 3.3 inspectent le spécificateur d'argument, mais ces fonctions ne sont que du "sucre syntaxique", et si vous ne les utilisez pas, il n'y a aucun obstacle à l'utilisation de noms de fonctions comme \mymodule_myfunc , mais vous ne le sauriez pas d'après le manuel.

Le 19 octobre 2017, à 17h52, EvanAad [email protected] a écrit :

@u-fischer OK, d'accord. Qu'en est-il de la convention selon laquelle les noms des fonctions doivent se terminer par un spécificateur d'argument ? Ce n'est pas entièrement une convention, car les fonctions de la section 3.3 inspectent le spécificateur d'argument, mais ces fonctions ne sont que du "sucre syntaxique", et si vous ne les utilisez pas, il n'y a aucun obstacle à l'utilisation de noms de fonctions comme \mymodule_myfunc.

C'est nécessaire pour cs_generate_ variant:Nn , bien sûr.

Ciao
Enrico

Avec \mymodule_myfunc vous ne pourrez utiliser aucune fonction de
l3expan . Ces fonctions d'expansion, et la notion de variantes, sont un
partie centrale de expl3.

Bien que je convienne que les noms de variables pourraient être raccourcis (en supprimant
"l_"/"g_" et "_int"/...), la signature de fonction n'est vraiment pas "juste un
convention".

@blefloch Je vois votre point, et c'est un bon, mais quand même, à mon avis si tout ce que vous voulez est d'écrire une commande de document, vous devez savoir que vous pouvez le faire en définissant une fonction comme

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

et vous n'avez pas besoin de définir d'abord une fonction "shadow" \mymodule_my_document_command: , puis de la copier avec

\cs_new_eq:NN \MyDocumentCommand \mymodule_my_document_command:

@blefloch Et, en passant, à côté de \cs_generate_variant:Nn , que @eg9 a mentionné, est-ce que d'autres fonctions du module l3expan utilisent la partie spécificateur d'argument du nom de la fonction ?

il me semble maintenant que vous vous disputez en grande partie pour le plaisir de discuter

oui tu peux faire tout ça et à la fin de la journée la seule langue dure
les règles sont ce que le moteur TeX câble dans ses primitives. Et donné
que TeX est un langage auto-modifiable que vous pouvez utiliser pratiquement n'importe où
là par exemple

\endlinechar-1\def~#1{\catcode`#113}~Q~S~U~_~V~W~J~K~L~M~N~O~@~X~Y~[~] ~(
~|~&~Z~'~"~ ~h~z~:~q~j~k~;~/~)~!~,~$~+\let_\let_~newcount~$$-1~Q ~J~V~S~K~W~U~L~,~''1~""2~ *1_&\count&144'&155'&145"&154"_[\ifnum_(\ifcase_O\ou
_|\else_]\fi_N\number_@\advance_X\expandafter_Z\global_Y\typeout_~newif
~\ifG~\if_~\def~j{[0 Q[0Jk|$]|$]|$]|$]}~k{&1NQNJ}~\2#1#2{}~:#1{

11#12#13#14#15#16#17#18}~h#1#2{#2 :{~\q#1}~#2^^J}~\q#1#2{(&1 #1#2~~OO$]}

~/{Y{Ligne et colonne ? par exemple E6}\read$toM\ifcat~X\2M~$$X\jM|!input!]}~!#1!{
Y{Invalide #1.}/}~\j#1#2{Q #1@Q- @J #2@J- 0;(V!bouge!]}~;{V0 (jS1z1z0z{$} S
0z1z{$}S$z1z0z{$}]}~_{@,\ifodd'-]}~z#1{{\trueK#1{\falseq}}}~q{@ QS@JK [j="
\ifZk'Z_2]@V1q|[j='ZVV\ifG\if|\aftergroupq]]]]}~\,#1{Q#1:.}~.#1{J#1;[0
WWVUQLJ]]}~+#1{(#1O2O-2O0O0O0O0O-2O2]}~){'X"X"N'Y{^^J :
~^^Jh1Ah2Bh3Ch4Dh5Eh6Fh7Gh8H :~^^J}\GfalseW(W$|0]~:\,\Gtrue[0 /];k'_1][$=WY{(,Égalité| Le joueur [0>,.|$]~gagne par N[0>,-],].}X\dump])}~~{ })

qui est un beau document TeX (en fait un beau document LaTeX)
mais en ce qui concerne son code, il n'est pas du tout très utile. Et
le fait que Bruno soit capable d'écrire un tel document ne signifie pas que
le manuel expl3 (ou dans ce cas un manuel LaTeX) devrait décrire tout cela.

Le code LaTeX (2.09 et aussi 2e) avait le gros problème qu'au début
trop de personnes qui programment ont compris les raccourcis de bas niveau
dans TeX et les ont utilisés et abusés parce qu'ils pensaient que ce n'était pas
nocif. En conséquence, une énorme quantité de packages 2e existants sont
incompatibles les uns avec les autres ou ont des problèmes subtils dans les endroits lorsqu'ils sont utilisés
ensemble, etc. ou se cassent de temps en temps parce qu'ils ont contourné l'un ou le
interface utilisateur (parce qu'elle semblait fonctionner sans elle).

En gros, vous nous demandez encore et encore de documenter cela,
c'est-à-dire des raccourcis possibles qui violent les principes de conception
car ils fonctionnent parfois (ou même en ce moment toujours). Mais expl3
et ses conventions/règles sont largement dérivées de l'expérience que
les codeurs ont désobéi à ces règles dans le passé et le désordre qui en résulte
de cela. Alors non, les règles sont délibérées et pas seulement de simples caprices (la plupart
du temps) et même si « si vous savez ce que vous faites, vous
peut contourner la plupart d'entre eux dans une situation spécifique" cela ne signifie pas que
si vous déplacez votre code, d'un endroit à l'autre, il sera toujours
le cas ou doit-il être le cas au fil du temps.

Comme d'autres l'ont dit, il y a une grande différence entre le code sur lequel vous écrivez
la mouche pour vous-même et le code que vous écrivez comme "officiellement" distribué
emballer. Au moins pour ces derniers, nous demandons d'accepter les règles et
considérez-les comme faisant partie de la langue. Pour vous-même, vous voudrez peut-être
apprendre à coder un document comme ci-dessus mais la connaissance comment faire
ça ne sortira pas du manuel expl3


ceci dit, je ne veux pas vous décourager des concepts difficiles,
commandes, interfaces, qu'avez-vous. Bon nombre des points que vous avez soulevés sur
d'autres occasions ont été bien saisies (ou du moins nous ont fait repenser une
ou l'autre point).

Mais en ce qui concerne le manuel expl3, je pense que ce que vous avez entendu de
plusieurs personnes, c'est qu'il n'y a aucun intérêt à documenter
« non-conventions » et les « non-règles ». De plus si le code détourne trop
d'après ce que nous appelons règles/conventions, ce sera toujours du code TeX mais
plus de code expl3.

Je pense que nous avons discuté de celui-ci: je vais fermer mais bien sûr rouvrirai si demandé.

Cette page vous a été utile?
0 / 5 - 0 notes