Julia: Supprimer l'exigence, l'importation et peut-être l'importation et la fusion dans l'utilisation

Créé le 14 août 2014  ·  72Commentaires  ·  Source: JuliaLang/julia

À partir de la liste de diffusion :

Stéphane :

Je pense que nous devrions nous débarrasser complètement de l'importation et avoir simplement

en utilisant Foo
en utilisant Foo : barre

Le premier chargerait et créerait une liaison pour Foo, rendrait ses exportations disponibles en tant que liaisons "soft" (ce que fait using maintenant). La seconde chargerait et créerait également une liaison pour Foo, et rendrait la barre disponible en tant que liaison "dure" (ce que fait l'importation maintenant)."

Il semble que ce soit encore un point de confusion pour les nouveaux arrivants.

breaking design modules speculative

Commentaire le plus utile

J'aime la symétrie de import et export . (Comme quelqu'un l'a souligné quelque part.)

Tous les 72 commentaires

Nous aurions besoin de la fonctionnalité import Foo d'une manière ou d'une autre, où vous obtenez juste le Foo et rien d'autre

using Foo: ?

using Foo: Foo ?

Pour lier Foo au module Foo (et rien d'autre) :
import Foo
Pour lier Foo au module Foo, x à Foo.x, y à Foo.y
import Foo: x, y
Pour lier Foo au module Foo, et tous les noms exportés de Foo sont liés sans qualification :
import Foo: *

Cela pourrait être using la place, mais j'ai l'impression que c'est plus dans l'esprit de import .

Cela supprime également la distinction entre apporter quelque chose dans la portée et le rendre disponible pour l'extension de méthode. Personnellement, je pense que c'est une bonne chose et que cela rend le système de modules plus compréhensible, mais je voulais m'assurer que nous en parlions.

Il y a de bonnes raisons de penser qu'une construction qui rend disponibles toutes les liaisons exportées d'un module devrait en faire des liaisons logicielles ( using ) et non des liaisons matérielles ( importall ). Supposons que le module A utilise le module B et définit foo(::Any) . Si une version ultérieure du module B définit et exporte également foo(::Any) , vous ne voulez pas qu'ils se bousculent. Vous ne voulez pas non plus que le module B définisse foo(::Int) et que le module A appelle parfois cette méthode au lieu de la méthode qu'il a définie car elle est plus spécifique, ou pour avoir à lister tous les identifiants que vous voulez du module B pour éviter d'importer un seul identifiant en conflit.

Mais si vous avez explicitement répertorié les identifiants que vous souhaitez importer, il n'y a jamais de raison de donner une liaison souple. Soit vous n'allez pas définir de nouvelles méthodes d'un identifiant, auquel cas les liaisons dures et souples ont un comportement identique, soit vous allez en définir de nouvelles méthodes, auquel cas une liaison souple équivaut à aucune liaison et si vous voulez ce comportement, vous devez simplement supprimer l'identifiant de la liste.

Donc, en bref, j'aime la proposition de @StefanKarpinski . Conceptuellement, nous avons besoin à la fois de liaisons dures et souples, mais nous pouvons obtenir tous les comportements utiles avec un seul mot-clé.

Je vois ce que tu veux dire. Dans ce cas, j'aime votre proposition selon laquelle using Foo: (avec les deux-points mais pas d'éléments) semble conceptuellement cohérent. Les deux-points indiquent que vous allez limiter les symboles à insérer, mais vous n'en listez aucun.

La fin vide : a l'air un peu drôle, mais je pense que les gens s'y habitueraient

Le problème avec les deux-points de fin vides est que nous recherchons actuellement un nom sur la ligne suivante - en d'autres termes, using Foo: est considéré comme incomplet.

Connexe : #4600

Je sais que la pratique actuelle de Julia consiste à tout importer dans le
namespace du module actuel, mais je pense vraiment qu'il devrait encore y avoir
être un moyen simple et naturel d'importer uniquement le nom d'un module.

Je pense aussi que la surcharge actuelle d'utilisation de Foo et d'utilisation de Foo.Bar est
problématique; il regarde à l'intérieur de Foo mais pas à l'intérieur de Foo.Bar (sauf si Foo.Bar est
un module ?)

Je pense que dans la proposition de Stefan, cela est abordé avec

utiliser Foo : Foo

Le jeudi 14 août 2014, toivoh [email protected] a écrit :

Je sais que la pratique actuelle de Julia consiste à tout importer dans le
namespace du module actuel, mais je pense vraiment qu'il devrait encore y avoir
être un moyen simple et naturel d'importer uniquement le nom d'un module.

Je pense aussi que la surcharge actuelle d'utilisation de Foo et d'utilisation de Foo.Bar est
problématique; il regarde à l'intérieur de Foo mais pas à l'intérieur de Foo.Bar (sauf si Foo.Bar est
un module ?)


Répondez directement à cet e-mail ou consultez-le sur GitHub
https://github.com/JuliaLang/julia/issues/8000#issuecomment -52202142.

@kmsquire mais que se passe-t-il alors s'il y a un module Foo à l'intérieur du module Foo ? C'est malheureusement ambigu.

C'est une erreur de conception (et provoque un avertissement), donc peu importe

J'aime la version proposée par @ssfrr ci-dessus le meilleur.

Existe-t-il une convention sous-jacente selon laquelle les packages ont un et un seul point d'entrée de module ? -- To import Foo signifie importer le module Foo du package Foo . Tout autre module doit être un sous-module de Foo . Ce qui signifie qu'il existe une correspondance implicite entre le package Foo, Foo.jl dans le package et les module Foo qu'il contient.

Je demande b/c Je me demande si import pourrait également être utilisé pour l'importation locale/relative. Disons qu'un projet a src/foo.jl et src/bar.jl puis dans foo.jl :

import bar

importerait de src/bar.jl . Il s'agit d'une amélioration par rapport à l'utilisation include car il fonctionne dans le système de modules.

Oui, les packages, les points d'entrée des packages et les modules doivent être individuels. Vous pouvez briser cette convention si vous en avez besoin, mais cela n'est pas vraiment nécessaire puisque les modules ne servent qu'à nommer et ne sont pas des unités fonctionnelles. L'idée que vous évoquez est #4600 sauf que la syntaxe pour une importation relative est import .bar alors que import bar est une importation absolue.

@StefanKarpinski Est-ce que import .bar recherche réellement "bar.jl" dans le répertoire local? J'avais l'impression que import .Bar ne faisait référence qu'à un sous-module d'un module parent déjà actuel.

MISE À JOUR : Duh, c'est ce que vous proposez dans #4600. Pardon.

:+1:. Si nous allons le faire, 0.4 serait un bon moment pour le faire.

:+1: S'il vous plaît allez-y et nettoyez-en une partie (pour 0.4 !)

au lieu d'avoir deux mécanismes d'importation pour distinguer l'extension ou non, pourquoi ne pas signaler l'extension sur le site d'extension lui-même via un mot-clé ou une annotation à la définition de la fonction, par exemple remplacer le mot-clé avant la fonction ? Similaire à l'annotation @Override en Java.

Cela a l'avantage que l'on voit clairement que la fonction en remplace une autre (et est donc une méthode)

C'est déjà possible, @ssagaert. Vous le faites en écrivant explicitement le nom du module dans la définition de la fonction (par exemple, Base.print(…) = … ), et cela semble être un style vers lequel beaucoup de gens convergent. Le seul problème est que la syntaxe ne fonctionne pas pour tous les noms possibles (tels que .+ , etc.).

(Soit dit en passant, veillez à entourer les macros de backticks `` pour éviter de harceler les autres utilisateurs de GitHub).

:+1: à la suggestion de @ssagaert d'utiliser @override , le message d'erreur d'origine lorsque l'on essaie d'étendre une méthode sans l'importer d'abord se lit comme suit :

ERROR: error in method definition: function Foo.x must be explicitly imported to be extended

Alors peut-être que @extend pourrait être plus adapté ? Je ne suis pas de langue maternelle anglaise, mais je crois comprendre que _override_ signifie quelque chose comme : annuler, annuler, invalider, nier, annuler, annuler, interrompre, etc.

Je pense que c'est plus explicite :

<strong i="13">@extend</strong> Base.show(...) = ...

Que:

import Base: show

# ... several lines here

Base.show(...) = ...

@Ismael-VC Base.show(...) = ... fonctionne déjà sans rien importer. import n'est nécessaire que si vous voulez que show(...) = ... étende Base.show .

Le mot de remplacement @Ismael-VC n'était qu'une suggestion. Il peut s'agir d'étendre ou de toute autre chose significative. De plus, il ne devrait pas y avoir de @ puisque dans Julia, cela signifie que c'est une macro ( @Override fait référence à Java où c'est une annotation).

