Julia: Entfernen Sie require, import und eventuell importall und führen Sie sie in using zusammen

Erstellt am 14. Aug. 2014  ·  72Kommentare  ·  Quelle: JuliaLang/julia

Aus der Mailingliste:

Stefan:

Ich denke, wir sollten den Import ganz abschaffen und einfach haben

mit Foo
mit Foo:bar

Die erste würde eine Bindung für Foo laden und erstellen, ihre Exporte als "weiche" Bindungen verfügbar machen (was die Verwendung jetzt tut). Die zweite würde auch eine Bindung für Foo laden und erstellen und bar als "harte" Bindung verfügbar machen (was import jetzt tut)."

Scheint, dass dies für Neuankömmlinge immer noch ein ziemlicher Punkt der Verwirrung ist.

breaking design modules speculative

Hilfreichster Kommentar

Ich mag die Symmetrie von import und export . (Wie jemand irgendwo darauf hingewiesen hat.)

Alle 72 Kommentare

Wir bräuchten irgendwie auch die import Foo -Funktionalität, wo man nur die Foo bekommt und sonst nichts

using Foo: ?

using Foo: Foo ?

So binden Sie Foo an das Foo-Modul (und sonst nichts):
import Foo
Um Foo an Modul Foo, x an Foo.x, y an Foo.y zu binden
import Foo: x, y
So binden Sie Foo an das Modul Foo, und alle exportierten Namen von Foo werden unqualifiziert gebunden:
import Foo: *

Dies könnte stattdessen using sein, aber ich habe das Gefühl, dass dies eher im Sinne von import ist.

Dadurch wird auch die Unterscheidung aufgehoben, etwas in den Anwendungsbereich zu bringen und es für die Methodenerweiterung verfügbar zu machen. Ich persönlich finde das gut und macht das Modulsystem verständlicher, aber ich wollte sichergehen, dass wir es ansprechen.

Es gibt starke Argumente dafür, dass ein Konstrukt, das alle exportierten Bindungen eines Moduls verfügbar macht, sie zu weichen ( using ) Bindungen und nicht zu harten ( importall ) Bindungen machen sollte. Angenommen, Modul A verwendet Modul B und definiert foo(::Any) . Wenn eine spätere Version von Modul B auch foo(::Any) definiert und exportiert, möchten Sie nicht, dass sie sich gegenseitig verprügeln. Sie möchten auch nicht, dass Modul B foo(::Int) definiert und dass Modul A manchmal diese Methode anstelle der von ihr definierten Methode aufruft, weil sie spezifischer ist, oder dass Sie alle gewünschten Bezeichner von Modul B auflisten müssen um den Import einer einzelnen widersprüchlichen Kennung zu vermeiden.

Aber wenn Sie die zu importierenden Bezeichner explizit aufgelistet haben, gibt es nie einen Grund, eine weiche Bindung anzugeben. Entweder Sie definieren keine neuen Methoden eines Bezeichners, in diesem Fall haben harte und weiche Bindungen ein identisches Verhalten, oder Sie definieren neue Methoden davon, in diesem Fall ist eine weiche Bindung gleichbedeutend mit keiner Bindung und wenn Sie möchten Bei diesem Verhalten sollten Sie die Kennung einfach aus der Liste entfernen.

Kurz gesagt, ich mag den Vorschlag von @StefanKarpinski . Konzeptionell benötigen wir sowohl harte als auch weiche Bindungen, aber wir können alle nützlichen Verhaltensweisen mit einem einzigen Schlüsselwort erhalten.

Ich verstehe dein Argument. In diesem Fall gefällt mir Ihr Vorschlag, dass using Foo: (mit dem Doppelpunkt, aber ohne Elemente) konzeptionell konsistent erscheint. Der Doppelpunkt zeigt an, dass Sie die einzufügenden Symbole einschränken, aber Sie listen einfach keine auf.

Das leere abschließende : sieht etwas komisch aus, aber ich denke, die Leute würden sich daran gewöhnen

Das Problem mit dem leeren abschließenden Doppelpunkt ist, dass wir derzeit in der nächsten Zeile nach einem Namen suchen – mit anderen Worten, using Foo: wird als unvollständig angesehen.

Verwandt: #4600

Ich weiß, dass die aktuelle Julia-Praxis darin besteht, alles in die zu importieren
Namespace des aktuellen Moduls, aber ich denke wirklich, dass es noch geben sollte
eine einfache und natürliche Möglichkeit sein, nur den Namen eines Moduls zu importieren.

Ich denke auch, dass die aktuelle Überladung der Verwendung von Foo und der Verwendung von Foo.Bar ist
problematisch; es sieht in Foo, aber nicht in Foo.Bar aus (es sei denn, Foo.Bar ist es
ein Modul?)

Ich denke in Stefans Vorschlag wird dies mit adressiert

mit Foo: Foo

Am Donnerstag, den 14. August 2014, schrieb toivoh [email protected] :

Ich weiß, dass die aktuelle Julia-Praxis darin besteht, alles in die zu importieren
Namespace des aktuellen Moduls, aber ich denke wirklich, dass es noch geben sollte
eine einfache und natürliche Möglichkeit sein, nur den Namen eines Moduls zu importieren.

