Julia: Les règles de portée des variables globales conduisent à un comportement peu intuitif au niveau du REPL/notebook

Créé le 21 août 2018  ·  98Commentaires  ·  Source: JuliaLang/julia

Exemple 1

Cela est venu avec un étudiant qui est passé de 0.6 à 1.0 directement, donc n'a même jamais eu la chance de voir un avertissement de dépréciation, et encore moins de trouver une explication pour un nouveau comportement :

julia> beforefor = true
true

julia> for i in 1:2
         beforefor = false
       end

julia> beforefor  # this is surprising bit
true

julia> beforeif = true
true

julia> if 1 == 1
         beforeif = false
       end
false

julia> beforeif  # Another surprise!
false

julia> function foo()
         infunc = true
         for i in 1:10
           infunc = false
         end
         <strong i="7">@show</strong> infunc
       end
foo (generic function with 1 method)

julia> foo()  # "I don't get this"
infunc = false 

Exemple 2

julia> total_lines = 0
0

julia> list_of_files = ["a", "b", "c"]
3-element Array{String,1}:
 "a"
 "b"
 "c"

julia> for file in list_of_files
         # fake read file
         lines_in_file = 5
         total_lines += lines_in_file
       end
ERROR: UndefVarError: total_lines not defined
Stacktrace:
 [1] top-level scope at ./REPL[3]:4 [inlined]
 [2] top-level scope at ./none:0

julia> total_lines  # This crushs the students willingness to learn
0

Je "comprends" pourquoi cela se produit dans le sens où je pense pouvoir expliquer, en faisant suffisamment référence aux arcanes du manuel sur ce qui introduit les portées et ce qui ne le fait pas, mais je pense que cela est problématique pour une utilisation interactive.

Dans l'exemple un, vous obtenez un échec silencieux. Dans l'exemple deux, vous obtenez un message d'erreur très précis. C'est à peu près comparable à du code Python que j'ai écrit dans un cahier au travail aujourd'hui.

Je ne sais pas quelles sont les règles en Python, mais je sais qu'en général, vous ne pouvez pas affecter des choses à la portée globale sans invoquer global. Mais au REPL, cela fonctionne, probablement parce qu'au REPL, les règles sont différentes ou la même logique que si elles étaient toutes dans le cadre de la fonction est appliquée.

Je ne peux pas assez jurer les règles pour proposer le changement concret que je souhaite, et sur la base de Slack, ce n'est même pas nécessairement perçu comme un problème par certaines personnes, donc je ne sais pas où aller avec ça, sauf pour le signaler.

Références croisées :

19324

https://discourse.julialang.org/t/repl-and-for-loops-scope-behavior-change/13514
https://stackoverflow.com/questions/51930537/scope-of-variables-in-julia

REPL minor change

Commentaire le plus utile

@JeffBezanson , rappelez-vous que beaucoup d'entre nous aimeraient utiliser Julia comme substitut de Matlab, etc. dans des cours techniques comme l'algèbre linéaire et les statistiques. Ce ne sont entièrement interactif avec de courts extraits et des variables globales.

De plus, la raison pour laquelle j'utilise un langage dynamique en premier lieu est de basculer de manière fluide entre l'exploration interactive et une programmation plus disciplinée. L'impossibilité d'utiliser le même code dans un contexte global et fonctionnel est un obstacle à cette fin, même pour quelqu'un qui a l'habitude de définir des concepts, et c'est bien pire pour les étudiants n'ayant pas de connaissances en informatique.

Tous les 98 commentaires