@simonster merci, je ne le savais pas !

@ssagaert donc tu veux dire un mot clé ? J'ai essayé quelque chose comme ça mais je suis toujours nul en macro-foo:

module Extend

export <strong i="9">@extend</strong>


macro extend(x)
    mod = x.args[1].args[1].args[1]
    met = x.args[1].args[1].args[2]
    imp = :(Expr(:import, $mod, $met))
    :(Expr(:toplevel, $imp, $(esc(x))))
end


end

Je sais que ce n'est pas général, mais je ne peux toujours pas créer d'expression qui renvoie ce que fait l'analyse:

julia> using Extend

julia> type Foo end

julia> <strong i="13">@extend</strong> Base.show(x::Foo) = Foo
:($(Expr(:toplevel, :($(Expr(:import, Base, :show))), show)))

julia> parse("import Base.show; Base.show(x::Foo) = Foo")
:($(Expr(:toplevel, :($(Expr(:import, :Base, :show))), :(Base.show(x::Foo) = begin  # none, line 1:
            Foo
        end))))

Je pense qu'une macro @extend générale et fonctionnelle ne changerait pas la sémantique du mécanisme d'importation.

@Ismael-VC Je l'ai fait mais j'aime aussi le "truc" de @mbauman .

Je pense que ce serait bien de pouvoir utiliser . seul pour signifier "ceci". vous pouvez donc écrire des expressions telles que :
import Base: . (c'est-à-dire importer Base.Base )
ou
using ..

Je suis à peu près sûr que cela nécessiterait https://github.com/JuliaLang/julia/pull/11891#issuecomment -116098481 cependant. peut-être est-il suffisant d'autoriser des espaces avant le . , mais pas après, pour résoudre le cas d'ambiguïté ?

Je crois que require est déjà obsolète. Ce serait peut-être bien d'ajouter une notation par points plus flexible sur l'importation de modules par 1.0, mais je doute que nous allons changer quoi que ce soit ici par le gel des fonctionnalités 0.6

Après un tas de discussions à ce sujet hier, je me penche vers quelque chose comme les propositions dans https://github.com/JuliaLang/julia/issues/8000#issuecomment -52142845 et https://github.com/JuliaLang/julia/ issues/8000#issuecomment -52143609 :

using A: x, y    # hard imports x and y from A

using A: A       # hard imports just the identifier `A`

using A: ...     # soft imports all of A's exports

using A     # equivalent to `using A: A, ...`

using A.B   # A.B must be a module. equivalent to `using A.B: B, ...`

using A: ..., thing1, thing2    # import all exports plus some non-exported things

L'alternative serait de garder import plutôt que using :

import A             # hard binding for the module `A`
import A: ...        # soft bindings for all names exported by `A`
import A: x, y       # hard bindings for `x` and `y` from `A`
import A: x, y, ...  # equivalent to doing both of the previous two

La règle générale pour savoir si une liaison est dure ou souple serait simple : tout nom explicitement demandé est une liaison dure, toute liaison donnée implicitement est souple.

Edit : il y a un certain désir d'ajouter à ce schéma un raccourci pour import A; import A: ... qui est approximativement ce que using A fait actuellement (la seule différence étant que using A importe actuellement A ); cela pourrait continuer à être using A ou import A... a été proposé.

Oui, je pense que c'est aussi une bonne proposition. Cela revient essentiellement à savoir si l'on préfère using ou import .

Addendum, nous pourrions garder using A comme raccourci pour import A; import A: ... - associé à la suppression du comportement actuel selon lequel chaque module a une liaison exportée pour lui-même, c'est pourquoi using A provoque qu'il y ait une liaison souple à A disponible.

Je serais assez déçu si nous avions encore plusieurs mots-clés après tout cela.

J'aime la symétrie de import et export . (Comme quelqu'un l'a souligné quelque part.)

Toujours faire des "liaisons dures" ne semble pas être la valeur par défaut la plus sûre en termes d'extension de méthode. Il y avait la proposition connexe mais quelque peu distincte d'exiger une qualification de module pour l'extension de méthode qui pourrait supprimer le besoin de « liaisons dures ».

Des reliures rigides sont également nécessaires à cette fin :

import Package: x
x = 1   # gives an error

Et surtout, cette proposition ne ferait pas toujours des liaisons dures --- uniquement pour les choses que vous listez explicitement. Exiger que l'on dise import au lieu de using n'ajoute pas beaucoup de sécurité.

La sécurité vient de la majorité des endroits qui ne font pas d'extension de méthode qui peut très bien s'en tirer avec une reliure souple. Je pense que nous devrions toujours avoir un moyen de demander une liaison souple spécifique sans avoir à importer toutes les exportations d'un paquet (ou obtenir une liaison souple spécifique qui n'est pas exportée).