Ich denke auch, dass die aktuelle Überladung der Verwendung von Foo und der Verwendung von Foo.Bar ist
problematisch; es sieht in Foo, aber nicht in Foo.Bar aus (es sei denn, Foo.Bar ist es
ein Modul?)


Antworten Sie direkt auf diese E-Mail oder zeigen Sie sie auf GitHub an
https://github.com/JuliaLang/julia/issues/8000#issuecomment -52202142.

@kmsquire aber was passiert dann, wenn es ein Foo-Modul innerhalb des Foo-Moduls gibt? Das ist leider zweideutig.

Das ist ein Designfehler (und verursacht eine Warnung), also spielt es keine Rolle

Mir gefällt die von @ssfrr oben vorgeschlagene Version am besten.

Gibt es eine zugrunde liegende Konvention, dass Pakete einen und nur einen Modul-Einstiegspunkt haben? -- Zu import Foo bedeutet Importmodul Foo des Pakets Foo . Jedes andere Modul muss ein Untermodul von Foo sein. Das bedeutet, dass es eine implizite Entsprechung zwischen dem Paket Foo, Foo.jl im Paket und den darin enthaltenen module Foo gibt.

I ask b/c Ich frage mich, ob import auch für den lokalen/relativen Import verwendet werden könnte. Angenommen, ein Projekt hat src/foo.jl und src/bar.jl dann in foo.jl :

import bar

würde aus src/bar.jl importieren. Dies ist eine Verbesserung gegenüber der Verwendung include , da es innerhalb des Modulsystems arbeitet.

Ja, es wird erwartet, dass Pakete, Paketeintrittspunkte und Module eins zu eins sind. Sie können diese Konvention bei Bedarf brechen, aber das ist nicht unbedingt nötig, da Module nur der Benennung dienen und keine funktionalen Einheiten sind. Die Idee, die Sie ansprechen, ist #4600, außer dass die Syntax für einen relativen Import import .bar ist, während import bar ein absoluter Import ist.

@StefanKarpinski Sucht import .bar tatsächlich nach "bar.jl" im lokalen Verzeichnis? Ich hatte den Eindruck, dass sich import .Bar nur auf ein Untermodul eines bereits aktuellen übergeordneten Moduls bezieht.

UPDATE: Duh, das schlagen Sie in #4600 vor. Es tut uns leid.

:+1:. Wenn wir dies tun wollen, wäre 0,4 ein guter Zeitpunkt dafür.

:+1: Bitte machen Sie weiter und bereinigen Sie etwas davon (für 0,4!)

Anstatt zwei Importmechanismen zu haben, um zwischen Erweitern oder Nichterweitern zu unterscheiden, warum nicht die Erweiterung auf der Erweiterungsseite selbst über ein Schlüsselwort oder eine Anmerkung in der Funktionsdefinition signalisieren, z. B. Schlüsselwort vor Funktion überschreiben? Ähnlich wie @Override Annotation in Java.

Dies hat den Vorteil, dass man deutlich sieht, dass die Funktion eine andere überschreibt (und daher eine Methode ist).

Das ist schon möglich, @ssagaert. Sie tun dies, indem Sie den Modulnamen explizit in die Funktionsdefinition schreiben (z. B. Base.print(…) = … ), und es scheint ein Stil zu sein, dem viele Leute zustimmen. Der einzige Knackpunkt ist, dass die Syntax nicht für alle möglichen Namen funktioniert (wie .+ usw.).

(Nebenbei, achten Sie bitte darauf, Makros mit Backticks `` einzuschließen, um zu verhindern, dass andere GitHub-Benutzer belästigt werden).

:+1: zu @ssagaerts Vorschlag, @override zu verwenden, die ursprüngliche Fehlermeldung, wenn man versucht, eine Methode zu erweitern, ohne sie zuerst zu importieren, lautet wie folgt:

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

Also könnte vielleicht @extend besser geeignet sein? Ich bin kein englischer Muttersprachler, aber nach meinem Verständnis bedeutet _override_ so etwas wie: stornieren, ungültig machen, ungültig machen, negieren, annullieren, annullieren, abbrechen usw.

Ich denke, das ist deutlicher:

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

Als:

import Base: show

# ... several lines here

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

@Ismael-VC Base.show(...) = ... funktioniert bereits, ohne etwas zu importieren. import ist nur erforderlich, wenn show(...) = ... Base.show verlängern soll.

@Ismael-VC Override-Wort war nur ein Vorschlag. Es kann erweitern oder irgendetwas anderes Sinnvolles sein. Außerdem sollte es kein @ geben, da dies in Julia bedeutet, dass es sich um ein Makro handelt ( @Override bezieht sich auf Java, wo es sich um eine Anmerkung handelt).

@simonster danke, das wusste ich nicht!

@ssagaert also meinst du ein Schlüsselwort? Ich habe so etwas versucht, aber ich sauge immer noch an Makro-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

Ich weiß, dass dies nicht allgemein ist, aber ich kann keinen Ausdruck erstellen, der zurückgibt, was parse tut:

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))))

Ich denke, dass ein allgemeines und funktionierendes @extend -Makro die Semantik des Importmechanismus nicht ändern würde.

@Ismael-VC habe ich, aber ich mag auch den 'Trick' von @mbauman .

Ich denke, es könnte nett sein, . alleine verwenden zu können, um "dies" zu bedeuten. Sie könnten also Ausdrücke schreiben wie:
import Base: . (bedeutet Importieren Base.Base )
oder
using ..