(Par @mlubin , c'est le changement pertinent https://github.com/JuliaLang/julia/pull/19324)

Stefan a suggéré ici qu'une possibilité de résoudre ce problème est l'encapsulation automatique des entrées REPL dans des blocs let

Mais ne serait-ce pas déroutant dans la mesure où tu ne pourrais pas faire

a = 1

et utiliser a après cela ? À moins que global soit inséré pour toutes les affectations de niveau supérieur, je suppose ?

Le comportement ne consisterait pas simplement à tout envelopper dans un bloc let —c'est plus compliqué que cela. Vous devez lier tout global affecté à l'intérieur de l'expression, puis extraire la valeur liée à un global à la fin de l'expression.

Donc, vous transformeriez a = 1 en quelque chose comme a = let a; a = 1; end . Et quelque chose comme

for i in 1:2
    before = false
end

serait transformé en ceci:

before = let before = before
    for i in 1:2
        before = false
    end
end

Franchement, je suis assez ennuyé que les gens ne donnent ce retour que maintenant. Cela a changé a été sur maître pendant dix mois.

Je suis coupable de ne pas avoir suivi de maître très fermé jusqu'à récemment, donc ce retour est effectivement un peu tardif. Plus qu'une préoccupation pour les programmeurs (la plupart des boucles for seront à l'intérieur d'une fonction dans le code de la bibliothèque), j'ai bien peur que ce soit une préoccupation pour l'enseignement. Souvent, les boucles for sont enseignées avant les fonctions ou les portées (bien sûr, vous devez comprendre les portées pour vraiment comprendre ce qui se passe, mais dans l'enseignement, les choses sont souvent simplifiées).

Ici, il devient un peu difficile d'enseigner à un débutant comment additionner des nombres de 1 à 10 sans expliquer les fonctions ou les variables globales.

Franchement, je suis assez ennuyé que les gens ne donnent ce retour que maintenant. Cela a changé a été sur maître pendant dix mois.

Pour être juste, Julia 0.7 est sortie il y a 13 jours. Il s'agit d'un nouveau changement pour la plupart des utilisateurs de Julia.

Franchement, je suis assez ennuyé que les gens ne donnent ce retour que maintenant. Cela a changé a été sur le maître pendant dix mois

Malheureusement pour ceux d'entre nous qui ne peuvent pas supporter de vivre à la limite, c'est tout nouveau de notre point de vue.

Franchement, je suis assez ennuyé que les gens ne donnent ce retour que maintenant. Cela a changé a été sur maître pendant dix mois.

Et pour ceux d'entre nous qui ont été encouragés à rester en dehors des branches de développement, "c'est tout nouveau de notre point de vue".

Pouvons-nous s'il vous plaît revenir en arrière pour nous concentrer sur le problème actuel, au lieu d'avoir une méta-discussion sur le temps que les gens ont eu pour tester cela. C'est ce qu'il est en ce moment, alors regardons vers l'avenir.

Je suis coupable de ne pas avoir suivi de maître très fermé jusqu'à récemment, donc ce retour est effectivement un peu tardif. Plus qu'une préoccupation pour les programmeurs (la plupart des boucles for seront à l'intérieur d'une fonction dans le code de la bibliothèque), je crains que ce ne soit une préoccupation pour l'enseignement. Souvent, les boucles for sont enseignées avant les fonctions ou les portées (bien sûr, vous devez comprendre les portées pour vraiment comprendre ce qui se passe, mais dans l'enseignement, les choses sont souvent simplifiées).

Ici, il devient un peu difficile d'enseigner à un débutant comment additionner des nombres de 1 à 10 sans expliquer les fonctions ou les variables globales.

C'est un gros point. Après avoir découvert quel est vraiment le problème, il est surprenant de constater à quel point il apparaît peu. C'est moins un problème avec beaucoup de code Julia dans la nature et dans les tests, et cela a révélé beaucoup de variables qui étaient accidentellement globales (dans les deux tests de Julia Base selon le PR d'origine, et j'ai remarqué cela sur la plupart des tests de DiffEq). Dans la plupart des cas, il semble que le comportement subtilement faux n'est pas ce que vous obtenez (en espérant un changement dans une boucle), mais plutôt en espérant pouvoir utiliser une variable dans une boucle est ce que j'ai trouvé être la grande majorité des où cela apparaît dans la mise à jour des scripts de test vers la v1.0. Donc, la bonne chose est que dans la plupart des cas, une erreur est présentée à l'utilisateur, et ce n'est pas difficile à corriger.

La mauvaise chose est que c'est un peu verbeux d'avoir à mettre global x intérieur des boucles, et maintenant votre code REPL est également différent du code de la fonction. Qu'il s'agisse ou non d'un comportement plus intuitif qu'auparavant est une opinion difficile, car il y avait certainement des cas limites dans la portée locale dure/douce et c'est donc clairement plus facile à expliquer. Mais en même temps, tout en ayant une explication beaucoup plus succincte que le comportement d'avant, il est maintenant plus facile d'atteindre les cas limites où la compréhension des règles de portée est importante. 🤷‍♂️.

Pour ma part, j'aimerais voir les expériences avec le blocage let . Cela conserverait l'aspect "vous ne vouliez pas vraiment autant de globals", ainsi que l'explication de portée simplifiée, tout en faisant en sorte que le code REPL se comporte comme des intérieurs de fonction (ce qui est apparemment ce que nous avons toujours voulu). Ou inversement, obliger les gens à spécifier des variables qu'ils veulent agir comme des globales

global x = 5
for i = 1:5
  println(x+i)
end

pourrait être un bon moyen de garder l'explicite, et rendrait le "code REPL lent à cause des globales" beaucoup plus évident. L'inconvénient est qu'une fois de plus, lancer des choses dans une fonction ne nécessiterait pas les marqueurs global .

Mais étant donné la façon dont cela a tendance à se manifester, ce n'est pas vraiment révolutionnaire ou un obstacle. Je la classerais comme une verrue qui devrait être mentionnée dans n'importe quel atelier, mais ce n'est pas comme si la v1.0 était inutilisable à cause de cela. J'espère que la modification de ce comportement n'est pas considérée comme une rupture et nécessite cependant la v2.0.

Je ne suis pas sûr d'aimer l'idée que le REPL se comporte comme un intérieur de fonction. Ce n'est clairement pas le cas, donc je m'attends à ce qu'il se comporte comme une portée globale. Pour moi, le REPL ne se comportant pas comme une portée globale serait potentiellement encore plus déroutant que l'écart qui cause ce problème.

Quoi qu'il en soit, à tout le moins, je pense que la documentation devrait être un peu plus explicite sur cette question. En lisant les documents avec désinvolture, j'aurais supposé que vous auriez besoin d'utiliser le mot-clé local pour que le comportement se produise par défaut dans la portée globale.

Pour ma part, j'aimerais voir les expériences avec le blocage let . Cela conserverait l'aspect "vous ne vouliez pas vraiment autant de globals", ainsi que l'explication de portée simplifiée, tout en faisant en sorte que le code REPL se comporte comme des intérieurs de fonction (ce qui est apparemment ce que nous avons toujours voulu)

Si nous recherchons "REPL est identique à l'intérieur d'une fonction", nous devrions également penser à outer :

julia> i = 1
1

julia> for outer i = 1:10
       end
ERROR: syntax: no outer variable declaration exists for "for outer"

contre:

julia> function f()
          i = 0
          for outer i = 1:10
          end
          return i
       end
f (generic function with 1 method)

julia> f()
10

Franchement, je suis assez ennuyé que les gens ne donnent ce retour que maintenant. Cela a changé a été sur maître pendant dix mois.

Les gens n'ont pas utilisé master pour une utilisation interactive ou pour l'enseignement, ils l'ont utilisé pour mettre à niveau des packages, qui ne sont que très peu affectés par cela et sont principalement écrits par des programmeurs expérimentés.

(J'étais l'une des rares personnes à avoir fait des commentaires dans #19324, cependant, où j'ai plaidé pour l'ancien comportement .)

Un moyen infaillible de s'en sortir serait de revenir à l'ancien comportement (idéalement pas en insérant des blocs implicites let ou quoi que ce soit - il suffit de restaurer l'ancien code dans julia-syntax.scm en option) dans le REPL. Ou plutôt, pour le rendre disponible dans des environnements comme IJulia qui pourraient le vouloir, ajoutez un indicateur soft_global_scope=false à include , include_string , et Core.eval pour restaurer le ancien comportement.

(J'étais l'une des rares personnes à avoir fait des commentaires dans #19324, cependant, où j'ai plaidé pour l'ancien comportement.)

Oui, et je l'apprécie beaucoup. Peu importe maintenant puisque nous avons fait le choix, l'avons laissé cuire pendant dix mois et l'avons maintenant publié avec un engagement à long terme pour la stabilité. Donc, la seule chose à faire maintenant est de se concentrer sur ce qu'il faut faire à l'avenir.

Avoir la possibilité de choisir entre l'ancien comportement et le nouveau est intéressant mais cela semble très difficile. Cela signifie que non seulement nous avons parfois un comportement de cadrage que tout le monde a apparemment trouvé incroyablement déroutant, mais nous ne l'avons pas toujours et que nous l'ayons ou non dépend d'un indicateur global. C'est assez insatisfaisant, j'en ai peur.

Avoir la possibilité de choisir entre l'ancien comportement et le nouveau est intéressant mais cela semble très difficile.

Si quelqu'un implémente une transformation AST à portée logicielle "unbreak me", il sera très tentant de l'utiliser dans IJulia, OhMyREPL, etc., auquel cas vous obtenez la situation encore plus problématique dans laquelle le REPL par défaut est considéré comme cassé.

Ce n'est pas ce que je dis. Il est clair que nous devrions utiliser la même solution dans tous ces contextes. Mais l'implémenter sous la forme de deux variantes différentes des règles de portée semble moins propre que de l'implémenter en tant que transformation de code avec un seul ensemble de règles de portée. Mais peut-être que ceux-ci sont fonctionnellement équivalents. Cependant, cela semble plus facile à expliquer en termes de nouvelles règles de portée plus simples + une transformation qui prend une entrée de style REPL et la transforme avant de l'évaluer.

Cela pourrait être fait en tant que Meta.globalize(m::Module, expr::Expr) qui transforme une expression en annotant automatiquement toutes les variables globales qui existent dans le module comme globales si elles sont affectées à l'intérieur d'une portée de non-fonction de niveau supérieur. Bien sûr, je pense que c'est équivalent à ce que faisait l'ancien analyseur, mais un peu plus transparent puisque vous pouvez appeler vous-même Meta.globalize et voir ce que le REPL évaluera.

Cela pourrait être fait en tant que Meta.globalize(m::Module, expr::Expr) qui transforme une expression en annotant automatiquement toutes les variables globales qui existent dans le module comme globales si elles sont affectées à l'intérieur d'une portée de non-fonction de niveau supérieur.

En fait, j'ai commencé à chercher à mettre en œuvre quelque chose comme ça il y a quelques minutes. Cependant, il semble que ce serait beaucoup plus facile à implémenter en option dans julia-syntax.jl :

  • L'écriture d'une transformation AST externe est possible, mais il semble qu'il y ait beaucoup de cas difficiles — vous devez essentiellement ré-implémenter les règles de portée — alors que nous avions déjà le code pour bien faire les choses dans julia-syntax.scm .
  • C'est encore plus délicat pour quelque chose comme IJulia qui utilise actuellement include_string pour évaluer tout un bloc de code et obtenir la valeur de la dernière expression. Non seulement nous devrions passer à l'analyse d'expression par expression, mais un peu de piratage peut être nécessaire afin de préserver les numéros de ligne d'origine (pour les messages d'erreur, etc.). (Bien que j'ai trouvé un hack pour ChangePrecision.jl pour ce genre de chose qui peut aussi fonctionner ici.)
  • Sans parler du cas des personnes qui include fichiers externes, qui ne seraient pas attrapés par votre transformation AST.

Cependant, cela semble plus facile à expliquer en termes de nouvelles règles de portée plus simples + une transformation qui prend une entrée de style REPL et la transforme avant de l'évaluer.

Je doute sérieusement que ce soit plus facile à expliquer aux nouveaux utilisateurs que de simplement dire que les règles sont moins pointilleuses pour une utilisation interactive ou pour include avec un certain drapeau.

Voici un brouillon d'une implémentation de globalize(::Module, ast) : https://gist.github.com/stevengj/255cb778efcc72a84dbf97ecbbf221fe

D'accord, j'ai compris comment implémenter une fonction globalize_include_string qui préserve les informations de numéro de ligne, et je l'ai ajoutée à mon résumé .

Une voie à suivre possible (sans rupture), si les gens aiment cette approche :

  1. Publiez un package SoftGlobalScope.jl avec les fonctions globalize etc.
  2. Utilisez SoftGlobalScope dans IJulia (et éventuellement Juno, vscode et OhMyREPL).
  3. Intégrez les fonctions SoftGlobalScope dans une future version du package REPL stdlib et utilisez-les dans le REPL.

Ou est-il pratique de le déployer immédiatement dans REPL.jl ? Je ne suis pas tout à fait clair sur le fonctionnement des mises à jour stdlib dans 1.0.

Veuillez jeter un œil à mon implémentation, au cas où il me manquerait quelque chose qui le rendrait fragile.

Ne pouvons-nous pas l'avoir comme fonctionnalité non par défaut du REPL en 1.1 ?

Duplicata des #28523 et #28750. À ceux qui disent qu'ils ne veulent pas enseigner aux gens les variables globales, je suggère d'abord d'enseigner les fonctions, avant les boucles for . Les fonctions sont de toute façon plus fondamentales, et cela aidera à définir l'attente que le code doit être écrit dans des fonctions. Bien que je comprenne l'inconvénient, ce comportement de cadrage peut être transformé en un avantage pédagogique : "En fait, les variables globales sont une si mauvaise idée, en particulier en les utilisant dans des boucles, que le langage vous fait vous plier en quatre pour les utiliser."

L'ajout d'une fonctionnalité non par défaut au REPL pour cela me semble cependant correct.

@JeffBezanson , rappelez-vous que beaucoup d'entre nous aimeraient utiliser Julia comme substitut de Matlab, etc. dans des cours techniques comme l'algèbre linéaire et les statistiques. Ce ne sont entièrement interactif avec de courts extraits et des variables globales.

De plus, la raison pour laquelle j'utilise un langage dynamique en premier lieu est de basculer de manière fluide entre l'exploration interactive et une programmation plus disciplinée. L'impossibilité d'utiliser le même code dans un contexte global et fonctionnel est un obstacle à cette fin, même pour quelqu'un qui a l'habitude de définir des concepts, et c'est bien pire pour les étudiants n'ayant pas de connaissances en informatique.

rappelez-vous que beaucoup d'entre nous aimeraient utiliser Julia comme substitut de Matlab, etc. dans des cours techniques comme l'algèbre linéaire et les statistiques. Ce ne sont pas des cours de programmation et les étudiants n'ont souvent aucune formation en programmation. Nous ne faisons jamais de programmation structurée — c'est presque entièrement interactif avec de courts extraits et des variables globales.

Beaucoup d'entre nous, utilisateurs de Julia, n'ont absolument aucune expérience en CS (y compris moi-même), mais il me semble que la bonne attitude (en particulier pour les étudiants) est une volonté d'apprendre plutôt que d'exiger que les choses soient changées pour le pire pour s'adapter à notre naïveté.

Maintenant, je ne dis pas nécessairement que ce changement particulier serait pour le pire que je n'ai qu'une compréhension limitée de ce qui se passe ici, mais si c'est le cas que c'est une complication importante ou rend trop facile d'écrire inutilement code peu performant, il ne semble pas utile de faire un changement afin d'avoir un meilleur exemple de cours. Vous ne pouvez pas changer les lois de la physique pour que les exemples d'électrostatique que vous montrez aux étudiants de première année soient plus applicables à la vie réelle.

Donc, ma question en tant qu'utilisateur non-CS qui se soucie également des performances est de savoir comment je serais susceptible de rater si cela devenait le comportement par défaut. Est-ce littéralement le genre d'exemples que nous voyons ici qui pose problème (dont j'étais déjà au courant), ou est-ce que nous sommes susceptibles de souvent gâcher cela de manière plus subtile ?

Pour ce que ça vaut, je suis d'accord pour dire que le fait que le code se comporte différemment en fonction de sa portée englobante est une caractéristique généralement indésirable.

Rendre le code plus difficile à écrire de manière interactive, obliger les débutants à écrire leurs premières boucles à comprendre des règles de portée obscures et faire en sorte que le code collé à partir de fonctions ne fonctionne pas dans les portées globales n'aide pas les programmeurs à écrire du code rapide dans les fonctions. Cela rend simplement plus difficile l'utilisation de Julia de manière interactive et plus difficile pour les débutants.

Ne pouvons-nous pas l'avoir comme fonctionnalité non par défaut du REPL en 1.1 ?

Faire d'une option "unbreak me" la valeur par défaut semble plus sage, en particulier une option qui s'adresse directement aux utilisateurs débutants. S'il ne s'agit pas d'une option par défaut, alors précisément les personnes qui en ont le plus besoin seront celles qui ne l'auront pas activée (et ne savent pas qu'elle existe).

Que ferait le mode REPL proposé aux scripts include ed ? L'évaluation des déclarations globales dépendrait-elle de l'activation ou non du mode REPL ? Si tel est le cas, l'OMI serait en contradiction avec la promesse de stabilité 1.0.

Si nous faisions quelque chose comme ça, il semblerait qu'il soit logique que le module détermine comment cela fonctionne. Ainsi, Main serait un module "soft scope" alors que par défaut les autres modules seraient des modules "hard scope".

J'étais intéressé de voir s'il était possible de patcher le REPL pour utiliser la fonction globalize @stevengj et il semble que ce soit sans trop d'effort (bien qu'assez bidon). Voir l' essentiel . Cela ne fonctionne pas avec Juno (ou tout autre élément qui appelle directement Core.eval ).

Je ne vais

julia> a = 0                                                                
0                                                                           

julia> for i = 1:10                                                         
         a += i                                                             
       end                                                                  
ERROR: UndefVarError: a not defined                                         
Stacktrace:                                                                 
 [1] top-level scope at .\REPL[2]:2 [inlined]                               
 [2] top-level scope at .\none:0                                            

julia> using SoftGlobalScope                                                
[ Info: Precompiling SoftGlobalScope [363c7d7e-a618-11e8-01c4-4f22c151e122] 

julia> for i = 1:10                                                         
         a += i                                                             
       end                                                                  

julia> a                                                                    
55                                                                          

(BTW : ce qui précède est à peu près autant de tests qu'il en a eu !)

Que ferait le mode REPL proposé aux scripts inclus ?

Rien. Fondamentalement, la proposition est que cela ne concernerait que le code entré lors d'une invite interactive. Dès que vous commencez à mettre des choses dans des fichiers, vous devez apprendre les règles de la « portée stricte ». Heureusement, lorsque vous commencez à mettre du code dans des fichiers, vous devriez commencer à utiliser des fonctions.

Il n'est pas idéal qu'il y ait des règles de portée plus sélectives pour le code global dans les fichiers qu'à l'invite. Mais je pense que #19324 combiné à la promesse de stabilité Julia 1.0 ne nous laisse aucune option idéale.

@stevengj :

@JeffBezanson , rappelez-vous que beaucoup d'entre nous aimeraient utiliser Julia comme substitut de Matlab, etc. dans des cours techniques comme l'algèbre linéaire et les statistiques. Ce ne sont pas des cours de programmation et les étudiants n'ont souvent aucune formation en programmation. Nous ne faisons jamais de programmation structurée — c'est presque entièrement interactif avec de courts extraits et des variables globales.

Ayant enseigné des cours utilisant Julia à des étudiants ayant déjà été exposés à Matlab/R/..., je sympathise avec cette préoccupation. Mais en même temps, je ne pense pas qu'utiliser Julia comme substitut de Matlab, etc. impliquant peut-être un coût encore plus important que d'investir pour comprendre en quoi Julia est différente de ces autres langues (cf.

Je pense que la question clé est l'échec silencieux ; le problème en soi est facile à comprendre et à résoudre. Je suggérerais de conserver le nouveau comportement, mais en donnant un avertissement dans Main (par défaut, il devrait être possible de le désactiver).

Pour moi, le plus gros problème est l'incohérence perçue. C'est-à-dire que j'accepte que Julia fasse les choses différemment, mais :

  • Pourquoi le code collé à partir d'une fonction ne devrait-il pas fonctionner dans un REPL ?
  • Aucune autre langue que j'ai jamais utilisée n'a ce comportement, et c'est un autre obstacle à l'adoption
  • Pourquoi les blocs for se comportent-ils différemment des blocs begin et if ? ( if Je comprends en quelque sorte, mais un bloc est [devrait être] un bloc.).

En ce qui concerne la puce 2, je pense que c'est une affaire plus importante que nous qui utilisons Julia depuis un certain temps (et qui sommes attachés à la langue) pourrions comprendre. Je peux vous dire que je suis actuellement 0 sur 7 pour convaincre mon groupe d'écrire du code en Julia ; deux d'entre eux étaient dus à ce problème de boucle for que je ne pouvais pas expliquer car je n'y avais jamais été exposé auparavant. Le reste je suppose que nous pouvons attribuer à mon manque de charisme.

Ma préférence serait de m'assurer que le code collé d'une fonction dans un REPL se comporte de manière identique à la fonction, et que les boucles for fassent la chose attendue lorsqu'elles les utilisent pour analyser des données de manière interactive ; c'est-à-dire, en particulier, qu'ils mutent les variables externes/globales lorsqu'ils sont dirigés sans aucun mot-clé spécial.

Je ne pense pas qu'utiliser Julia comme substitut de Matlab, etc. que d'investir pour comprendre en quoi Julia est différente de ces autres langues.

Désolé, mais cet argument est ridicule pour moi. Je ne parle pas des cours où j'enseigne la programmation. Il y a une place pour des calculs interactifs simples, et dans les classes non-CS, il est courant d'être initié aux langages de programmation en tant que "calculatrice glorifiée" pour commencer. Enseigner le calcul de performance à Julia est un processus entièrement différent, mais cela ne fait pas de mal s'ils ont déjà utilisé Julia comme "calculatrice".

Si vous commencez par présenter Matlab aux étudiants comme leur "calculatrice", il est beaucoup plus difficile de faire la transition vers la "vraie" programmation, car leur premier réflexe est d'en faire autant que possible avec Matlab avant de quitter le navire, à quel point leurs mauvaises habitudes sont ancrés et hésitent à apprendre une nouvelle langue. En revanche, si vous commencez avec Julia comme calculatrice glorifiée, lorsque vient le temps de faire une programmation plus sérieuse, vous disposez d'un éventail d'options beaucoup plus large. Vous n'avez pas besoin de les entraîner à tout mettre dans des opérations « vectorielles » ou de les forcer à mal faire les choses avant de les faire correctement.

Êtes-vous en train de dire que je ne devrais pas utiliser Julia dans mon cours d'algèbre linéaire ? Ou que je ne devrais l'utiliser que si je suis prêt à enseigner l'informatique ainsi que l'algèbre linéaire ?

Je suis d'accord avec @stevengj à la fois sur le problème (l'enseignement aux non-programmeurs devient beaucoup plus difficile) et sur la solution (faire fonctionner les choses dans le REPL et les différents IDE). L'inclusion d'un script aurait toujours les règles de portée de Julia 1.0 mais c'est moins préoccupant, il faut juste faire attention à avoir la classe "nous pouvons mettre notre boucle for dans une fonction puis appeler la fonction" avant la classe "nous pouvons mettre notre boucle for dans un fichier et inclure la classe file".

Cela semble être un bon compromis car le débogage interactif au REPL ne devient pas plus pénible qu'il ne devrait l'être (ou plus déroutant pour les nouveaux utilisateurs), tandis que le code normal dans les scripts doit suivre des règles de portée strictes et est à l'abri des bogues qui écrasent certains variables accidentellement.

Êtes-vous en train de dire que je ne devrais pas utiliser Julia dans mon cours d'algèbre linéaire ? Ou que je ne devrais l'utiliser que si je suis prêt à enseigner l'informatique ainsi que l'algèbre linéaire ?

Vous avez peut-être mal compris ce que je disais (ou je ne l'ai pas exprimé clairement). Je parlais de cours qui utilisent Julia pour enseigner quelque chose de spécifique à un domaine (par exemple, j'ai enseigné des méthodes numériques à des étudiants en économie), pas des cours d'informatique (avec lesquels je n'ai aucune expérience).

Le point que j'essayais de faire est qu'il est raisonnable de s'attendre à un certain niveau de différence entre Julia et le langage X (qui peut être Matlab) ; à l'inverse, ignorer cela peut (et conduit) à des problèmes.

Personnellement, lors de l'apprentissage d'une nouvelle langue, je préfère faire face à ces problèmes dès le début ; aussi, je pense que la simplicité et la cohérence de la sémantique du langage sont plus importantes que la similitude avec d'autres langages à long terme. Mais je reconnais ces préférences comme subjectives, et les personnes raisonnables peuvent en avoir différentes.

J'ai créé le package (non enregistré) https://github.com/stevengj/SoftGlobalScope.jl

Si cela semble raisonnable, je peux aller de l'avant et enregistrer le package, puis l'utiliser par défaut dans IJulia (et peut-être soumettre des PR à Juno, etc.).

Le point que j'essayais de faire est qu'il est raisonnable de s'attendre à un certain niveau de différence entre Julia et le langage X (qui peut être Matlab)

Évidemment. Quand je dis "utiliser Julia au lieu de Matlab", je ne veux pas dire que j'essaie de leur enseigner la syntaxe Matlab dans Julia, ni que je cible spécifiquement les anciens utilisateurs de Matlab.

Je préfère affronter ces problèmes dès le début

Il ne s'agit pas de différences par rapport à Matlab en soi. Je préférerais vraiment ne pas parler de portée globale vs locale et de l'utilité d'un mot-clé global pour l'analyse statique la première fois que j'écris une boucle devant des étudiants non-CS, ou la première fois qu'ils collent du code à partir d'un fonction dans le REPL pour l'essayer de manière interactive. Je préfère me concentrer sur les mathématiques que j'essaie d'exprimer avec la boucle.

Personne ici ne plaide pour une portée interactive douce simplement parce que c'est ce à quoi s'attendent les utilisateurs de Matlab. digressions dans le concept peu familier de "portée" vont certainement faire dérailler toute conférence non-CS où vous montrez une boucle pour la première fois. (Et même pour les utilisateurs expérimentés, il est plutôt gênant d'être obligé d'ajouter des mots-clés global lorsque nous travaillons de manière interactive.)

Un autre correctif non mentionné ici consiste simplement à arrêter de faire en sorte que 'for' définisse un bloc de portée (juste fonction et let créerait une nouvelle portée)

@vtjnash , je préfère concentrer cette discussion sur les choses que nous pouvons faire avant Julia 2.0. Je suis d'accord que le fait que le mode interactif se comporte différemment n'est qu'un pis-aller, et nous devrions sérieusement envisager de changer les règles de portée dans quelques années.

Bon point, cela a aussi besoin de import Future.scope 😀

(Je pense que ce module/espace de noms/effet comportemental est déjà réservé/existe)

Pour rappel ici, le changement visait à garantir que le code se comporte de la même manière dans tous les environnements de portée globale, indépendamment de ce qui avait été précédemment évalué dans ce module. Avant ce changement, vous pouviez obtenir des réponses complètement différentes (résultant d'une affectation de portée différente) simplement en exécutant le même code deux fois ou en le déplaçant dans un fichier.

Avant ce changement, vous pouviez obtenir des réponses complètement différentes (résultant d'une affectation de portée différente) simplement en exécutant le même code deux fois ou en le déplaçant dans un fichier.

Le nombre de plaintes que j'ai vues à ce sujet dans la pratique (zéro) sera certainement éclipsé par le nombre de plaintes et de confusion que vous verrez (et que vous voyez déjà) à propos du comportement actuel.

Avant ce changement, vous pouviez obtenir des réponses complètement différentes (résultant d'une affectation de portée différente) simplement en exécutant le même code deux fois

Voulez-vous dire que dans le code ci-dessous, a change entre la première et la deuxième boucle for ? Dans mon esprit, c'est un comportement attendu, pas un bug.

a = 0
for i = 1:5
  a += 1
end

for i = 1:5
  a += 1
end

Que ferait le mode REPL proposé aux scripts inclus ?

@mauro3 @stevengj Je suppose que l'ajout d'une fonction (disons, exec("path/to/script.jl") ) peut être fait avec juste une version mineure ? Nous pouvons également avertir exec 'ing un autre fichier du script exec 'ed, puis y mettre des messages pédagogiques pour les pousser à utiliser include .

Certaines réflexions que j'ai écrites hier soir en essayant de comprendre ce problème (encore une fois) pour essayer de déterminer quelle pourrait être la meilleure ligne de conduite. Pas de conclusion, mais je pense que cela pose le problème assez clairement. Après avoir réfléchi à ce problème pendant quelques années, je ne pense pas qu'il existe de "solution idéale" - cela peut être l'un de ces problèmes où il n'y a que des choix sous-optimaux.


Les gens voient naïvement la portée globale comme un genre amusant englobant la portée locale. C'est pourquoi les étendues globales fonctionnaient comme elles le faisaient dans Julia 0.6 et les versions antérieures :

  • Si une portée locale externe crée une variable locale et qu'une portée locale interne lui est affectée, alors cette affectation met à jour la variable locale externe.
  • Si une portée globale externe crée une variable globale et qu'une portée locale interne lui est affectée, alors cette affectation a précédemment mis à jour la variable globale externe.

La principale différence, cependant, est :

  • L'existence d'une variable locale externe, par conception, ne dépend pas de l'ordre d'apparition ou d'exécution des expressions dans la portée locale externe.
  • Cependant, l'existence d'une variable globale ne peut pas être indépendante de l'ordre, car on évalue les expressions dans la portée globale, une à la fois.

De plus, étant donné que les portées globales sont souvent assez longues (il n'est pas rare qu'elles soient réparties sur plusieurs fichiers), le fait que la signification d'une expression dépend d'autres expressions à une distance arbitraire de celle-ci constitue un effet « effrayant à distance » et, en tant que tel, tout à fait indésirable .


Cette dernière observation montre pourquoi le comportement différent des deux versions différentes d'une boucle for à portée globale est problématique :

# file1.jl
for i = 1:5
  a += 1
end
# file2.jl
a = 1



md5-f03fb9fa19e36e95f6b80b96bac9811e



```jl
# main.jl
include("file1.jl")
include("file2.jl")
include("file3.jl")

Notez également que les contenus de file1.jl et file3.jl sont identiques et nous pourrions simplifier l'exemple en incluant deux fois le même fichier avec une signification et un comportement différents à chaque fois.

Un autre cas problématique est une session REPL de longue durée. Essayez un exemple quelque part en ligne ? Il échoue parce que vous avez une variable globale du même nom que l'exemple utilise pour une variable locale dans une boucle for ou une construction similaire. Donc, l'idée que le nouveau comportement est le seul qui peut causer de la confusion n'est certainement pas exacte. Je suis d'accord que le nouveau comportement est un problème d'utilisabilité dans le REPL mais je veux juste tempérer la conversation et présenter clairement l'autre côté ici.

Ma petite suggestion, qui ne traite pas du problème de repl, mais serait utile à des fins didactiques lors de l'enseignement du langage non-interactif, au moins : définir un bloc principal nommé "programme", comme on peut le faire en fortran (c'est le même chose que le "let...end" ci-dessus, juste avec une notation plus naturelle):

essai de programme
...
finir

on pourrait enseigner la langue sans entrer dans les détails de la portée et seulement éventuellement discuter de ce point.

Un autre cas problématique est une session REPL de longue durée. Essayez un exemple quelque part en ligne ? Il échoue parce que vous avez une variable globale du même nom que l'exemple utilise pour une variable locale dans une boucle for ou une construction similaire.

Combien de plaintes de listes de diffusion et de problèmes de github ont été déposées à ce sujet par des utilisateurs mécontents ? Zéro, à mon avis. Pourquoi? Probablement parce que ce comportement n'est fondamentalement pas surprenant pour les gens - si vous travaillez dans une portée mondiale, vous dépendez de l'état mondial.

Donc, l'idée que le nouveau comportement est le seul qui peut causer de la confusion n'est certainement pas exacte.

Je pense que c'est une fausse équivalence - il y a une grande disparité dans le niveau de confusion potentielle ici. Dans Julia 0.6, je pourrais expliquer votre exemple à un étudiant en quelques secondes : "Oh, voyez cette boucle dépend de a , que vous avez modifié ici." Dans Julia 1.0, je m'inquiète honnêtement de ce que je ferai si je suis au milieu d'un cours d'algèbre linéaire et que je dois mystérieusement taper un mot-clé global devant des étudiants qui n'ont jamais entendu le mot "portée" au sens CS.

nous devrions sérieusement envisager de changer les règles de cadrage dans quelques années.

Absolument pas. Voulez-vous sérieusement revenir au monde pré-v0.2 (voir #1571 et #330) de la portée de la boucle ?

En fait, nous n'avons jamais complètement pris en charge la copie et le collage de code d'une fonction ligne par ligne dans le REPL. Nous pouvons donc voir cela comme une opportunité pour que cela fonctionne. Plus précisément, alors que cela " fonctionnait " pour les boucles for , cela ne fonctionnait pas pour les fonctions internes :

x = 0
f(y) = (x=y)

À l'intérieur d'une fonction, f va muter le x de la première ligne. Dans le REPL, ce ne sera pas le cas. Mais avec une transformation comme celle-ci dans SoftGlobalScope.jl, cela pourrait fonctionner. Bien sûr, nous ne voudrions probablement pas que cela soit activé par défaut, car coller des définitions de fonctions autonomes ne fonctionnerait pas. La première chose qui vient à l'esprit est un mode REPL pour le débogage des fonctions ligne par ligne.

Voulez-vous sérieusement revenir au monde pré-v0.2

Non, je veux retourner dans le monde 0.6. ??

Je suppose que je répondais plus à :

Un autre correctif non mentionné ici consiste simplement à arrêter de faire 'for' définir un bloc de portée

En fait, nous n'avons jamais complètement pris en charge la copie et le collage de code d'une fonction ligne par ligne dans le REPL. Nous pouvons donc voir cela comme une opportunité pour que cela fonctionne.

J'apprécie beaucoup ce sentiment et pour mes cas d'utilisation, cela m'aiderait vraiment. De mon point de vue, il s'agit vraiment de rendre le REPL aussi utile que possible plutôt que de changer directement les règles de portée du langage.

Cela dit, plus je pense à ce problème, plus je vois les points de vue contradictoires que j'ai (personnellement) sur ce que le REPL devrait faire.

Pour être concret, j'aimerais beaucoup que le REPL corresponde aux règles de portée d'un corps de fonction ; c'est-à-dire que les variables sont locales plutôt que globales et vous pouvez simplement copier-coller du code directement à partir d'une fonction et savoir que cela fonctionnera. J'imagine qu'une implémentation naïve serait quelque chose comme le let-block wrapping (comme cela a été mentionné précédemment) de la forme

julia> b = a + 1

être transformé en

let a = _vars[:a]::Float64 # extract the variables used from the backing store
    # Code from the REPL
    b = a + 1
    # Save assigned variables back to the backing store
   _vars[:b] = b
end

Fait correctement (c'est-à-dire par quelqu'un qui sait ce qu'il fait), j'imagine que cela aurait un certain nombre d'avantages par rapport au REPL existant. 1. les workflows précédents avec analyse/calcul interactif des données fonctionnent tout simplement. 2. beaucoup moins de messages sur Discourse où la réponse de base est "arrêtez l'analyse comparative avec des variables globales" - tout serait local et, espérons-le, rapide ! :) 3. copier-coller vers/depuis un corps de fonction fonctionne comme prévu. 4. une fonction similaire à workspace() est triviale si le magasin de sauvegarde est une sorte de Dict ; il suffit de l'effacer. 5. les globals deviennent explicites - les choses sont locales à moins que vous ne demandiez spécifiquement qu'elles soient globales ; c'est un gros avantage de mon point de vue, je n'aime pas créer implicitement des globals. Un dernier point très mineur (et j'hésite à ajouter ceci!), Cela correspondrait au comportement de Matlab, ce qui faciliterait la transition des personnes - au Matlab REPL, toutes les variables semblent être locales à moins qu'elles ne soient explicitement annotées comme globales.

Jusqu'à il y a quelques heures, cette histoire me parait bien. Mais après le commentaire de Jeff sur les fonctions, j'ai pensé à coller des définitions de fonctions autonomes et à la façon dont cette approche empêcherait cela puisque les définitions de fonctions devraient être dans la portée globale (du moins, c'est probablement ce qui est prévu); mais alors et s'ils étaient destinés à entrer dans la portée locale (une fonction interne) ? Il n'y a aucune information pour lever l'ambiguïté des deux possibilités. Il semblerait que deux modes REPL soient nécessaires, un avec une portée locale et un avec une portée globale. D'un côté cela peut être très déroutant (imaginez les posts Discourse...) mais de l'autre cela peut être extrêmement utile. (Avoir les deux modes REPL serait également ininterrompu puisque vous introduisez simplement de nouvelles fonctionnalités :) )

Opter pour la maison de transition de SoftGlobalScope.jl pourrait finir par être le compromis le moins déroutant, mais mon inquiétude est que ce n'est qu'un autre ensemble de règles à retenir (quelles choses fonctionnent dans le REPL mais pas dans mon corps de fonction/portée globale et vice versa).

Je m'excuse pour le long message, mais je pense que c'est important pour la convivialité (et cela m'a aidé à y réfléchir !).

Combien de plaintes de listes de diffusion et de problèmes de github ont été déposées à ce sujet par des utilisateurs mécontents ? Zéro, à mon avis. Pourquoi? Probablement parce que ce comportement n'est fondamentalement pas surprenant pour les gens - si vous travaillez dans une portée mondiale, vous dépendez de l'état mondial.

Hmm, avez-vous vraiment fait une étude systématique de cela? J'ai dû rater ça. Néanmoins, cela ne signifie pas que ce comportement n'est pas une source de bogues ou de résultats inattendus ; juste qu'une fois que l'utilisateur l'a compris, il a été reconnu comme un comportement correct et n'a donc pas suscité de problème/plainte.

Dans Julia 1.0, je m'inquiète honnêtement de ce que je ferai si je suis au milieu d'un cours d'algèbre linéaire et que je dois mystérieusement taper un mot-clé global

Je compatis à ce problème. Lorsque j'enseignais la programmation simple aux étudiants en économie nécessaires pour un cours, je suggérais généralement qu'ils fassent des allers-retours entre l'encapsulation du code dans des fonctions et le simple commentaire de function et end et l'exécution de choses à l'échelle mondiale, afin qu'ils puissent inspecter ce qui se passe. Cela compensait à peu près le manque d'infrastructure de débogage à l'époque dans Julia.

Il semble que cette approche ne soit plus réalisable. Mais je me demande si c'était vraiment la bonne façon de le faire de toute façon, et entre-temps, diverses choses se sont beaucoup améliorées (#265 a été corrigé, Revise.jl et récemment Rebugger.j ont considérablement amélioré le workflow/le débogage).

Il semble que ce problème ne dérange pas beaucoup les utilisateurs expérimentés, la principale préoccupation est la confusion dans un cadre pédagogique. Je n'ai pas encore expérimenté cela moi-même, mais je me demande si nous pourrions plutôt adapter nos approches à l'enseignement, par exemple introduire des fonctions avant les boucles, éviter les boucles à portée globale. Ce sont des éléments de bon style de toute façon et profiteraient aux étudiants.

Juste une petite note : alors que la casse spéciale de la portée globale du REPL, permettra de copier-coller du code dans et depuis des fonctions, cela ne permettra pas de copier-coller dans/depuis la portée globale d'un autre module.

Je me demande si nous pourrions adapter nos approches à l'enseignement à la place, par exemple introduire des fonctions avant les boucles, éviter les boucles de portée globale.

C'est totalement impraticable dans une classe qui n'est pas axée sur l'enseignement de la programmation. Je pourrais aussi bien ne pas utiliser Julia dans mes cours si je ne peux pas l'utiliser de manière interactive et/ou si je dois d'abord écrire des fonctions pour tout.

(Et ce n'est pas seulement pédagogique. Les boucles de portée globale sont utiles pour le travail interactif. Et l'une des principales raisons pour lesquelles les gens aiment les langages dynamiques pour l'informatique technique est leur facilité d'exploration interactive. Tout le codage n'est pas axé sur les performances.)

Il y a eu des dizaines de fils de discussion et de problèmes au fil des ans au cours desquels les gens sont confus ou se plaignent de l'ancienne distinction "portée souple/dure", donc prétendre que personne n'a jamais été confus ou ne s'est plaint de l'ancien comportement est juste... pas vrai. Je pourrais en déterrer certains, mais tu étais là, @stevengj , donc tu peux les déterrer tout aussi facilement et j'ai du mal à croire que tu n'as pas remarqué ou que tu ne te souviens pas de ces plaintes et conversations.

@StefanKarpinski , je fais spécifiquement référence aux personnes qui se plaignent qu'une boucle globale dépend de l'état global. Je ne me souviens pas que quelqu'un se soit plaint qu'il s'agissait d'un mauvais comportement, et je ne peux en trouver aucun exemple.

Je suis d'accord que les gens ont été confus quant au moment et à l'endroit où l'affectation définit de nouvelles variables, mais cela a généralement été dans l'autre sens - ils voulaient que les portées locales agissent de manière plus globale (plutôt que l'inverse), ou qu'il n'y ait pas de distinction entre begin et let . IIRC, la plainte n'a jamais été que l'affectation à une variable globale dans une boucle globale avait l'effet secondaire surprenant de modifier une globale.

Toute la question de la portée est déroutante pour les nouveaux utilisateurs, et elle continuera de l'être. Mais la partie déroutante n'était pas les cas où l'affectation à un nom de variable globale affectait l'état global. Le comportement actuel rend cela pire, pas meilleur.

@StefanKarpinski : J'ai le sentiment qu'auparavant, la confusion avec la portée soft/hard était plus de nature théorique (des personnes lisant le manuel) que pratique (des personnes obtenant des résultats inattendus). C'était définitivement comme ça pour moi et ce que, par exemple, les résultats de la recherche ici soutiennent cela; J'ai trouvé un contre-exemple ici .

D'un autre côté, ce nouveau comportement ne confondra pas les gens lors de la lecture du manuel, mais lors de l'utilisation du REPL. On peut dire que ce dernier est pire.

SoftGlobalScope.jl est désormais un package enregistré. Mon intention est de l'activer par défaut (opt-out) pour IJulia, au moins ce semestre.

@mauro3 , même votre "contre-exemple" concerne une personne confuse par la portée

Je précise qu'IJulia a la possibilité intéressante de faire des variables locales faire des blocs par défaut. C'est-à-dire que si vous faites cela en un seul bloc, cela fonctionne :

t = 0
for i = 1:n
    t += i
end
t

... et t n'est visible que dans ce bloc d'évaluation. Si vous vouliez qu'il soit visible à l'extérieur, il faudrait faire ceci :

global t = 0
for i = 1:n
    global t += i
end
t

J'ai également envisagé une approche similaire pour Julia où les blocs sont des fichiers plutôt que des modules. En d'autres termes, le simple fait t = 0 faire global t = 0 qui serait alors visible dans tout le module. Peut-être trop bizarre, mais cela m'est venu à l'esprit plusieurs fois au fil des ans.

IJulia a la possibilité intéressante de faire des variables locales faire des blocs par défaut

@StefanKarpinski , je pense que ce serait encore plus déroutant et irait à l'encontre de la façon dont les ordinateurs portables sont normalement utilisés. Il est courant que la même variable soit utilisée/modifiée dans plusieurs cellules, donc exiger un mot-clé global pour toutes les variables inter-cellules n'est pas un début pour moi - cela nécessiterait encore plus de discussion sur les concepts de portée que le problème avec les boucles for nous avons parlé ici.

Je pense que tant que nous sommes tous d'accord --- comme nous semblons --- qu'il s'agit principalement ou entièrement d'un problème d'interaction, alors nous avons une voie à suivre. Si nous faisons un cas particulier dans le REPL (comme c'est le cas pour IJulia), le seul mauvais cas est de développer quelque chose dans le REPL, puis de le déplacer vers le code de script de niveau supérieur. C'est sans doute le point où vous devriez introduire des fonctions, donc je ne pense pas que ce soit si mauvais. Le copier-coller du code entre le REPL et le corps de la fonction fonctionnera (principalement), ce qui est probablement suffisant.

Ensuite, nous avons également la possibilité de justifier/clarifier davantage la distinction en rendant les variables REPL en quelque sorte locales au REPL --- c'est-à-dire pas des variables globales normales, non disponibles en tant que Main.x . Ceci est très similaire à ce que @StefanKarpinski vient de proposer ci-dessus, mais partagé entre tous les blocs/cellules d'entrée.

D'un point de vue pratique, obtenir ce "fixe" dans le REPL n'est pas
important uniquement pour les utilisateurs enseignants/non-programmeurs. Ce comportement aussi
rend le débogage interactif via le REPL (par copier-coller des parties) très
peu pratique. Ce mode de débogage peut parfois être préférable (même à un
bon débogueur et) même pour les programmeurs expérimentés (et est souvent l'un des
les raisons de préférer un langage dynamique). Bien sûr pour les expérimentés
programmeurs, être facultatif ne devrait pas être un problème ; Pour les utilisateurs novices, il
serait de préférence la valeur par défaut.

@StefanKarpinski
En tant que programmeur naïf, je ne vois pas vraiment ce qui ne va pas dans la visualisation du
portée globale comme une sorte amusante englobant la portée locale, en particulier dans la dynamique
langues. Je comprends que du point de vue du compilateur, ce n'est pas
forcément correct (chez Julia), mais c'est un modèle sympa, facile et utile
pour un programmeur (naïf). (Je soupçonne également que cela pourrait être mis en œuvre de cette façon dans
certaines langues).
Julia semble également le présenter ainsi au programmeur :
La fonction fonction suivante donnera l'erreur "un non défini", qui
cela ne le fera pas si a=1 est placé avant la boucle for.

test de fonctionnalité()
pour je = 1:10
a=a+i
finir
a=1
@montrer un
finir

qui, à moins que je ne sois complètement incompris, semble en contradiction avec « Que ce soit un
la variable locale externe existe, par conception, ne dépend pas de l'ordre de
l'apparition ou l'exécution des expressions dans la portée locale externe".

Je suis tout à fait d'accord pour éviter "l'action effrayante à distance", et beaucoup
préférez une définition explicite pour l'utilisation des globals au niveau de la pile de fonctions/appels
niveau et j'aimerais personnellement aussi avoir quelque chose comme le chargement d'un fichier dans
sa propre portée et nécessitant une définition explicite pour l'utilisation de variables globales.
Au niveau des boucles ça va un peu trop loin pour moi par contre, comme le
définitions/contexte est généralement assez proche.
L'exemple de 3 fichiers est un peu artificiel (et échoue avec l'attendu "a not
défini" erreur): Vous mettriez normalement la définition initiale dans le même
déposer.
Il y a un réel danger effrayant dans cela (et j'ai été mordu par cela
dans d'autres langues) dans lesquelles les inclusions sont exécutées dans la portée globale, vous
définissent par inadvertance une variable globale qui peut interférer avec d'autres
code. Cependant, devoir utiliser global dans la boucle n'est pas une solution pour
ce problème.

par rapport à la session REPL de longue durée :
Le comportement actuel remplace un mode de défaillance très rare et facile à repérer
pour exécuter un exemple en ligne dans le REPL (vous manquez de copier/coller le
définition initiale de la variable avant la boucle, et ont déjà le
même variable définie globalement à partir de quelque chose de précédent) sans être
capable d'exécuter correctement un exemple en ligne s'il fait partie d'une fonction
(sans ajouter global partout), et ne pas résoudre le problème s'il est
non (si global est déjà présent dans le code en ligne, vous utiliserez toujours le
valeur erronée dans la variable globale déjà existante)

J'aurais dû m'y mettre plus tôt, mais après un bref moment d'inquiétude, tout semble aller pour le mieux.

En fait, nous n'avons jamais complètement pris en charge la copie et le collage de code d'une fonction ligne par ligne dans le REPL... La première chose qui vient à l'esprit est un mode REPL pour le débogage de fonction ligne par ligne.

En effet, Rebugger (qui est exactement cela) ne fonctionne correctement sur 1.0 que parce qu'il manque la dépréciation de portée de 0.7, et n'a jamais pu fonctionner sur 0.6. Cependant, je suis heureux de pouvoir vérifier que SoftGlobalScope.jl ne semble pas casser cela. Par exemple, si vous entrez assez profondément dans show([1,2,4]) vous obtenez ici :

show_delim_array(io::IO, itr::Union{SimpleVector, AbstractArray}, op, delim, cl, delim_one) in Base at show.jl:649
  io = IOContext(Base.TTY(RawFD(0x0000000d) open, 0 bytes waiting))
  itr = [1, 2, 4]
  op = [
  delim = ,
  cl = ]
  delim_one = false
  i1 = 1
  l = 3
rebug> eval(softscope(Main, :(<strong i="10">@eval</strong> Base let (io, itr, op, delim, cl, delim_one, i1, l) = Main.Rebugger.getstored("bbf69398-aac5-11e8-1427-0158b103a88c")
       begin
           print(io, op)
           if !(show_circular(io, itr))
               recur_io = IOContext(io, :SHOWN_SET => itr)
               if !(haskey(io, :compact))
                   recur_io = IOContext(recur_io, :compact => true)
               end
               first = true
               i = i1
               if l >= i1
                   while true
                       if !(isassigned(itr, i))
                           print(io, undef_ref_str)
                       else
                           x = itr[i]
                           show(recur_io, x)
                       end
                       i += 1
                       if i > l
                           delim_one && (first && print(io, delim))
                           break
                       end
                       first = false
                       print(io, delim)
                       print(io, ' ')
                   end
               end
           end
           print(io, cl)
       end
       end)))
[1, 2, 4]

Cela fonctionne donc bien sur 1.0 (avec ou sans softscope ). Sur 0.7, évaluer ceci (avec ou sans softscope ) donnera

┌ Warning: Deprecated syntax `implicit assignment to global variable `first``.
│ Use `global first` instead.
└ @ none:0
┌ Warning: Deprecated syntax `implicit assignment to global variable `first``.
│ Use `global first` instead.
└ @ none:0
[ERROR: invalid redefinition of constant first
Stacktrace:
 [1] top-level scope at ./REBUG:9 [inlined]
 [2] top-level scope at ./none:0
 [3] eval(::Module, ::Any) at ./boot.jl:319
 [4] top-level scope at none:0
 [5] eval at ./boot.jl:319 [inlined]
 [6] eval(::Expr) at ./client.jl:399
 [7] top-level scope at none:0

Donc, 0.7/1.0 est définitivement un pas en avant, et si softscope facilite certaines choses sans casser des fonctionnalités importantes, c'est génial.

La plus grande préoccupation, par conséquent, est simplement de savoir comment intercepter cela de manière appropriée sans tanker d'autres packages (https://github.com/stevengj/SoftGlobalScope.jl/issues/2).

@timholy , SoftScope ne touche pas aux arguments des appels de macro (puisqu'il n'y a aucun moyen de savoir comment la macro la réécrirait), donc :(<strong i="6">@eval</strong> ...) est protégé.

semble en contradiction avec « Qu'un
la variable locale externe existe, par conception, ne dépend pas de l'ordre de
l'apparition ou l'exécution des expressions dans la portée locale externe".

La variable locale (externe) a existe, mais n'a pas encore été affectée. Si la boucle essayait d'affecter à a avant de la lire, l'affectation serait également visible à l'extérieur.

En général, la création d'une liaison de variable et l'attribution d'une valeur à celle-ci sont des étapes distinctes.

Quel est le calendrier à ce sujet ? Il semble que ce serait une grande amélioration pour la convivialité de l'utilisateur. Et à ce moment "critique" de Julia avec la sortie 1.0, il semblerait avantageux d'obtenir ce correctif le plus rapidement possible (de la manière suggérée par Jeff ci-dessus) et de marquer une nouvelle version de Julia ou une version REPL. (Désolé pour ce commentaire sur le fauteuil, car je ne vais certainement pas résoudre ce problème !)

@JeffBezanson
J'allais faire valoir que bien que cela soit vrai (pour l'implémentation/le compilateur), le programmeur naïf de Julia ne peut pas voir de comportement différent de son modèle conceptuel plus simple (une variable commence à exister au moment où elle est définie). Malheureusement vous avez raison, le code suivant ne donnera pas d'erreur, alors qu'il donnera une erreur si vous omettez le a=2 à la fin
test de fonctionnalité()
pour je = 1:10
a=1
finir
println(a)
a=2
finir
Je vais vous expliquer malheureusement : je peux comprendre le comportement (parce que j'ai déjà travaillé avec des langages compilés) mais je le trouve toujours déroutant et inattendu. À quel point cela doit-il être mauvais pour quelqu'un qui n'a qu'une expérience de script ou qui est novice en programmation. Aussi, j'ai trouvé du code qui montre le comportement, je ne vois pas d'application utile (peut-être que vous pouvez m'aider là-bas)

Sur le REPL :
Je suis juste devenu plus convaincu que le retour de la portée à "normale" au moins dans le REPL (pas besoin d'ajouter des boucles globales) est une priorité élevée: je testais certaines choses dans le REPL aujourd'hui et j'ai été (encore) mordu par cela, prendre le temps de s'en rendre compte. Étant donné que je suis Julia depuis un certain temps déjà, j'aime vraiment beaucoup, je suis même ce fil de discussion sur le problème, je dirais même que c'est un écueil : un débutant (à la Julia) qui teste la langue est très susceptible de ne pas trouver résoudre le problème et abandonner.

@jeffbezanson et moi sommes tous les deux en vacances tant attendues (je ne devrais pas lire ceci). Nous pouvons trouver quoi faire dans une semaine ou deux.

@derijkp , bien que les commentaires soient appréciés, les règles de portée ne sont pas

@derijkp Une réponse courte est que je pense que c'est plus facile si la portée d'une variable correspond à une construction de bloc (par exemple le corps d'une fonction ou d'une boucle). Avec votre suggestion, la portée d'une variable serait un sous-ensemble d'un bloc, ce qui, je pense, est finalement plus complexe et déroutant --- vous ne pouvez pas pointer vers une forme syntaxique qui correspond à la portée de la variable.

Oui, je peux croire que c'est un décalage avec l'intuition de certaines personnes. Mais vous ne pouvez optimiser que pendant les dix premières minutes d'utilisation d'une langue jusqu'à un certain point. La vraie question est, à quel point est-il difficile d'enseigner/apprendre comment cela fonctionne, et quelle conception permettra de gagner du temps à long terme (en simplifiant le langage, en facilitant le développement d'outils, etc.) ?

(en accord avec une grande partie de ce qui précède sur la modification du comportement du REPL)
J'aimerais que le REPL soit d'une manière qui ne conduise pas à cette question de débordement de pile
et plus tôt serait le mieux car beaucoup de nouveaux yeux regardent Julia

Je suis d'accord... Et je pense aussi que les règles de portée ne devraient pas nécessairement changer, juste toutes les interfaces interactives (c'est-à-dire les contrôles REPL, Jupyter et Juno entrent)

Il ne s'agit pas seulement pour les débutants d'apprendre une nouvelle règle. Si vous ne pouvez pas copier et coller des fragments de code dans le REPL, jupyter, etc., ainsi que dans les fonctions, c'est également un inconvénient majeur pour les programmeurs intermédiaires.

Bien sûr, je suis également d'accord avec les autres affiches ... avec les débutants, ils vont prendre des fragments de code qu'ils voient dans les fonctions, me copier dans des scripts et être complètement confus quand il n'a pas le même comportement lorsqu'il est copié à l'intérieur d'une fonction , dans juno, le repl et jupyter. Il y aura 100 questions d'échange de pile qui se ramènent au même problème. Les programmeurs intermédiaires vont avoir toutes sortes de solutions maison avec un emballage dans des blocs let , etc.

Il y aura 100 questions d'échange de pile qui se ramènent au même problème. Les programmeurs intermédiaires vont avoir toutes sortes de solutions maison avec un emballage dans des blocs let , etc.

C'est possible, mais à ce stade, c'est hypothétique (le PO de la question liée demande également la justification de la règle de portée, au lieu d'être confus à ce sujet).

De plus, bien que je respecte l'expérience d'enseignement de tous ceux qui ont des inquiétudes à ce sujet, le temps nous le dira si cela s'avère être un gros problème en classe.

le questionneur semble avoir été déconcerté par cela: "Je me demande si cela est intuitif pour les utilisateurs débutants de Julia. Ce n'était pas intuitif pour moi ..."

le questionneur semble avoir été confus par cela :

Sans oublier que c'est quelqu'un qui en sait clairement assez sur les langages de programmation pour comprendre les nuances de la portée. Qu'en est-il de tous les utilisateurs de type matlab qui sont complètement ignorants de ces sujets..., et n'investiront probablement jamais assez de temps pour comprendre les nuances.

C'est possible, mais à ce stade c'est hypothétique

J'ai déjà répondu à plusieurs questions liées à cela sur stackoverflow, principalement par de nouveaux utilisateurs, et encore plus dans la vraie vie (la dernière hier, d'un utilisateur de Matlab, qui a vu cela comme un non-droit).

Il y aura 100 questions d'échange de pile qui se ramènent au même problème.

Dans mon "temps libre", j'ai ajouté des balises scope , scoping et global-variables aux questions SE. Je ne m'arrête que par manque de temps, pas parce qu'il n'y en a plus.

Conclusion après de nombreuses discussions, y compris le triage : nous allons inclure quelque chose du genre SoftGlobalScope dans Base et l'utiliser dans le REPL et tous les autres contextes d'évaluation interactifs. @JeffBezanson a souligné que la façon dont cela est mis en œuvre est en fait essentiellement la même que la façon dont la portée souple a été précédemment mise en œuvre, donc dans une certaine mesure, nous arrivons à la boucle. La différence est qu'il n'y a désormais plus de comportement de portée dans les modules ou les scripts, uniquement dans des contextes de type REPL. Je pense aussi qu'_expliquer_ la portée souple en tant que réécriture de source est plus clair que d'essayer de faire la distinction entre les portées matérielles et souples (ce que nous n'avons jamais expliqué comme Jeff l'a expliqué, je pourrais le souligner).

Ces deux affirmations me confondent un peu car elles semblent un peu contradictoires :

et l'utiliser dans le REPL et tous les autres contextes d'évaluation interactive

il n'y a pas de comportement de portée dans les [...] scripts, seulement dans des contextes de type REPL.

Cela signifie-t-il que le module Main a parfois une portée souple (disons à l'invite REPL) et parfois une portée dure (disons quand julia -L script.jl ) ? Ne serait-il pas logique de dire que Main toujours une portée souple ? Et un module peut s'inscrire au soft scope de using SoftGlobalScope ?

(Je suppose) les règles de portée ne peuvent pas être modifiées dans les scripts car elles seraient rétrocompatibles, c'est-à-dire rompraient la promesse que tout code écrit pour 1.0 s'exécutera sur n'importe quelle version 1.*. Vous avez cependant raison de dire que le même problème avec la portée du REPL s'applique également aux scripts (utilisateur naïf qui ne comprend pas pourquoi son code ne fonctionne pas correctement lorsqu'il est exécuté en tant que script). Un moyen de résoudre/d'atténuer ce problème sans incompatibilité majeure serait d'ajouter une option à la ligne de commande julia pour utiliser softscope (ou alternative), par exemple julia -f programfile, et d'afficher cette option dans toute description/tutoriel qu'un débutant est susceptible de rencontrer.
Je vois également une alternative potentielle pour le softscope qui peut avoir certains avantages (bien que j'oublie probablement les inconvénients): Et si un fichier (un script appelé) introduisait toujours sa propre portée locale: les règles de portée seraient en parfaite cohérence avec celles de fonctions, et avec les attentes de nombreux utilisateurs. Cela supprimerait également une grande partie des obligations de performance avec les nouveaux utilisateurs :
Plus de globals inutiles (les globals devraient être explicitement définis), et le code pourrait être compilé
(Combien de fois avez-vous dû dire de tout mettre dans une fonction, et d'éviter d'utiliser des globales ?)

Je viens de frapper ceci et j'étais complètement abasourdi pour être honnête, ne l'ayant jamais vu auparavant dans aucune autre langue. Je prévois d'introduire un cours Julia facultatif pour les utilisateurs avancés de R dans mon université plus tard cette année une fois que les choses se seront calmées, et mes étudiants l'atteindront le jour 0 lorsqu'ils commenceront à taper des choses au hasard dans le REPL. Et le fait que les boucles for se comportent différemment des instructions if fait que frotter le sel dans la plaie, aussi logique que cela puisse être en termes de portée. La portée à l'intérieur des fonctions est suffisamment difficile à comprendre pour les étudiants en biologie, l'idée d'avoir à expliquer des incohérences _bien perçues_ flagrantes dans le REPL / dans un script / dans une boucle for / dans une instruction if (parce que c'est de cela que nous parlons à propos d'ici) d'une manière différente de toutes les autres langues sur terre me rend très triste.

Je comprends la promesse de compatibilité descendante qui a été faite, mais avoir ce travail _comme prévu par toutes les personnes non-cs sur la planète (et la plupart des gens cs je soupçonne)_ semble être une correction de bug plutôt qu'un problème de compatibilité descendante - nous ne disons pas que chaque bug sera reproduit à jamais, n'est-ce pas ? Le correctif REPL est évidemment essentiel, c'est donc bien que vous proposiez cela, mais devoir expliquer ensuite que vous ne pouvez pas copier un script dans le REPL et vous attendre à ce que le même comportement semble aussi mauvais ou pire que le problème d'origine.

S'il vous plaît, s'il vous plaît, pensez à traiter cela comme une correction de bogue et à le pousser avec des scripts ainsi que le REPL - même s'il y a un changement pour passer à l'"ancien" comportement - et le faire dès que possible dans 1.0.1.

Un collègue que j'essayais d'apprendre à Julia vient également de tomber sur ça. Devoir expliquer toute la variable globale vs locale au début n'est pas idéal...

Je ne pense pas que traiter cela comme une "correction de bug" soit dans les cartes, car cela briserait le contrat de stabilité 1.0. Cependant, il me semble raisonnable d'utiliser softscope pour les scripts exécutés avec julia -i (c'est-à-dire en mode "interactif").

(C'est-à-dire qu'il y aurait un indicateur --softscope={yes|no} et sa valeur par défaut serait isinteractive .)

Nous devrons considérer le choix du mode de script.

D'ailleurs, ce n'est pas fou pour moi d'utiliser par défaut --softscope=yes pour tout "script", c'est-à-dire pour julia foo.jl , et d'activer uniquement les règles de portée "dures" pour les modules et include (à quel point vous devriez vraiment mettre la plupart du code dans des fonctions).

D'ailleurs, ce n'est pas fou pour moi de choisir par défaut --softscope=yes pour tout "script",

Cette. L'autre à considérer sérieusement est Juno. N'oubliez pas que les gens vont <shift-enter> travers leur code pour faire du développement interactif (en particulier lorsqu'ils travaillent avec les tests de régression) et s'attendent ensuite à pouvoir exécuter le même fichier. Cela devrait-il avoir de l'importance si le code est dans un @testset ou non (ce qui, je pense, pourrait introduire une portée) ? Ce serait très déroutant pour l'utilisateur si le même texte change lorsqu'il est dans un @testset rapport à l'utilisation de l'intégration d'Atom, et est incompatible avec le fait ] test faire

Il me semble que la meilleure solution est que la portée matérielle est simplement une chose d'opt-in, où si chaque autre utilisation (y compris include dans les scripts) utilise softscope sauf si vous dites le contraire .

différent de toutes les autres langues sur terre

Voulez-vous écrire var x = 0 pour introduire chaque variable ? Cela "réparerait" également cela et ressemblerait davantage à d'autres langues.

nous ne disons pas que chaque bogue sera reproduit pour toujours, n'est-ce pas ?

Ce n'est pas ainsi que cela fonctionne. Vous ne pouvez pas modifier la langue que vous souhaitez simplement en appelant le comportement actuel un bogue.

Je ne pense vraiment pas qu'il devrait y avoir une option de ligne de commande pour cela. Ensuite, chaque morceau de code julia devra être accompagné d'un commentaire ou de quelque chose vous indiquant quelle option utiliser. Une sorte de directive d'analyseur syntaxique dans un fichier source serait un peu mieux, mais encore mieux serait d'avoir une règle fixe. Par exemple, une portée stricte à l'intérieur des modules uniquement peut avoir du sens.

Permettez-moi d'essayer à nouveau de fournir une explication qui pourrait être utile pour éviter la manie, l'hystérie et le carnage que les gens voient en classe :

"
Julia a deux types de variables : locales et globales. Les variables que vous introduisez dans le REPL ou au niveau supérieur, en dehors de toute autre chose, sont globales. Les variables introduites dans les fonctions et les boucles sont locales. La mise à jour des variables globales dans un programme est généralement mauvaise, donc si vous êtes dans une boucle ou une fonction et que vous souhaitez mettre à jour une globale, vous devez être explicite à ce sujet en écrivant à nouveau la déclaration global .
"

Peut-être que cela peut être amélioré; suggestions bienvenues. Je sais, tu préfères ne pas avoir besoin d'explication du tout. Je comprends. Mais cela ne me semble pas si mal.

Je ne pense vraiment pas qu'il devrait y avoir une option de ligne de commande pour cela. Ensuite, chaque morceau de code julia devra être accompagné d'un commentaire ou de quelque chose vous indiquant quelle option utiliser. Une sorte de directive d'analyseur dans un fichier source serait un peu mieux, mais encore mieux serait d'avoir une règle fixe

Je suis d'accord. Cela ressemble à un casse-tête d'enseignement et de communication pour moi.

Par exemple, une portée stricte à l'intérieur des modules uniquement peut avoir du sens.

Juste pour que je comprends: (non dans un module) si j'avais un petit script dans un .jl fichier que j'avais copié à partir d' un ordinateur portable IJulia, puis si je courais directement ou shift- que le code soit dans le REPL entrez dans Juno, alors il se comporterait de manière cohérente comme une portée logicielle... mais si je le copiais au lieu d'un bloc module , il me crierait dessus à propos de globals? Mais si j'ai copié ce code à l'intérieur de fonctions à l'intérieur d'un module, cela devrait fonctionner.

Si c'est le cas, c'est tout à fait logique, c'est très enseignable et cohérent. Les scripts de haut niveau sont une interface interactive pour l'exploration, etc. mais vous ne mettriez jamais ce genre de code dans un module. Les modules sont quelque chose que vous devez remplir avec des fonctions qui sont très soigneusement considérés comme des globals. Il serait facile de parler de ces règles aux gens.

Voulez-vous écrire var x = 0 pour introduire chaque variable ? Cela "réparerait" également cela et ressemblerait davantage à d'autres langues.

Non, je préfère pas ! Mais les langages de script qui ont un REPL le font rarement (par exemple ruby, python, R, ...), ils se comportent comme Julia v0.6.

Julia a deux types de variables : locales et globales. Les variables que vous introduisez dans le REPL ou au niveau supérieur, en dehors de toute autre chose, sont globales. Les variables introduites dans les fonctions et les boucles sont locales. La mise à jour des variables globales dans un programme est généralement mauvaise, donc si vous êtes à l'intérieur d'une boucle ou d'une fonction et que vous souhaitez mettre à jour une globale, vous devez être explicite à ce sujet en réécrivant la déclaration globale.

Je comprends tout à fait ce que vous dites ici, et je ne vais plus (toucher du bois!) Faire cette erreur. Mais tout le problème qui m'inquiète n'est pas moi. J'ai trouvé relativement facile d'introduire la portée (sans le mentionner directement) lorsque j'explique que les variables à l'intérieur des fonctions ne peuvent pas voir celles à l'extérieur et vice-versa (même si c'est plus une aspiration qu'un fait dans R !), parce que les fonctions eux-mêmes sont déjà un concept _relativement_ avancé. Mais cela frappe beaucoup plus tôt dans la courbe d'apprentissage ici où nous ne voulons pas que quelque chose d'aussi compliqué que la portée empiète sur les gens...

Notez également que ce n'est pas seulement "_les variables que vous introduisez dans le REPL ou au niveau supérieur, en dehors de toute autre chose, sont globales_" et "_les variables introduites à l'intérieur des fonctions et des boucles sont locales_", c'est aussi ces variables dans les instructions if dans le REPL ou à le niveau supérieur est global mais les variables d'un @testset sont locales. Nous nous retrouvons dans un terrier de « essayez-le et déterminez par vous-même que ce soit local ou mondial, bonne chance ».

Cependant, je suis d'accord avec @jlperla - la proposition selon laquelle "la portée

nous ne voulons pas que quelque chose d'aussi compliqué que la portée empiète sur les gens...
au niveau supérieur sont globales mais les variables dans un @testset sont locales

Ce que j'essaie de comprendre, c'est que je pense qu'une simple description du global par rapport au local est suffisante pour un enseignement précoce --- vous n'avez même pas besoin de dire le mot "portée" (cela ne se produit pas du tout dans mon explication ci-dessus). Lorsque vous montrez simplement quelques expressions et boucles simples dans le REPL, vous n'enseignez pas aux gens les ensembles de tests et vous n'avez pas besoin d'une liste exhaustive du comportement de portée de tout dans le langage.

Mon seul point est que ce changement ne rend pas soudainement nécessaire d'enseigner beaucoup de détails sur la langue dès le départ. Vous pouvez toujours ignorer la grande majorité des choses sur les étendues, les ensembles de tests, etc., et une simple ligne sur global vs local devrait suffire.

et une simple ligne sur global vs local devrait suffire.

Dans un monde où tout le monde a commencé à écrire tout son code à partir de zéro, je serais tout à fait d'accord.

Le problème est que vous devez enseigner aux étudiants non seulement la portée, mais aussi la compréhension de la portée d'où ils ont copié-collé le code qu'ils ont obtenu. Vous devez leur apprendre que s'ils copient-collent du code qui se trouve sur stackexchange dans une fonction ou un bloc let, ils doivent le parcourir et trouver où ajouter "global" s'ils le collent dans le REPL ou un .jl Fichier

Et puis les étudiants commencent à se demander pourquoi for crée cette portée dont ils doivent s'inquiéter mais pas d'autres choses....

Nous nous retrouvons dans un terrier de « essayez-le et déterminez par vous-même que ce soit local ou mondial, bonne chance ».

Pop quiz : dans julia 0.6, est x global ou local :

for i = 1:10
    x = i
end

La réponse est qu'il n'y a aucun moyen de le savoir, car cela dépend si un x global a été défini auparavant. Maintenant, vous pouvez dire avec certitude que c'est local.

Mes amis, cette discussion est sur le point de ne plus être productive. Jeff sait très bien que l'ancien comportement était sympa dans le REPL. Selon vous, qui l'a conçu et mis en œuvre en premier lieu ? Nous nous sommes déjà engagés à changer le comportement interactif. Une décision doit encore être prise pour savoir si un "script" est interactif ou non. Cela semble interactif lorsque vous l'appelez "un script", mais cela semble beaucoup moins interactif lorsque vous l'appelez "un programme" - pourtant, c'est exactement la même chose. Veuillez garder les réponses courtes et constructives et centrées sur les choses qui doivent encore être décidées. S'il y a des commentaires qui s'écartent de cela, ils peuvent être masqués et le fil peut être verrouillé.

Une pensée que j'avais mais que nous avons rejetée comme étant "trop ​​ennuyeuse" et "susceptible d'amener les villageois à sortir leurs fourches" était que dans des contextes non interactifs, nous pourrions exiger un local ou global annotation dans "soft scope". Cela garantirait que le code d'un module fonctionnerait de la même manière s'il était collé dans le REPL. Si nous appliquions cela aux "scripts"/"programmes", il en serait de même pour eux.

Lorsque j'ai découvert Julia pour la première fois (il n'y a pas si longtemps, et je viens principalement d'un milieu Fortran), on m'a appris que " Julia est compilée et rapide au niveau des fonctions, donc tout ce qui doit être efficace doit être fait à l'intérieur des fonctions . Dans le 'programme' principal, il se comporte comme un langage de script". J'ai trouvé cela assez juste, car je ne peux imaginer que quelqu'un fasse quelque chose de trop exigeant en termes de calcul sans comprendre cette affirmation. Par conséquent, s'il y a un sacrifice dans les performances du programme principal pour utiliser la même notation et les mêmes constructions que dans les fonctions, je trouve cela totalement acceptable, beaucoup plus acceptable que d'essayer de comprendre et d'enseigner ces règles de portée et de ne pas pouvoir copier et coller des codes d'un endroit à un autre.

Soit dit en passant, je suis encore un novice en Julia, l'ayant choisi pour enseigner à des étudiants du secondaire et du premier cycle quelques bases de simulations de systèmes physiques. Et j'espère déjà que ce problème revient au comportement « normal » des versions précédentes, car cela nous donne pas mal de maux de tête.

Cette conversation est verrouillée maintenant et seuls les committers Julia peuvent commenter.

@JeffBezanson , quel serait le plan pour implémenter la sémantique que vous avez suggérée dans ce fil de discussion , initialement uniquement dans le REPL et opt-in ailleurs?

On dirait que vous envisagez de mettre cela directement dans le code de réduction ( julia-syntax.scm ), plutôt que comme une réécriture de syntaxe à la SoftScope.jl ? Ou préférez-vous d'abord l'avoir comme réécriture de la syntaxe (modifier SoftScope selon la règle proposée et la convertir en stdlib), et reporter sa mise dans le code de réduction pour une version ultérieure de Julia ?

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

Questions connexes

omus picture omus  ·  3Commentaires

sbromberger picture sbromberger  ·  3Commentaires

dpsanders picture dpsanders  ·  3Commentaires

iamed2 picture iamed2  ·  3Commentaires

Keno picture Keno  ·  3Commentaires