Avons-nous toujours l'avertissement pour écraser une liaison importée ?

Je pense qu'exiger une qualification de module est une bonne idée. À l'heure actuelle, pour savoir si une fonction est en cours d'introduction ou si une méthode est étendue, vous devez examiner le contenu entier du package pour une instruction import A: func .

Avons-nous toujours l'avertissement pour écraser une liaison importée ?

Je crois que c'est en fait une erreur maintenant.

Il existe une autre proposition flottante qui conserve les deux mots-clés, mais simplifie encore un peu les choses :

  1. Ajoutez la syntaxe import A: ...
  2. Déprécier using A:
  3. Dans using A.B , exigez que A.B soit un module et indiquez qu'il s'agit d'un raccourci pour import A.B; import A.B: ... .

De cette façon, tout peut être fait avec seulement import , mais using X est disponible pour plus de commodité. Ce serait également particulièrement facile de passer à.

BTW, using semble incohérent comme je l'ai posté ici . Si nous gardons using , il devrait être renommé en use si possible.
Je pense que nous devrions exiger la quantification des modules lors de l'extension des fonctions, car sa signification est beaucoup plus claire que le modèle d'importation puis d'extension.

Mon approche préférée :

  • Exiger un préfixe de module explicite pour l'extension
  • Si vous ne voulez que le nom du module et non ses symboles, utilisez using A: A
  • Supprimer import et le type de liaison qu'il crée