Ich bin mir ziemlich sicher, dass dies jedoch https://github.com/JuliaLang/julia/pull/11891#issuecomment -116098481 erfordern würde. Vielleicht reicht es aus, Leerzeichen vor dem . zuzulassen, aber nicht danach, um den Mehrdeutigkeitsfall aufzulösen?

Ich glaube, require ist bereits veraltet. Es könnte nett sein, mit 1.0 eine flexiblere Punktnotation beim Importieren von Modulen hinzuzufügen, aber ich bezweifle, dass wir hier bis 0.6 Feature Freeze irgendetwas ändern werden

Nach einer Reihe von Diskussionen dazu neige ich gestern zu etwas wie den Vorschlägen in https://github.com/JuliaLang/julia/issues/8000#issuecomment -52142845 und https://github.com/JuliaLang/julia/ Ausgaben/8000#AusgabeKommentar -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

Eine Alternative wäre, import statt using zu behalten:

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

Die allgemeine Regel dafür, ob eine Bindung hart oder weich ist, wäre einfach: Jeder explizit angeforderte Name ist eine harte Bindung, jede implizit gegebene Bindung ist weich.

Bearbeiten: Es besteht der Wunsch, diesem Schema eine Kurzschrift für import A; import A: ... hinzuzufügen, was ungefähr dem entspricht, was using A derzeit tut (der einzige Unterschied besteht darin, dass using A derzeit A weich importiert using A sein oder es wurden import A... vorgeschlagen.

Ja, das finde ich auch einen guten Vorschlag. Grundsätzlich kommt es darauf an, ob man using oder import bevorzugt.

Nachtrag, wir könnten using A als Abkürzung für import A; import A: ... beibehalten – verbunden mit der Beseitigung des aktuellen Verhaltens, dass jedes Modul eine exportierte Bindung für sich selbst hat, weshalb using A verursacht es soll eine weiche Bindung zu A verfügbar sein.

Ich wäre ziemlich enttäuscht, wenn wir nach all dem immer noch mehrere Keywords hätten.

Ich mag die Symmetrie von import und export . (Wie jemand irgendwo darauf hingewiesen hat.)

Immer "harte Bindungen" zu machen, scheint nicht die sicherste Standardeinstellung in Bezug auf die Methodenerweiterung zu sein. Es gab den verwandten, aber etwas separaten Vorschlag, eine Modulqualifikation für die Methodenerweiterung zu fordern, die die Notwendigkeit für "harte Bindungen" beseitigen könnte.

Dazu werden auch feste Bindungen benötigt:

import Package: x
x = 1   # gives an error

Und entscheidend ist, dass dieser Vorschlag nicht immer harte Bindungen machen würde – nur für Dinge, die Sie ausdrücklich auflisten. Zu verlangen, dass man import statt using sagt, bringt nicht viel Sicherheit.

Die Sicherheit kommt von den meisten Orten, an denen keine Methodenerweiterung durchgeführt wird, die mit einer weichen Bindung gut auskommen kann. Ich denke, wir sollten immer noch eine Möglichkeit haben, nach einer bestimmten Soft-Bindung zu fragen, ohne alle Exporte eines Pakets importieren zu müssen (oder eine bestimmte Soft-Bindung zu erhalten, die nicht exportiert wird).

Haben wir immer noch die Warnung zum Überschreiben einer importierten Bindung?

Ich halte die Forderung nach Modulqualifikation für eine gute Idee. Um jetzt zu wissen, ob eine Funktion eingeführt oder eine Methode erweitert wird, müssen Sie den gesamten Inhalt des Pakets nach einer import A: func -Anweisung durchsuchen.

Haben wir immer noch die Warnung zum Überschreiben einer importierten Bindung?

Ich glaube, es ist jetzt tatsächlich ein Fehler.

Es kursiert ein weiterer Vorschlag, der beide Schlüsselwörter beibehält, aber die Dinge dennoch ein wenig vereinfacht:

  1. Fügen Sie die Syntax import A: ...
  2. Verwerfen Sie using A:
  3. Erfordern Sie in using A.B , dass A.B ein Modul ist, und dokumentieren Sie, dass es sich um eine Abkürzung für import A.B; import A.B: ... handelt.

Auf diese Weise kann alles mit nur import erledigt werden, aber using X sind bequem verfügbar. Dies wäre auch besonders einfach zu wechseln.

Übrigens, using sieht inkonsistent aus, wie ich hier gepostet habe . Wenn wir using behalten, sollte es nach Möglichkeit in use umbenannt werden.
Ich denke, wir sollten beim Erweitern von Funktionen eine Modulquantifizierung fordern, da ihre Bedeutung viel klarer ist als das Importieren-dann-Erweitern-Muster.

Mein bevorzugter Ansatz:

  • Erfordert ein explizites Modulpräfix für die Erweiterung
  • Wenn Sie nur den Modulnamen und nicht seine Symbole möchten, verwenden Sie using A: A
  • Entfernen Sie import und die Art der Bindung, die es erstellt

Dinge, die passieren müssten, um https://github.com/JuliaLang/julia/issues/8000#issuecomment -327512355 zu implementieren:

  • ändere das Verhalten von using A auf harten Import A
  • Unterstützung für using A: x entfernen
  • Entfernen Sie die Unterstützung für using A.x , wobei x kein Submodul ist
  • Entfernen Sie die Unterstützung für import A.x , wobei x kein Submodul ist
  • Hinzufügen von Unterstützung für die ... -Syntax zu import

using A: x wird oft verwendet und ist sehr nützlich. Sie sagen, dass Sie x in Ihrem Namensraum haben möchten, ihn aber nicht erweitern möchten. In import A: x sagen Sie, dass Sie in der Lage sein möchten, x zu verlängern. Es gibt eine sinnvolle Unterscheidung zwischen der Verfügbarkeit einer Funktion und der Möglichkeit, sie zu erweitern.

Wenn ich darüber nachdenke, würde ich sagen, dass das größte Problem hier darin besteht, dass using A.B zwei Dinge tut: Wenn B ein Modul ist, importiert es alle seine Exporte weich und ansonsten nur weiche Importe B . Ich denke, wir sollten das einfach beheben und using A.B nur für Module zulassen und using A: a, b haben, um bestimmte Bindungen einzeln weich zu importieren.

Ich würde es vorziehen, wenn es eine Möglichkeit gäbe, import A: x zu schreiben, anstatt dass es import A.x entspricht.

Ich stimme für import A: x , da wir das auch tun können; import A: x, y, @z aber import A.x, A.y, a.@z würde hässlich aussehen.

Bedeutet das Entfernen aus 1.0, dass uns sowohl using als auch import für 1.0 übrig bleiben? Das ist meiner Meinung nach etwas schade.

Wie wäre es mit:

  • Modulpräfix für Erweiterung erzwingen. Dadurch wird der Code klarer, dass eine Erweiterung beabsichtigt ist, anstatt dass sie versehentlich von einem Import in eine andere Datei stammt.
  • using A wird zu import A: ...
  • using A.X ( X ist ein Modul) wird zu import A.X: ...
  • using A: X ( X ist kein Modul) wird zu import A: X
  • import A: X wird nicht geändert, aber Sie können X nicht automatisch verlängern (siehe erster Punkt)
  • Löschen Sie das Schlüsselwort using

Übersehe ich einen Anwendungsfall? Vielleicht wurde das schon vorgeschlagen...

Was mir gefällt, wenn ich das Modul beim Erweitern explizit anspreche, ist, dass die Erweiterung viel lokaler wird. Wenn eine Methode erweitert wird, ist es derzeit üblich, den Import sehr nahe an den Anfang des Moduls zu stellen (der sich möglicherweise in einer völlig anderen Datei befindet!). Wenn die erweiterte Methode gelöscht wird, wird normalerweise die import-Anweisung vergessen.

Ich denke, das ist im Wesentlichen dasselbe wie mein Vorschlag oben, der ziemlich viel Unterstützung erhielt. @JeffBezanson möchte wirklich using A zumindest für die Benutzerfreundlichkeit und using A: x beibehalten, da es anscheinend (ich bin davon nicht überzeugt) ein wichtiger Anwendungsfall ist, um eine Bindung importieren zu können so dass man es nicht verlängern kann. Es gibt einige Vorschläge, in die andere Richtung zu gehen und import durch using zu ersetzen, aber keiner von ihnen hat wirklich viel Anklang gefunden ( import scheint grundlegender zu sein).

Ich denke der Unterschied liegt in:

  • import A: x, y # Hard Bindings für x und y aus A

Angenommen, die Bedeutung von "harter Bindung" ist so, dass Sie sie ohne Modulpräfix erweitern können, gibt es in meiner Version keine festen Bindungen. Wenn Sie erweitern möchten, setzen Sie das Modul genau dort voran, wo Sie es erweitern. Keine gespenstischen import -Anweisungen in anderen Dateien, die die Bedeutung ändern, ob etwas eine Erweiterung ist oder nicht.

und using A: x , weil es anscheinend (ich bin davon nicht überzeugt) ein wichtiger Anwendungsfall ist, eine Bindung so importieren zu können, dass Sie sie nicht erweitern können.

Geht es nicht darum, das Modulpräfix zu erzwingen? Oder sprechen wir über Nicht-Module wie:

module M
    x = 1
end

Sowohl import M: x; x = 2 als auch using M: x; x = 2 geben die gleiche Warnmeldung aus, also sehe ich nicht, wo das Problem liegt ...

using A der Einfachheit halber über import A: ... zu halten scheint meiner Meinung nach etwas übertrieben.

Geht es nicht darum, das Modulpräfix zu erzwingen?

Jawohl; Wenn Sie Funktionen qualifizieren müssten, um sie zu erweitern, wäre dieser Punkt irrelevant.

Die Verwendung von A zur Vereinfachung des Überimports von A: ... scheint meiner Meinung nach etwas übertrieben.

Ich sehe es umgekehrt; Leute dazu zu bringen, von using A (was nett und kurz ist und an das wir alle ziemlich gewöhnt sind) zu import A: ... zu wechseln, nur um eine künstliche Anforderung zu erfüllen, dass es nur 1 Schlüsselwort gibt, ist übertrieben.

Aus dem Lesen des Threads geht hervor, dass der Hauptwert von zwei Schlüsselwörtern darin besteht, zwischen Bindungen zu unterscheiden, die erweitert werden können oder nicht (harte Bindung). In Anbetracht dessen denke ich, dass es zwei praktikable Lösungen gibt:

  • 1 Schlüsselwort + Präfix zum Erweitern erforderlich
  • zwei Schlüsselwörter, eines für die normale (nicht erweiterte) Verwendung und ein zweites für die erweiterte Verwendung. Ich schlage in diesem Fall using und extending vor. import ist in Ordnung, aber extending macht den Grund für die Existenz eines zweiten Schlüsselworts offensichtlich

In jedem Fall schlage ich vor, dass using so sein sollte, wie es jetzt ist, mit dem Hinzufügen eines der folgenden Elemente, um nur das Modul Foo zu binden:

  • using Foo: nothing (funktioniert jetzt)
  • using Foo: Foo (funktioniert jetzt)
  • using Foo: (kann später hinzugefügt werden)

Dann sollte sich extending identisch zu using verhalten, mit dem einzigen Unterschied, dass Sie mit extending eingeführte Bindungen erweitern und möglicherweise extending Foo verbieten können, damit dies der Fall ist explizit sein.

Aktuelles Verhalten

| | verfügbar machen (mittels) | erweiterbar machen (importieren)|
| ------------------- | -------------------------- | ---------------------- |
| nur Modul | using module: module oder using module: nothing | import module |
| alles exportiert | using module (Nebenwirkung: verhält sich auch wie import module ) | ? |
| besondere Dinge | using module: x,y | import module: x,y |

Anregung

| | verfügbar machen (mittels) | erweiterbar machen (importieren) |
| ------------------ | ------------------------ | -------------------------- |
| nur Modul | using module | import module |
| alles exportiert | using module: * | import module: * |
| besondere Dinge | using module: x,y | import module: x,y |

Das Schöne daran ist, dass das Importieren von mehr dem Schreiben von mehr entspricht. Das heißt, Sie beginnen mit using module und wenn Sie eine Variable direkt in den Namespace importieren möchten, fügen Sie ein : x hinzu, anstatt ein nothing oder module zu entfernen. Es bedeutet auch, dass das Kürzeste, was Sie eingeben, das Wenigste enthält.

Sie können auch using: *,x tun, um alles Exportierte verfügbar zu machen, und x , was nicht exportiert wurde.

Kompromiss für Abwärtskompatibilität:

| | verfügbar machen (mittels) | erweiterbar machen (importieren) |
| ------------------ | ------------------------ | -------------------------- |
| nur Modul | using module: | import module: |
| alles exportiert | using module: * | import module: * |
| besondere Dinge | using module: x,y | import module: x,y |

Behalten Sie etwa using module und import module mit dem aktuellen Verhalten für die Abwärtskompatibilität, aber verwerfen Sie es.

@FelixBenning : import Module macht derzeit (von sich aus) nichts Erweiterbares mehr als using Module , es lädt nur den Code und bringt Module (und sonst nichts) in den Namespace .

Nur um zu spiegeln, was ich in Slack gesagt habe und damit es nicht im Slack Hole verschwindet:

Ich glaube nicht, dass using X: * als Standardeinstellung dafür verwendet wird, dass alle exportierten Dinge verfügbar gemacht werden, anstatt nur using X , die Leute werden zwangsläufig vorsichtiger gegenüber dem, was sie importieren. Ich weiß, es wird als schlechte Form angesehen, darauf hinzuweisen, wie andere es tun, aber Python hat im Grunde diese Semantik mit ihren import X und import X: * , doch ihr Ökosystem ist übersät mit diesen Star-Importen 🤷‍♂️ (und sie hassen das bekanntermaßen) Ich glaube nicht, dass der geringfügig längere Text, den man eingeben muss, die Leute davon abhält, das zu tun, was sie für das bequemste halten: einfach alles importieren/verwenden und den Compiler es herausfinden lassen. Deshalb bin ich vorsichtig mit der Wunderwaffe, die Leute dazu zu bringen, diesen Stern explizit zu schreiben.

Außerdem sind import module: * und using module: * für die vorgeschlagene Bedeutung nicht verfügbar. Es hat bereits eine Bedeutung, da * eine gültige Julia-Kennung ist und genau wie + oder das Wort mul importiert/verwendet werden kann.

@tpapp entweder habe ich die Dokumentation wieder falsch verstanden, oder import Module macht Module.x erweiterbar. Während using Module: x Module.x nicht erweiterbar macht. Daher stellt import Module etwas zur Erweiterung zur Verfügung und using Module tut dies auch, weshalb ich bemerkt habe, dass using diesen Nebeneffekt hat.
grafik
(von https://docs.julialang.org/en/v1/manual/modules/)

Es ist eigentlich egal, wer von uns Recht hat – in beiden Fällen ist die aktuelle Situation offensichtlich ein Chaos, wenn wir nicht einmal herausfinden können, was alles tut.

@mbauman guter Punkt - das habe ich vergessen. Ich interessiere mich nicht wirklich für die * , nur die Struktur, dass using die Dinge widerspiegelt, die import tut, mit dem Unterschied zwischen Import und Verwendung, ob Dinge erweiterbar werden oder nicht. Wenn es also ein passenderes Symbol gibt - all , __all__ , everything , exported , ...? Ich bin dafür. Ich denke nur, dass das Importieren von mehr wahrscheinlich durch das Eingeben von mehr widergespiegelt werden sollte.

Aber wer das gar nicht möchte, der könnte natürlich auch darauf setzen

| | verfügbar machen (mittels) | erweiterbar machen (importieren) |
| ------------------ | ------------------------ | -------------------------- |
| nur Modul | using module: module | import module: module |
| alles exportiert | using module | import module |
| besondere Dinge | using module: x,y | import module: x,y |

oder

| | verfügbar machen (mittels) | erweiterbar machen (importieren) |
| ------------------ | ------------------------ | -------------------------- |
| nur Modul | using module | import module |
| alles exportiert | using module: module | import module: module |
| besondere Dinge | using module: x,y | import module: x,y |

Aber was auch immer das Endergebnis ist, es sollte konsistent sein. Und im Moment ist es einfach nicht.

using A macht A.f erweiterbar, _nicht_ f alleine. Um _nur_ f zu erweitern, ohne anzugeben, von welchem ​​Modul aus Sie es erweitern möchten, müssen Sie explizit import A: f angeben. Andernfalls müssen Sie es noch qualifizieren.

Überprüfen Sie Folgendes auf die Semantik von 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?"

Und dies für die Semantik von 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 : Ja, ich glaube, du hast es falsch verstanden. FWIW, ich denke, dass das "... macht Foo.x erweiterbar" eine verwirrende Herangehensweise an die Unterscheidung ist - Sie können immer Methoden für vollständig qualifizierte Funktionsnamen definieren. Was mit using Foo: x passiert, ist, dass Foo selbst nicht in den Namespace gebracht wird.

Übrigens frage ich mich beim erneuten Lesen dieses Themas, ob #25306 uns zu einer Art lokalem Optimum geführt hat, und die einzige Entscheidung, die bleibt, ist, ob wir nicht erweiterbare Importe in den Namensraum benötigen (derzeit using Foo: f ). Da das aber kaputt geht, das Manual-Kapitel zwischenzeitlich vielleicht von einer Umschreibung profitieren würde, finden viele User das Ganze verwirrend.

So würde ich den Status quo in den Dokumenten angehen:

  1. using M lädt das Modul und bringt M und seine exportierten Symbole in den Namespace. Nur in diesem Fall kommt es auf den Export an.
  2. Sobald Sie M haben, können Sie qualifizierte Namen wie M.y verwenden, um (a) auf nicht exportierte Symbole zuzugreifen und (b) Methoden zu Funktionen hinzuzufügen, unabhängig davon, ob sie exportiert werden oder nicht
  3. Julia hindert Sie daran, Methoden zu Funktionen hinzuzufügen, es sei denn, Sie verwenden qualifizierte Namen wie M.f oder ...
  4. ... import M: f , dann können Sie beim Definieren von Methoden einfach f verwenden.
  5. import M und using M: x dienen dazu, nur M bzw. x (und nicht M ) in den Namespace zu bringen. Einige Leute verwenden diese Formulare gerne im Paketcode, um sicherzustellen, dass ihre Namespaces sauber bleiben (link relevante Styleguides hier).

Die Reihenfolge spiegelt mehr oder weniger Anwendungsfälle wider, die bei einer fortgeschritteneren Nutzung auftreten. Alle oben genannten Punkte gelten für Submodule, wobei M.A anstelle von M wird.

Was ist damit:

  • using M bringt M in den Geltungsbereich
  • using M: x bringt M.x in den Geltungsbereich (als x )
  • using M: ... bringt alle exportierten Symbole von M in den Geltungsbereich
  • using M: x, ... bringt alle exportierten Symbole von M in den Geltungsbereich und auch x (das möglicherweise nicht exportiert wird)
  • Es gibt keine import . Sie müssen den qualifizierten Namen zum Erweitern einer Funktion verwenden. (Oder using M; const foo = M.foo wie man es jetzt schon machen könnte.)
  • In all dem Obigen könnte M auch ein Untermodul sein, zB Foo.Bar und x könnten auch x as y mit der aktuellen Bedeutung sein.

Oder wir verwenden import anstelle von using , was dann https://github.com/JuliaLang/julia/issues/8000#issuecomment -355960915 entspricht.

Eine sehr häufige Verwendung (insbesondere in "Skripten", aber auch Paketen für bestimmte Stile)

using Foo, Bar, Baz

und sich nur auf exportierte Symbole verlassen. Es würde einige Vorteile bringen, dies so einfach wie möglich zu halten, möglicherweise so einfach wie es jetzt ist.

@tpapp

Ich glaube du hast es falsch verstanden. FWIW, ich denke, dass das "... macht Foo.x erweiterbar" eine verwirrende Herangehensweise an die Unterscheidung ist - Sie können immer Methoden für vollständig qualifizierte Funktionsnamen definieren.

Okay, kann ich Foo.x nach using Foo: x verlängern? Denn wenn ja, dann ist die Dokumentation nicht vollständig (siehe mein Screenshot). Wenn nein, dann habe ich vollkommen verstanden, wie diese Anweisungen funktionieren, und ganz offensichtlich hat import Foo etwas getan , um Foo.x erweiterbar zu machen. Daher stellt es buchstäblich Foo.x für die Erweiterung zur Verfügung. In jeder Bedeutung dieser Worte. Sicher, es macht x nicht für die Erweiterung verfügbar, aber dafür ist import Foo: x da

Okay, kann ich Foo.x nach using Foo: x verlängern?

Es sei denn, Sie bringen Foo irgendwie in den Namensraum (zB using Foo ).

Ich habe vollkommen verstanden, wie diese Aussagen funktionieren

Ich bin mir da nicht ganz sicher – wenn Sie jedoch Fragen zu Modulen und Namespaces haben, nutzen Sie bitte das Discourse-Forum.

ganz offensichtlich hat import Foo etwas getan, um Foo.x erweiterbar zu machen

Nur in dem Sinne, dass Sie irgendwie mit einem qualifizierten Namen auf eine Funktion verweisen müssen, bevor Sie ihr Methoden hinzufügen.

dann ist die Dokumentation nicht vollständig

Ich denke schon, auf eine etwas skurrile Art und Weise. Wenn Sie in diesem speziellen Beispiel nur using MyModule: x, p tun; dann sind keine Methoden zur Erweiterung verfügbar, also ist die Tabelle korrekt.

Ich stimme zu, dass es besser geschrieben werden könnte, wie ich oben sagte. Viele Leute, die nicht an Namespaces gewöhnt sind, finden es verwirrend. Und, TBH, der ganze using / import Zirkus ist leicht verwirrend, daher dieses Problem.

@tpapp

Okay, hier ist die Sache: Es ist absolut nicht offensichtlich, dass Sie jede Funktion mit dem vollständigen Namen erweitern können, wenn sich das Modul im Namensraum befindet. Ich weiß, dass dies ein aktuelles Verhalten ist, aber ich glaube nicht, dass jemand, der das nicht bereits weiß, das annehmen würde. Vor allem, weil im Namensraum zu sein nicht immer bedeutet, dass dieser auch erweiterbar ist. Wenn ich using module:x mache, kann ich $#$ x $#$ nicht verlängern. Während ich x verlängern kann , wenn ich import module:x verwende. Daher ist es eine vernünftige Annahme, dass der Unterschied zwischen using und import darin besteht, ob Sie die importierten Funktionen erweitern können oder nicht.

Unter dieser Annahme wäre es sinnvoll, wenn using module nur die Verwendung von module.x erlauben würde, aber nicht die Erweiterung von module.x . Während import module die Erweiterung von module.x erlauben würde . Wenn Sie also diese Annahme annehmen und die Dokumentation lesen, werden Sie feststellen, dass zwei Dinge daran falsch sind.

  1. using module können Sie module.x verlängern . Daher hat using module aus Lernersicht den Nebeneffekt , dass es sich auch wie import module verhält - dh es macht module.x erweiterbar. Und Dinge erweiterbar zu machen ist eine import Sache, keine using Sache
  2. im Gegensatz zu import stellt using nicht nur module zur Verfügung, sondern alles, was darin enthalten ist.

Das habe ich versucht, mit meiner Tabelle darzustellen. Wenn zwei verschiedene Wörter verwendet werden (importieren/verwenden), sollten sie zwei verschiedene Dinge tun, und das sollte eindeutig sein. Wenn using manchmal eine Verlängerung zulässt und manchmal nicht, ist das kein vorhersehbares Verhalten.

Eine gute Alternative besteht darin, den Import vollständig zu entfernen, wie @martinholters vorschlägt. Sie können einfach nicht zwei Wörter haben, die nur zufällig für bestimmte Dinge verwendet werden. Wenn import bedeutet, Dinge beim Importieren bestimmter Funktionen erweiterbar zu machen, während using module: foo dies nicht zulässt, dann sollte das gleiche Verhalten auftreten, wenn Sie nur module in den Namensraum aufnehmen.

Nur weil Sie jetzt alles mit seinem vollen Namen erweitern können, wenn sich das Modul im Namensraum befindet, heißt das nicht, dass dies eine offensichtliche oder einfache Sache ist.

Entweder reicht es, im Namensraum zu sein, dann sollte ich auch in der Lage sein, x zu erweitern, wenn ich es mit using module:x in den Namensraum aufgenommen habe, oder es reicht nicht , im Namensraum zu sein, und dann sollte ich es auch tun kann module.x nicht erweitern, wenn der Befehl using verwendet wird.
Oder dritte Option: Es gibt kein import und Sie müssen Funktionen einfach immer mit ihrem vollständigen Namen erweitern.

Es ist absolut nicht offensichtlich, dass Sie jede Funktion mit dem vollständigen Namen erweitern können, wenn das Modul im Namensraum liegt.

Dem stimme ich zu, weshalb ich für eine Neufassung der Dokumentation plädiere. Dies ist jedoch tangential zum aktuellen Problem. Es wäre am besten, (1) Vorschläge zur Verbesserung der Dokumentation der aktuellen API und (2) Vorschläge zur Neugestaltung getrennt zu halten, auch wenn die beiden etwas verwandt sind. Ich plane, bald eine PR für (1) zu machen.

@tpapp Sie können nichts dokumentieren, was von Natur aus keinen Sinn ergibt. Keine dieser Dokumentationen ist korrekt:

  1. "Sie können alles erweitern, was sich im Namensraum befindet" (weil using module:x Ihnen nicht erlaubt, x zu erweitern)
  2. "Im Namensraum zu sein ist nicht ausreichend für die Erweiterung, deshalb existieren getrennte Wörter using und import " (falsch, weil es für vollständige Namen tatsächlich ausreicht, im Namensraum zu sein - damit aufhören ausreichen war mein Vorschlag)
  3. "Sie können alles im Namensraum um seinen vollständigen Namen erweitern" (was import module:x erfordern würde, um nicht mehr zu existieren, um die volle Wahrheit zu sein - @martinholters Vorschlag)

Jede davon wäre akzeptabel. Aber nichts davon ist wirklich Realität. Die Realität sieht derzeit so aus, dass es zwei verschiedene Wörter gibt, die für „Zeug importieren“ verwendet werden, aber sie haben eigentlich keinen bestimmten Anwendungsfall. Es ist, als ob sich eine for -Schleife manchmal wie eine while -Schleife verhält, wenn Sie über boolesche Werte iterieren. Keine Menge an Dokumentation wird dies verwirrend machen

Man kann nichts dokumentieren, was an sich keinen Sinn ergibt.

Die aktuelle Modul-API ist gut definiert, sodass sie dokumentiert werden kann (ist sie bereits, ich denke nur, dass sie besser sein sollte).

Keine dieser Dokumentationen ist korrekt:

Bitte seien Sie so freundlich und warten Sie auf meine (oder die von jemand anderem) PR und kommentieren Sie den eigentlichen Text, der dort erscheinen wird. Eine hypothetische Dokumentation zu posten und dann zu behaupten, dass sie falsch ist, fügt dieser Diskussion nur Lärm hinzu.

@tpapp vielleicht hätte ich sagen sollen,

Sie können etwas nicht verwirrend dokumentieren, was an sich keinen Sinn ergibt

Ich meine, in gewisser Weise ist der Code die gesamte Dokumentation, die Sie brauchen, oder? Was stimmt damit nicht? Die Zeit, die es braucht, um es zu verdauen. Und ich sehe derzeit keinen kurzen Weg, um zu beschreiben, wie es funktioniert, weil es mit Ausnahmen gespickt ist. Ausnahmen, die es gar nicht erst geben sollte.

Siehst du wirklich nicht, was ich versuche zu vermitteln?

Ich denke, es besteht allgemein Einigkeit darüber, dass die aktuelle Situation unnötig komplex und unzureichend dokumentiert ist. Und zweifellos wird die Vereinfachung der Maschinen die Dokumentation erleichtern. Aber eine solche Vereinfachung wird brechen und kann daher nicht vor 2.0 durchgeführt werden. Also ist Ihr Punkt, dass eine Verbesserung der Dokumentation davor keinen Sinn machen würde? Dann bin ich anderer Meinung. Ich sehe zwei getrennte Probleme: Vereinfachung mit 2.0 (um die es in dieser Ausgabe geht), die (hoffentlich) die notwendigen Dokumentationsaktualisierungen enthalten wird, und Verbesserung der Dokumentation der aktuellen Arbeiten, die nach dem Lesen dieses Threads dringend erforderlich zu sein scheint, aber ein anderes Problem ist .

Nachdem ich #38271 gemacht habe, denke ich, dass offene Fragen sind

Umgang mit dem Hinzufügen von Methoden zu Funktionen in anderen Modulen

  1. erfordern immer vollständig qualifizierte Namen ( Foo.bar() = ... ),
  2. auch nur zulassen, wenn das Symbol im Geltungsbereich ist ( using Foo: bar; bar() = ... )
  3. (2), aber in besonderer Weise in den Geltungsbereich bringen ( status quo , import Foo: bar; bar() = ... )

Welche Syntax behandelt Exportlisten und nur den Modulnamen

  1. using Foo bringt alle exportierten Symbole in Foo in den Geltungsbereich, import Foo nur das Modul ( Status quo )
  2. using Foo: ... oder eine andere ähnliche Syntax, dann using Foo nur das Modul.

(1|2) & 2 würden die Vereinigung zu einem einzigen Schlüsselwort ermöglichen, entweder using oder import , auf Kosten des Verlusts einer einzelnen Zeile

using LinearAlgebra, Random, StaticArrays

Ich bin mir nicht sicher, ob es das wert ist, auch ohne Berücksichtigung der Breaking Change.

Dies ist nicht eines dieser Probleme, die eine saubere Lösung bieten, die das ursprüngliche Design gerade verfehlt hat; es gibt Kompromisse. Ich würde abwarten, ob eine bessere Dokumentation die Benutzererfahrung verbessern kann, während das aktuelle (1.0) Setup beibehalten wird.

Für 2.0 denke ich, dass nur eine Bereinigung der Syntax, um konsistenter zu sein und zu beschreiben, was tatsächlich passiert, nett wäre. Etwas wie:

| Vorher | Nach |
|-|-|
| using Foo | useall from Foo |
| import Foo | use Foo |
| using Foo: a | use a from Foo |
| import Foo: a und import Foo.a | extend a from Foo |

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen

Verwandte Themen

sbromberger picture sbromberger  ·  3Kommentare

TotalVerb picture TotalVerb  ·  3Kommentare

iamed2 picture iamed2  ·  3Kommentare

i-apellaniz picture i-apellaniz  ·  3Kommentare

wilburtownsend picture wilburtownsend  ·  3Kommentare