Choses qui devraient arriver pour mettre en œuvre https://github.com/JuliaLang/julia/issues/8000#issuecomment -327512355 :

  • changer le comportement de using A en importation matérielle A
  • supprimer la prise en charge de using A: x
  • supprimer la prise en charge de using A.xx n'est pas un sous-module
  • supprimer la prise en charge de import A.xx n'est pas un sous-module
  • ajouter la prise en charge de la syntaxe ... à import

using A: x est souvent utilisé et est très utile. Vous dites que vous voulez x dans votre espace de noms mais vous ne voulez pas l'étendre. Dans import A: x , vous dites que vous voulez pouvoir étendre x . Il existe une distinction significative entre avoir une fonction disponible et être capable de l'étendre.

En y repensant, je dirais que le plus gros problème ici est que using A.B fait deux choses : si B est un module, il importe toutes ses exportations en douceur, et sinon seulement les importations en douceur B . Je pense que nous devrions simplement corriger cela, et faire en sorte que using A.B ne soit autorisé que pour les modules, et avoir using A: a, b pour l'importation logicielle de liaisons spécifiques une à la fois.

Je préférerais s'il y avait une façon d'écrire import A: x plutôt que d'être équivalent à import A.x .

Je vote pour import A: x puisque nous pouvons aussi le faire ; import A: x, y, @z mais import A.x, A.y, a.@z aurait l'air moche.

Est-ce que cette suppression de la version 1.0 signifie qu'il nous restera à la fois using et import pour la version 1.0 ? C'est un peu dommage à mon avis.

Que diriez-vous:

  • Forcer le préfixe du module pour l'extension. Cela rendra le code plus clair sur le fait qu'une extension est destinée au lieu qu'elle soit accidentelle à partir d'une importation dans un autre fichier.
  • using A devient import A: ...
  • using A.X ( X est un module) devient import A.X: ...
  • using A: X ( X n'est pas un module) devient import A: X
  • import A: X n'est pas modifié mais vous ne pouvez pas étendre automatiquement X (voir le premier point)
  • supprimer le mot-clé using

Me manque-t-il un cas d'utilisation ? Peut-être que cela a déjà été suggéré...

Ce que j'aime dans le fait d'être explicite sur le module lors de l'extension, c'est que l'extension devient beaucoup plus locale. À l'heure actuelle, lorsqu'une méthode est étendue, il est courant de placer l'import très près du haut du module (qui peut se trouver dans un fichier complètement différent !). Lorsque la méthode étendue est supprimée, l'instruction d'importation est généralement oubliée.

Je pense que c'est essentiellement la même chose que ma proposition ci-dessus, qui a reçu un bon soutien. @JeffBezanson veut vraiment garder using A au moins pour la facilité d'utilisation et using A: x parce qu'apparemment (je ne suis pas vendu là-dessus), c'est un cas d'utilisation important pour pouvoir importer une liaison dans de telle sorte que vous ne pouvez pas l'étendre. Il y a quelques propositions pour aller dans l'autre sens et remplacer import par using mais aucune d'entre elles n'a vraiment eu beaucoup de succès ( import semble plus fondamental).

Je pense que la différence réside dans :

  • import A: x, y # liaisons matérielles pour x et y partir de A

En supposant que la signification de "liaison dure" est telle que vous pouvez l'étendre sans préfixe de module, dans ma version, il n'y a pas de liaisons dures. Si vous souhaitez étendre, vous le préfixez exactement là où vous l'étendez. Pas d'instructions effrayantes import dans d'autres fichiers changeant la signification si quelque chose est une extension ou non.

et using A: x parce qu'apparemment (je ne suis pas convaincu par cela), c'est un cas d'utilisation important pour pouvoir importer une liaison de telle sorte que vous ne puissiez pas l'étendre.

Forcer le préfixe du module ne s'occupe-t-il pas de cela? Ou parlons-nous de non-modules comme :

module M
    x = 1
end

import M: x; x = 2 et using M: x; x = 2 donnent le même message d'avertissement donc je ne vois pas où est le problème...

Garder using A pour une facilité de plus de import A: ... semble un peu excessif à mon avis.

Forcer le préfixe du module ne s'occupe-t-il pas de cela?

Oui; si vous deviez qualifier des fonctions pour les étendre, ce point ne serait pas pertinent.

Continuer à utiliser A pour faciliter la surimportation A: ... semble un peu excessif à mon avis.

je le vois dans le sens opposé; faire passer les gens de using A (ce qui est agréable et court et nous y sommes tous assez habitués) à import A: ... juste pour satisfaire une exigence artificielle qu'il n'y ait qu'un seul mot-clé est excessif.

À la lecture du fil de discussion, il semble que l'intérêt principal d'avoir deux mots-clés soit de différencier les liaisons qui peuvent être étendues ou non (liaison dure). Dans cet esprit, je pense qu'il existe deux solutions viables:

  • 1 mot-clé + préfixe requis pour l'extension
  • deux mots clés, un pour une utilisation normale (sans extension) et un second pour une utilisation étendue, je suggère using et extending dans ce cas. import est bien, mais extending rend évidente la raison de l'existence d'un deuxième mot-clé

Dans les deux cas, je suggère que using devrait être tel qu'il est maintenant avec l'ajout de l'un des éléments suivants pour lier uniquement le module Foo :

  • using Foo: nothing (fonctionne maintenant)
  • using Foo: Foo (fonctionne maintenant)
  • using Foo: (peut être ajouté plus tard)

Ensuite, extending devrait se comporter de la même manière que using , la seule différence étant que vous pouvez étendre les liaisons apportées avec extending , et éventuellement interdire extending Foo afin qu'il ait être explicite.

Comportement actuel

| | rendre disponible (en utilisant) | rendre extensible (importation) |
| ------------------- | -------------------------- | ---------------------- |
| seul module | using module: module ou using module: nothing | import module |
| tout exporté | using module (effet secondaire : agit également comme import module ) | ? |
| choses particulières | using module: x,y | import module: x,y |

Suggestion

| | rendre disponible (en utilisant) | rendre extensible (importation) |
| ----------------- | ------------------------ | -------------------------- |
| seul module | using module | import module |
| tout exporté | using module: * | import module: * |
| choses particulières | using module: x,y | import module: x,y |

La bonne chose à ce sujet est qu'importer plus correspond à écrire plus. C'est-à-dire que vous commencez par using module et si vous souhaitez importer une variable directement dans l'espace de noms, vous ajoutez un : x au lieu de supprimer un nothing ou module . Cela signifie également que la chose la plus courte que vous tapez inclut le moins.

Vous pouvez également faire using: *,x pour rendre disponible tout ce qui est exporté et x qui n'a pas été exporté.

Compromis pour la rétrocompatibilité :

| | rendre disponible (en utilisant) | rendre extensible (importation) |
| ----------------- | ------------------------ | -------------------------- |
| seul module | using module: | import module: |
| tout exporté | using module: * | import module: * |
| choses particulières | using module: x,y | import module: x,y |

conservez environ using module et import module avec le comportement actuel pour la rétrocompatibilité, mais déconseillez-le.

@FelixBenning : import Module ne rend actuellement (par lui-même) rien extensible de plus que using Module , il charge simplement le code et apporte Module (et rien d'autre) dans l'espace de noms .

Juste pour refléter ce que j'ai dit sur le mou et pour qu'il ne disparaisse pas dans le trou du mou :

Je ne pense pas que faire using X: * la valeur par défaut pour rendre chaque chose exportée disponible par rapport à seulement using X rendra les gens nécessairement plus méfiants vis-à-vis de ce qu'ils importent. Je sais, montrer comment les autres le font est considéré comme une mauvaise forme, mais Python a essentiellement cette sémantique avec leurs import X et import X: * , pourtant leur écosystème est jonché de ces importations d'étoiles 🤷‍♂️ (et ils détestent ça) Je ne pense pas que le texte légèrement plus long que l'on doit taper empêche les gens de faire ce qu'ils considèrent comme le plus pratique : il suffit d'importer/utiliser tout et de laisser le compilateur le comprendre. C'est pourquoi je me méfie de la formule magique consistant à faire écrire explicitement cette étoile aux gens.

De plus, import module: * et using module: * ne sont pas disponibles pour la signification proposée. Il a déjà une signification car * est un identifiant Julia valide et peut être importé/utilisé comme + ou le mot mul .

@tpapp soit j'ai encore mal compris la documentation, soit import Module rend Module.x extensible. Tandis que using Module: x ne rend pas Module.x extensible. Par conséquent, import Module rend quelque chose disponible pour l'extension et using Module le fait aussi, c'est pourquoi j'ai noté que l'utilisation a cet effet secondaire.
grafik
(à partir de https://docs.julialang.org/en/v1/manual/modules/)

Peu importe lequel d'entre nous a raison - dans les deux cas, la situation actuelle est évidemment un gâchis si nous ne pouvons même pas comprendre ce que tout fait.

@mbauman bon point - j'avais oublié ça. Je ne me soucie pas vraiment des * , juste de la structure consistant à avoir using reflétant les choses que fait import avec la différence entre l'importation et l'utilisation, que les choses deviennent ou non extensibles. Donc, s'il existe un symbole plus approprié - all , __all__ , everything , exported , ... ? J'approuve totalement. Je pense juste que l'importation de plus devrait probablement être reflétée en tapant plus.

Mais si vous ne le souhaitez pas du tout, vous pouvez bien sûr aussi opter pour

| | rendre disponible (en utilisant) | rendre extensible (importation) |
| ----------------- | ------------------------ | -------------------------- |
| seul module | using module: module | import module: module |
| tout exporté | using module | import module |
| choses particulières | using module: x,y | import module: x,y |

ou

| | rendre disponible (en utilisant) | rendre extensible (importation) |
| ----------------- | ------------------------ | -------------------------- |
| seul module | using module | import module |
| tout exporté | using module: module | import module: module |
| choses particulières | using module: x,y | import module: x,y |

Mais quel que soit le résultat final, il doit être cohérent. Et pour le moment, ce n'est tout simplement pas le cas.

using A rend A.f extensible, _pas_ f par lui-même. Afin d'étendre _juste_ f sans déclarer à partir de quel module vous voulez l'étendre, vous devez explicitement import A: f . Sinon, vous devrez encore le qualifier.

Vérifiez ce qui suit pour la sémantique de using

julia> module A
        export f
        f() = "no args in A"
       end
Main.A

julia> f()
ERROR: UndefVarError: f not defined
Stacktrace:
 [1] top-level scope at REPL[2]:1

julia> using .A

julia> f()
"no args in A"

julia> f(1)
ERROR: MethodError: no method matching f(::Int64)
Closest candidates are:
  f() at REPL[1]:3
Stacktrace:
 [1] top-level scope at REPL[5]:1

julia> f(x) = "one arg where?"
ERROR: error in method definition: function A.f must be explicitly imported to be extended
Stacktrace:
 [1] top-level scope at none:0
 [2] top-level scope at REPL[6]:1

julia> A.f(x) = "one arg where?"

julia> f(1)
"one arg where?"

Et ceci pour la sémantique de import :

julia> module A
        export f
        f() = "no args in A"
       end
Main.A

julia> f()
ERROR: UndefVarError: f not defined
Stacktrace:
 [1] top-level scope at REPL[2]:1

julia> import .A

julia> f()
ERROR: UndefVarError: f not defined
Stacktrace:
 [1] top-level scope at REPL[4]:1

julia> A.f()
"no args in A"

julia> f(1)
ERROR: UndefVarError: f not defined
Stacktrace:
 [1] top-level scope at REPL[6]:1

julia> A.f(1)
ERROR: MethodError: no method matching f(::Int64)
Closest candidates are:
  f() at REPL[1]:3
Stacktrace:
 [1] top-level scope at REPL[7]:1

julia> f(x) = "one arg where?"
f (generic function with 1 method)

julia> f(1)
"one arg where?"

julia> A.f(1)
ERROR: MethodError: no method matching f(::Int64)
Closest candidates are:
  f() at REPL[1]:3
Stacktrace:
 [1] top-level scope at REPL[10]:1

julia> A.f(x) = "one arg where in A"

julia> A.f(1)
"one arg where in A"

@FelixBenning : oui, je pense que vous avez mal compris. FWIW, je pense que le "... rend Foo.x extensible" est une façon déroutante d'aborder la distinction --- vous pouvez toujours définir des méthodes pour des noms de fonction pleinement qualifiés. Ce qui se passe avec using Foo: x , c'est que Foo lui-même ne sera pas introduit dans l'espace de noms.

Soit dit en passant, en relisant ce sujet, je me demande si # 25306 nous a amenés à une sorte d'optimum local, et la seule décision qui reste est de savoir si nous avons besoin d'importations non extensibles dans l'espace de noms (actuellement using Foo: f ). Mais comme cela se brise, le chapitre du manuel bénéficierait peut-être d'une réécriture en attendant, de nombreux utilisateurs trouvent le tout déroutant.

Voici comment j'aborderais le statu quo dans la documentation :

  1. using M charge le module et place M et ses symboles exportés dans l'espace de noms. C'est le seul cas où les exportations comptent.
  2. une fois que vous avez M , vous pouvez utiliser des noms qualifiés comme M.y pour (a) accéder aux symboles non exportés et (b) ajouter des méthodes aux fonctions, qu'elles soient exportées ou non
  3. Julia vous empêche d'ajouter des méthodes aux fonctions à moins que vous n'utilisiez des noms qualifiés comme M.f , ou ...
  4. ... import M: f , alors vous pouvez simplement utiliser f lors de la définition des méthodes.
  5. import M et using M: x servent à amener seulement M ou x (et non M ), respectivement, dans l'espace de noms, respectivement. Certaines personnes aiment utiliser ces formulaires dans le code du package pour s'assurer que leurs espaces de noms restent propres (lien vers les guides de style pertinents ici).

L'ordre reflète plus ou moins les cas d'utilisation rencontrés avec une utilisation plus avancée. Tout ce qui précède s'applique aux sous-modules, avec M.A à la place de M .

Et ça:

  • using M apporte M dans la portée
  • using M: x apporte M.x dans la portée (comme x )
  • using M: ... apporte tous les symboles exportés de M dans la portée
  • using M: x, ... apporte tous les symboles exportés de M dans la portée ainsi que x (qui peuvent ne pas être exportés)
  • Il n'y a pas import . Vous devez utiliser le nom qualifié pour étendre une fonction. (Ou using M; const foo = M.foo comme on pourrait déjà le faire maintenant.)
  • Dans tout ce qui précède, M pourrait également être un sous-module, par exemple Foo.Bar et x pourraient également être x as y avec la signification actuelle.

Ou nous utilisons import au lieu de using , ce qui correspond alors à https://github.com/JuliaLang/julia/issues/8000#issuecomment -355960915.

Une utilisation très courante (surtout dans les "scripts", mais aussi les packages pour certains styles est)

using Foo, Bar, Baz

et en s'appuyant uniquement sur les symboles exportés. Il serait avantageux de garder cela le plus simple, peut-être aussi simple qu'il l'est maintenant.

@tpapp

Je pense que tu as mal compris. FWIW, je pense que le "... rend Foo.x extensible" est une façon déroutante d'aborder la distinction --- vous pouvez toujours définir des méthodes pour des noms de fonctions pleinement qualifiés.

D'accord, puis-je prolonger Foo.x après using Foo: x ? Car si oui, alors la documentation n'est pas complète (voir ma capture d'écran). Si non, alors j'ai parfaitement compris le fonctionnement de ces instructions et, de toute évidence, import Foo a fait quelque chose pour rendre Foo.x extensible. Par conséquent, il rend littéralement Foo.x disponible pour l'extension. Dans tous les sens de ces mots. Bien sûr, cela ne rend pas x disponible pour l'extension, mais c'est à cela que sert import Foo: x

D'accord, puis-je prolonger Foo.x après using Foo: x ?

Sauf si vous apportez Foo dans l'espace de noms d'une manière ou d'une autre (par exemple, using Foo ).

J'ai parfaitement compris le fonctionnement de ces déclarations

Je ne suis pas tout à fait sûr de cela - cependant, si vous avez des questions sur les modules et les espaces de noms, veuillez utiliser le forum Discourse.

de toute évidence, import Foo a fait quelque chose pour rendre Foo.x extensible

Seulement dans le sens où vous devez pouvoir faire référence à une fonction en utilisant un nom qualifié avant d'y ajouter des méthodes.

alors la documentation n'est pas complète

Je pense que oui, d'une manière un peu bizarre. Dans cet exemple particulier, si tout ce que vous faites est using MyModule: x, p ; alors aucune méthode n'est disponible pour l'extension, donc le tableau est correct.

Je suis d'accord qu'il pourrait être mieux écrit, comme je l'ai dit plus haut. Beaucoup de gens qui ne sont pas habitués aux espaces de noms trouvent cela déroutant. Et, TBH, tout le cirque using / import est légèrement déroutant, d'où ce problème.

@tpapp

D'accord, voici le problème : il n'est absolument pas évident que vous puissiez étendre chaque fonction avec le nom complet si le module se trouve dans l'espace de noms. Je sais qu'il s'agit d'un comportement actuel, mais je ne pense pas que quelqu'un qui ne le sait pas déjà puisse le supposer. Surtout parce qu'être dans l'espace de noms ne signifie pas toujours qu'il est également extensible. Si je fais using module:x je ne peux pas étendre x . Bien que je puisse étendre x , si j'utilise import module:x . Par conséquent, il est raisonnable de supposer que la différence entre using et import est de savoir si vous pouvez ou non étendre les fonctions importées.

En utilisant cette hypothèse, il serait logique que using module n'autorise que l'utilisation de module.x mais n'autorise pas l'extension de module.x . Tandis que import module autoriserait l'extension de module.x . Donc, si vous prenez cette hypothèse et lisez la documentation, vous constaterez que deux choses à ce sujet sont fausses.

  1. using module vous permet d'étendre module.x . Par conséquent, du point de vue des apprenants, using module a pour effet secondaire qu'il agit également comme import module - c'est-à-dire qu'il rend module.x extensible. Et rendre les choses extensibles est une chose import , pas une chose using
  2. contrairement à import , using ne rend pas seulement module disponible, mais plutôt tout ce qu'il contient.

C'est donc ce que j'ai essayé de représenter avec mon tableau. Si deux mots différents sont utilisés (importation/utilisation), ils doivent faire deux choses différentes et cela doit être clair. Si using permet parfois une extension et d'autres fois, ce n'est pas un comportement prévisible.

Une bonne alternative consiste à supprimer complètement l'importation comme le suggère @martinholters . Vous ne pouvez tout simplement pas avoir deux mots qui sont utilisés au hasard pour certaines choses. Si import signifie rendre les choses extensibles lors de l'importation de certaines fonctions, alors que using module: foo ne le permet pas, alors le même comportement devrait se produire lorsque vous n'incluez que module dans l'espace de noms.

Ce n'est pas parce que vous pouvez tout étendre par son nom complet dès maintenant si le module se trouve dans l'espace de noms que c'est une chose évidente ou simple.

Soit être dans l'espace de noms est suffisant, alors je devrais également pouvoir étendre x si je l'ai inclus dans l'espace de noms avec using module:x ou être dans l'espace de noms n'est pas suffisant, et alors je devrais aussi ne pas pouvoir étendre module.x lors de l'utilisation de la commande using .
Ou troisième option : il n'y a pas import et vous devez simplement étendre les fonctions avec leur nom complet tout le temps.

Il n'est absolument pas évident que vous puissiez étendre chaque fonction avec le nom complet si le module est dans l'espace de noms.

Je suis d'accord, c'est pourquoi je plaide pour une réécriture des docs. Cependant, cela est tangent au problème actuel. Il serait préférable de garder (1) les propositions d'amélioration de la documentation sur l'API actuelle et (2) les propositions de refonte séparées, même si les deux sont quelque peu liées. Je prévois de faire un PR pour (1) bientôt.

@tpapp Vous ne pouvez pas documenter quelque chose qui n'a intrinsèquement aucun sens. Aucune de ces documentations n'est correcte :

  1. "Vous pouvez étendre tout ce qui se trouve dans l'espace de noms" (parce que using module:x ne vous permet pas d'étendre x )
  2. "Être dans l'espace de noms n'est pas suffisant pour l'extension, c'est pourquoi des mots séparés using et import existent" (faux car pour les noms complets, il suffit en fait d'être dans l'espace de noms - arrêtant cela être suffisant était ma suggestion)
  3. "Vous pouvez étendre n'importe quoi dans l'espace de noms par son nom complet" (ce qui nécessiterait import module:x pour cesser d'exister pour être la pleine vérité - proposition de @martinholters )

N'importe lequel d'entre eux serait acceptable. Mais rien de tout cela n'est réellement la réalité. La réalité est actuellement qu'il existe deux mots différents utilisés pour "importer des choses", mais ils n'ont pas réellement de cas d'utilisation distinct. C'est comme si une boucle for se comportait parfois comme une boucle while si vous parcourez des booléens. Aucune quantité de documentation ne rendra cela sans confusion

Vous ne pouvez pas documenter quelque chose qui, par nature, n'a pas de sens.

L'API actuelle des modules est bien définie, elle peut donc être documentée (elle l'est déjà, je pense juste qu'elle devrait être meilleure).

Aucune de ces documentations n'est correcte :

Veuillez avoir la gentillesse d'attendre mon PR (ou celui de quelqu'un d'autre) et de commenter le texte réel qui s'y trouvera. Présenter une documentation hypothétique et prétendre ensuite qu'elle est incorrecte ne fait qu'ajouter du bruit à cette discussion.

@tpapp j'aurais peut-être dû dire,

vous ne pouvez pas documenter quelque chose d'une manière non confuse qui n'a pas de sens en soi

Je veux dire, dans un sens, le code est toute la documentation dont vous avez besoin, n'est-ce pas ? Qu'est-ce qui ne va pas avec ça? Le temps qu'il faut pour le digérer. Et je ne vois actuellement aucun moyen de décrire brièvement comment cela fonctionne, car il est truffé d'exceptions. Des exceptions qui ne devraient pas exister en premier lieu.

Vous ne voyez vraiment pas ce que j'essaie de transmettre?

Je pense que tout le monde s'accorde à dire que la situation actuelle est inutilement complexe et insuffisamment documentée. Et nul doute que la simplification de la machinerie facilitera sa documentation. Mais une telle simplification sera cassante, donc ne peut pas être faite avant 2.0. Donc, est-ce que vous dites que l'amélioration de la documentation avant cela n'aurait pas de sens ? Alors je ne suis pas d'accord. Je vois deux problèmes distincts : la simplification à faire avec la version 2.0 (sur laquelle porte ce problème) qui inclura (espérons-le) les mises à jour de documentation nécessaires et l'amélioration de la documentation du fonctionnement actuel qui, en lisant ce fil, semble mal nécessaire mais est un autre problème .

Après avoir fait #38271, je pense que les questions en suspens sont

Comment gérer l'ajout de méthodes aux fonctions dans d'autres modules

  1. toujours exiger des noms complets ( Foo.bar() = ... ),
  2. également autoriser uniquement lorsque le symbole est dans la portée ( using Foo: bar; bar() = ... )
  3. (2), mais introduire dans la portée d'une manière spéciale ( statu quo , import Foo: bar; bar() = ... )

Quelle syntaxe gère les listes d'exportation et uniquement le nom du module

  1. using Foo apporte tous les symboles exportés dans Foo à la portée, import Foo juste le module ( statu quo )
  2. using Foo: ... ou une autre syntaxe similaire, puis using Foo juste le module.

(1|2) & 2 permettraient l'unification en un seul mot-clé, using ou import , au prix de la perte d'une seule ligne

using LinearAlgebra, Random, StaticArrays

Je ne suis pas sûr que cela en vaille la peine, même sans tenir compte du changement de rupture.

Ce n'est pas l'un de ces problèmes qui offrent une solution propre que la conception originale a manquée de justesse ; il y a des compromis. J'attendrais de voir si une meilleure documentation peut améliorer l'expérience utilisateur tout en conservant la configuration actuelle (1.0).

Pour 2.0, je pense qu'un simple nettoyage de la syntaxe pour être plus cohérent et descriptif de ce qui se passe réellement serait bien. Quelque chose comme:

| Avant | Après |
|-|-|
| using Foo | useall from Foo |
| import Foo | use Foo |
| using Foo: a | use a from Foo |
| import Foo: a et import Foo.a | extend a from Foo |

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

Questions connexes

felixrehren picture felixrehren  ·  3Commentaires

yurivish picture yurivish  ·  3Commentaires

manor picture manor  ·  3Commentaires

StefanKarpinski picture StefanKarpinski  ·  3Commentaires

omus picture omus  ·  3Commentaires