Rust: Standardargumente und Schlüsselwortargumente

Erstellt am 6. Juni 2013  ·  70Kommentare  ·  Quelle: rust-lang/rust

Ich habe keine Probleme mit Keyword-Argumenten gesehen, gibt es Pläne dafür in Zukunft?


HINWEIS: Der Bugtracker ist nicht der richtige Ort für eine Designdiskussion. Bitte leiten Sie alle Designdiskussionen an das Etherpad ( https://pad.riseup.net/p/hvbg6dQQnEe7 ) oder erstellen Sie eine Bikeshedding-Seite im Wiki.

Etherpads:

Hilfreichster Kommentar

Triage 2013-08-05: kein Fortschritt (der mir bekannt ist), obwohl es immer noch ordentlich wäre; vielleicht könnte die Deklarationssyntax so aussehen

fn foo(bar: int, qux: int = 4, ham: Option<int> = None) { ... }

wobei RHS ein konstanter Ausdruck ist (dh Dinge, die in einer static Deklaration gültig sind).

Alle 70 Kommentare

Es gibt keine Pläne dafür, obwohl sich noch niemand dagegen ausgesprochen hat. Ich habe lange über dieses Thema nachgedacht, und es läuft im Grunde auf eine Anfrage nach Standardargumenten hinaus. Ich nehme an, es würde ungefähr so ​​aussehen:

fn foo(bar: int, qux=2: int, ham=0: uint) { ... }

Was dann als foo(1) , foo(1, 3) , foo(1, ham=4) , foo(6, 7, 8) usw.

BEARBEITEN: Bitte sehen Sie sich unten die von huonw vorgeschlagene Syntax an, die mit der aktuellen Deklarationssyntax viel besser und einheitlicher aussieht.

Triage 2013-08-05: kein Fortschritt (der mir bekannt ist), obwohl es immer noch ordentlich wäre; vielleicht könnte die Deklarationssyntax so aussehen

fn foo(bar: int, qux: int = 4, ham: Option<int> = None) { ... }

wobei RHS ein konstanter Ausdruck ist (dh Dinge, die in einer static Deklaration gültig sind).

IMO sind diese wirklich nützlich, sogar das C++-System von _Standardargumenten_, bei dem Sie nur für nachfolgende nicht spezifizierte Argumente auf Standardwerte zurückgreifen können, wäre großartig ... in der Lage zu sein, auch diese Instructs und Enum-Varianten zu deklarieren, wäre großartig.
Vielleicht könnte diese einfachere Teilmenge des erforderlichen Verhaltens implementiert werden, bevor ein Konsens darüber besteht, wie / ob benannte Schlüsselwortparameter aufgerufen werden sollen ... es würde die Interna vorbereiten?

Wäre es praktikabel/nützlich, als Ausdrücke zu implementieren ... in Bezug auf früher angegebene Argumente und generische Typparameter (z. B. Fähigkeit, eine Null zu suchen::, oder schreiben Sie Dinge wie Slice(&self, start:uint , end:uint=self.len ()) /* "foobarbaz".slice(6) == "baz" .. oder create_window(parent,x,y, width :uint=parent.width ()/4, height:uint= (width_161)/100) /_ Standard sind Fenster mit goldenem Schnitt und 1/4 Bildschirmbreite, wenn Sie überhaupt keine Größe angeben.. */ .. .. oder klingt das nur nach einem Rezept für Code-Aufblähungen..

Standardeinstellungen im c++-Stil würden Haskell-ähnliche partielle Funktoin-Anwendungen ausschließen, aber ich habe gehört, dass dies unwahrscheinlich ist.

Hier ist eine weitere Idee für Standardargumente. Anstatt zuzulassen, dass Argumente vollständig entfernt werden, lassen wir zu, dass Argumente, für die ein Standardwert existiert, mit einem _ aufgerufen werden.

Also das schöne Beispiel von @huonw für:

fn foo(bar: int, qux: int = 4, ham: Option<int> = None) { ... }

...wir könnten dies als foo(1, 2, None) , foo(1, _, Some(1)) , foo(1, _, _) usw. aufrufen.

Diese Idee würde jedoch keine wirklichen Vorteile bei der Übernahme von Schlüsselwortargumenten bieten, da sie immer noch streng positionell ist.

Titel in "Standardargumente und Schlüsselwortargumente" ändern.

nicht implementiert, aber ich habe versucht, ast & parser; https://github.com/dobkeratops/rust/compare/default_args , ist das der richtige Weg? (Option @expr .. es werden Einschränkungen bezüglich des Ausdrucks benötigt?)
Ich tauche meine Zehen ins Wasser, um mit dieser Funktion zu experimentieren.
Ich wäre sehr daran interessiert, die Untermenge der C++-APIs zu erhöhen, die übersetzt werden könnten, und sie sind definitiv etwas, das ich von C++ vermisse, und das benannte Schlüsselwort-Feature wäre großartig, was C++ übertrifft.
Es schien einfach und logisch, die zweite vorgeschlagene Deklarationssyntax zu verwenden

@dobkeratops das sieht gut aus (obwohl das type Default = Option<@expr> wahrscheinlich unnötig ist).

Die Einschränkungen für expr gehen in rustc::middle::check_const ; Sie müssen eine Funktion wie check_fn hinzufügen, die jedes Argument überprüft. (Außerdem sollte es vermutlich eine Überprüfung geben, dass Standardargumente nur am Ende erscheinen; und ich denke, es muss etwas in der Typprüfung rustc::middle::typeck und auch in trans sein. )

Ok, das Feld ist so benannt, dass eine unnötige Anmerkung ist.
Danke für die Hinweise, wo ich suchen muss, ich habe in rustc::middle::typeck::.. mod.rs (check_fn something ) gesucht.

im namen der 'kontinuierlichen integration' lohnt es sich, die trivialen grundlagen als pr einzureichen, wenn ich daran scheitere, könnte jemand, der die codebasis besser kennt, die anderen teile ein anderes Mal viel schneller erledigen.
Ich bin immer paranoid, wenn es um divergierende Veränderungen geht

Ist es nur destruktiv, wenn Sie sich definitiv _gegen_ entscheiden?

Jemand hat vorgeschlagen, dass so etwas mit einer -Z-Option (..Session::debug_opts...

Ich kann sehen, dass Sie etwas dagegen haben, dass der Compiler etwas analysiert, das er noch nicht verwendet. Ich könnte es vielleicht von einer Warnung in einen Fehler ändern ...

@dobkeratops Beachten Sie, dass sich noch kein offizieller Entwickler dazu

Okay, ich würde vermuten, dass sie noch lange eine sehr niedrige Priorität haben würden, da sie nichts blockieren (sie stehen sicherlich nicht ganz oben auf meiner eigenen Wunschliste).
Ich dachte nur, es könnte eine tief hängende Frucht sein und eine weitere Vertrautheit für C++-Leute.
Ich nehme an, Makros können manchmal eine ähnliche Aufgabe erfüllen (und mehr).. und ich kann bessere Benennungsgewohnheiten für häufige/ungewöhnliche Variationen entwickeln

Ich kann sehen, dass benannte Argumente nicht kritisch sind, aber optionale IMO-Argumente sind es. Denn ich glaube, ich habe an einigen Stellen ein paar Methoden mit ähnlichen Signaturen und ähnlichen Namen gesehen, nur um einfachere Schnittstellen anzubieten, wenn ein bestimmtes Argument nicht benötigt wird. Es wäre scheiße, wenn wir am Ende solche Legacy-Funktionen haben, nur weil eine Funktion fehlt.

Es gibt viele subtile Teile hier. Alle Standardargumente sind eine implizite Form der Überladung, so dass (vermute ich) wahrscheinlich in den Trait-Resolution-Algorithmus und/oder die Typinferenz integriert werden. Es ist vielleicht nicht sehr schwierig, aber es lohnt sich, sorgfältig zu entwerfen, und an dieser Stelle bin ich mir nicht sicher, ob wir das Zeitbudget dafür haben. Als _Sprache_-Funktion stelle ich mir vor, dass es abwärtskompatibel ist; als api-feature würde es wahrscheinlich das api-design beeinflussen, also ein bc-risiko darstellen.

@Seldaek Ich stimme zu, dass es wichtig ist, sie zu implementieren, bevor wir uns auf die Abwärtskompatibilität von stdlib

Im Gegensatz zu Rust verfügt Go jedoch über einige Funktionen, die das Fehlen von Standardargumenten weniger schmerzhaft machen. Zum einen haben sie variadische Funktionen.[1] Zweitens dürfen Sie beim Erstellen von Strukturen Felder weglassen, was bedeutet, dass es einfach ist, eine Konfigurationsstruktur an eine Funktion zu übergeben (die ausgelassenen Felder scheinen auf Compiler-spezifizierte (?) Standardwerte gesetzt zu sein, anstatt etwas zu sein, was ein Benutzer kann anpassen).[2]

Bei ersterem könnten wir mit Makro-Hackern davonkommen, um das gleiche Ergebnis zu erzielen (wenn auch mit mühsamen Implementierungskosten). Tatsächlich macht der bevorstehende Ersatz für fmt!() Folgendes:

ifmt!("{foo} {1} {bar} {0}", 0, 1, foo=2, bar=3)

Das ist Code, der heute funktioniert.

Für letzteres wäre das beste Analogon, das wir haben, die Aktualisierung der funktionalen Struktur wie folgt:

struct Foo { x: int, y: int, z: int }

impl Foo {
    fn default() -> Foo {
        Foo{x: 0, y: 0, z: 0}
    }
}

fn bar(f: Foo) {
    printf!(f);
}

fn main() {
    bar(Foo{y: 5, ..Foo::default()});
}

Ich habe diese Methode in der Vergangenheit beiläufig als Problemumgehung für das Fehlen von Standardargumenten gesehen, aber in der Praxis ist sie ziemlich unbeholfen (vergleiche bar(Foo{y: 5, ..Foo::default()}) mit bar(y=5) oder meinem konservativeren Vorschlag von bar(_, 5, _) ), und natürlich zwingt es alle Aufrufer, eine Struktur zu erstellen, auch wenn sie alle Argumente _do_ übergeben wollen.

Um es noch einmal zu wiederholen, als jemand, der von Python und Javascript kommt, würde ich natürlich Standardargumente begrüßen. Ich kann jedoch ein wenig in die Philosophie von Go einfühlen, APIs explizit zu machen (im Austausch dafür (möglicherweise stark) die Anzahl der Funktionen in der API selbst zu erhöhen).

[1] https://groups.google.com/d/msg/golang-nuts/NWMReL1HueQ/X9mdYduCOB8J

[2] http://stackoverflow.com/questions/2032149/optional-parameters

Ich sollte auch darauf hinweisen, dass es mit Closures interagiert und erstklassige Referenzen auf Funktionen nimmt.

@bstrie danke für die Übersicht von Go. Ich stimme dem Punkt zu, dass Überladung wahrscheinlich eine schlechte Idee ist, da sie verwirrende APIs erzeugt. In ähnlicher Weise sind die "überladenen" Methoden von jQuery, die einen Rückruf in so ziemlich jedem gewünschten Argument annehmen können, sehr seltsam und mühsam zu implementieren.

Und ich stimme irgendwie zu, dass Wrapper-Funktionen zum Ausfüllen von Standardargumenten in einigen Fällen nicht so schlecht sind. Das Problem ist jedoch, dass es Sie zwar vor einigen verwirrenden Fällen bewahrt, in anderen jedoch eine Menge Boilerplate einführt. Ein Beispiel ist *::to_str_radix(num, radix) . Wenn wir optionale Argumente hätten, könnte dies gut in *::to_str(num, radix = 10) gefaltet werden. Ich habe das Gefühl, dass die Explosion von Funktionsvarianten in einigen Fällen die Dinge wirklich weniger intuitiv und schwerer zu merken macht. Aber natürlich nur meine zwei Cent.

Ich kann sehen, dass es ein guter Zeitpunkt ist, sie zu verschieben;
aber lässt _wirklich_ Standardwerte philosophisch weg, oder rechtfertigen sie nur die Kompromisse, die sie bei der Implementierung von Zeitbudgets eingegangen sind? Ich wäre überrascht, wenn jemand denkt, dass es ein Schritt nach vorne ist, durch long_function_names_with_lots_of_trailing_qualifiers zu waten :) , aber wenn er sagt, "es ermöglicht uns, einfache Tools zu nutzen, wir haben noch keine leistungsstarke IDE entwickelt ..." das ist eine andere Sache.

Ich vermisse definitiv sowohl das Überladen als auch die Standardeinstellungen von C++.

Für ein Experiment dachte ich darüber nach, sie als "Parser-Hack" zu versuchen ... auf der Ebene von Makros zu arbeiten? Inlining der Standardausdrücke an den Aufrufseiten, bei denen Argumente fehlen?
Mir ist klar, dass dies wahrscheinlich kein akzeptables Design ist - aber es lässt mich glauben, dass es nicht _unmöglich_ ist, Standardargumente zu verwenden, die mit den Funktionen von Rusts funktionieren.

Eine Sache, an die ich mich immer noch wirklich gewöhne, ist, dass Methoden theoretisch viel besser in Rost anwendbar sind.
Zurück in C++ habe ich wegen Header-/Abhängigkeitsproblemen Angst vor ihnen (dies ist der Hauptgrund, warum ich hier bin), also gehe ich oft instinktiv zuerst auf Funktionen.
Vielleicht verpasse ich weniger Standardeinstellungen, wenn ich Rost besser verwende.

Damit die Idee nicht verschwindet: Der gesprächige Aspekt von bar(Foo{y: 5, ..Foo::default()}) scheint mir der ..Foo::default() Teil zu sein.

Wenn wir es einfacher machen würden, Felder in einer Strukturkonstruktion wegzulassen und sie auf einen Standardwert fallen zu lassen, wäre dieser Ansatz vielleicht schmackhafter.

ZB eine neue Form, die wahrscheinlich nur für Strukturen definiert wurde, die ein geeignetes Merkmal implementieren ( Zero oder Default oder was auch immer), das so aussah:

Foo{y: 5, *) wobei das * die Stelle des früheren ..Foo::default() .

Dann ist das Beispiel "nur" bar(Foo{y: 5, *}) . Vielleicht würden andere es vorziehen, dass die Foo nicht da wären, aber das scheint mir ziemlich sauber zu sein.

Ich nehme an, Aufzählungen bieten Ihnen auch einige andere Möglichkeiten, Gruppen optionaler Parameter auf andere Weise zu übergeben, als es ein C++-Programmierer gewohnt ist ... und eine Möglichkeit, Anmerkungen und Werte mit einem Aufruf zu durchsetzen.
Das scheint für Fälle wie das Erstellen von etwas mit vielen Setup-Parametern gut zu passen

@pnkfelix Denken Foo Struktur in diesem Beispiel wahrscheinlich einen viel selbsterklärenderen Namen hätte und selbst mit Ihrem vorgeschlagenen Zucker in der Praxis ungefähr so ​​aussehen würde:

html::start_server(html::ServerOpts{port: 10088, *});

Es sieht etwas schöner aus. Immer noch nicht sicher, ob es genug hilft, um eine neue Syntax zu rechtfertigen, oder ob es ausreicht, um alle Anwendungsfälle von Standardargumenten zu erfüllen.

Um mein eigenes Beispiel zu verwenden: Wenn ich etwas mit vielen Optionen wie einem HTML-Server initialisiere, wäre ich wahrscheinlich genauso glücklich, vorher ein server_opts Struct einzurichten und dann einfach start_server(server_opts) aufzurufen. ..html::ServerOpts::default hinzuzufügen, wenn meine struct-Initialisierung bereits mehrere Zeilen umfasst.

Ich denke, wir müssen vielleicht überdenken, wo Standardargumente am nützlichsten wären. Für mich gibt es hier nicht viele potenzielle Argumente; diese sollten ziemlich selten sein, und ein paar Zeilen zum Initieren einer Konfigurationsstruktur sind in Ordnung. Vielmehr vermisse ich Standardargumente am meisten, wenn 1) die Operation ziemlich häufig ist und 2) die Operation höchstens ein oder zwei Argumente hat, die fast immer überflüssig sind, aber der Vollständigkeit halber erforderlich sind. Beispielsweise:

let foo = Some('a');
foo.unwrap();  // same as today
foo.unwrap('z');  // imagine that this replaced unwrap_or_default

int::from_str("4");  // same as today
int::from_str("4", 16);  // imagine that this replaced from_str_radix

Nachdem ich diese nun eingegeben habe, bevorzuge ich jedoch die Explizitheit der separaten Methoden. :) Vielleicht weiß ich doch nicht so recht, was ich will!

@dobkeratops , können Sie uns aus Ihrer Erfahrung mit C++ einige Beispiele für nette C++-APIs geben, die durch Standardargumente aktiviert sind?

Kommentare in https://github.com/mozilla/rust/wiki/Meeting-weekly-2013-08-13 scheinen darauf hinzudeuten, dass die Entwickler dies als ein Feature in ferner Zukunft ansehen. Nominieren.

OCaml führt ein optionales/Standardargument aus, ohne es zu überladen, glaube ich. Optionale Argumente vom Typ T ohne Standardwert werden implizit in die T-Option umgewandelt und die Funktion muss prüfen, ob ein Wert angegeben wurde. Es gibt auch Einschränkungen bei der Deklaration und Verwendung optionaler Argumente, damit der Compiler weiß, wenn ein Argument weggelassen wurde. Alle optionalen Argumente müssen Labels haben (sie müssen Schlüsselwortargumente sein, um optional zu sein). Dies verkompliziert das Typsystem von OCaml (Beschriftungen werden Teil des Typs der Funktion), aber ich denke, dies ist ein Artefakt der Interoperabilität mit dem Rest des Hindley-Milner-Typsystems.

Von C++ kommend, würde ich die Standardargumentwerte wirklich vermissen. Ich verwende oft Funktionen mit einer großen Anzahl von Parametern, wobei die meisten von ihnen fast immer Standardwerte haben, mit Ausnahme einiger seltener Anwendungs- oder Testfälle. Das Hinzufügen von rein kombinatorischen Funktionsnamen-Derivaten anstelle von Standardwerten würde ein großes Durcheinander von wirklich langen Namen erzeugen.

Ich stimme zu, dass Standardargumente und/oder Schlüsselwortargumente äußerst nützlich wären. Ich denke, wir sollten auf einen Vorschlag vor 1.0 hinarbeiten, der skizziert, was in Bezug auf Überladung getan werden muss, und vielleicht eine Diskussion über die Syntax führen.

Schön zu sehen, dass sich mehr Leute dafür äußern :) Ein weiterer Grund, warum ich es wollte, war, die Anzahl der C++-APIs zu erhöhen, die nahtlos übersetzt werden können. Die Verwendung von C++-Bibliotheken ist einer der Hauptgründe, warum wir bei C++ stecken bleiben.

Ich vermute, die einzige Möglichkeit, dies zu erreichen, wäre, wenn die Community es ohne Kosten für die Kernentwickler _implementieren_ könnte.
Ich stimme zu, dass sie viel wichtigere Dinge zu tun haben und Aufzählungen verlorene Polymorphismen mit Möglichkeiten wiederherstellen, die für C++-konditionierte Gehirne nicht sofort ersichtlich sind.

Aus diesem Grund wollte ich die Grundlagen für das Parsen in die AST-Datenstruktur einreichen. Es wäre bereit, dass jemand anderes es abholt und versucht, eine Lösung zu finden. Erste Rückmeldungen haben mich davon abgehalten, eine PR zu machen.

von c++ aus schaue ich neidisch darauf, was scala und python dazu haben. Eine native Nicht-GC-Sprache mit dieser Eleganz wäre großartig.
...mehr Funktionalität an einem Ort, weniger Navigieren, um herauszufinden, was vor sich geht, weniger Verschachtelungstiefe, weniger Rauschen im Quellcode. Nicht nur trivial weniger Tippen.
vielleicht könnten Sie sogar behaupten, dass es selbstdokumentierender ist, die Funktionssignatur sagt Ihnen mehr darüber, wie sie verwendet wird.

Das Schwierige ist der Typchecker, der subtile Fehlermöglichkeiten und verwirrende Fehler verhindert, wenn er nur als Parser-Hack durchgeführt wurde, denkt es.

Stellen Sie sich vor, Sie könnten so etwas schreiben..

fn substr(a:&str, start:int, end:int=a.len())->~str

hacken Sie einfach so etwas ohne Fehlerprüfung in den AST, ich bin mir sicher, dass viel schief gehen kann.. aber stellen Sie sich vor, wir könnten das beheben, um das zu tun, was erwartet wurde :)

Nur ein Fehler. Wir können über die Vorzüge diskutieren, werden aber eine Veröffentlichung dazu nicht blockieren.

Ich mag das Verhalten, das @tautologico in seinem Kommentar beschreibt.

Auf Rust angewendet, würde dies meiner Meinung nach Folgendes bedeuten:

fn ga(bu: int, zo: Option<int>, meu: Option<int>) {
  let zo = zo.get_or_default(42);
  let meu = meu.get_or_default(99);
  ...
}

Und das wären alles gültige Aufrufe:

ga(10, 20, 30); // 20 and 30 are automagically
                // converted to Some(20) and Some(30)
ga(10, 20);         // ga(10, 20, 99) 
ga(10);             // ga(10, 42, 99)
ga(10, None, None); // ga(10, 42, 99)
ga(10, 20, None);   // ga(10, 20, 99)
ga(10, None, 30);   // ga(10, 42, 30)

Die Regel ist, dass die nachfolgenden Parameter Option<T> weggelassen werden können. Hier wird der Typ Option wiederverwendet, aber wenn dies Probleme verursacht, könnte ein anderer, spezifischerer Typ OptParam erstellt werden.

Dieser Vorschlag ermöglicht es dem Anrufer, unabhängig von seiner Position genau die Parameter auszuwählen, die er bereitstellen möchte und die er als Standard beibehalten möchte. Außerdem finde ich es gut, dass der Standardwert nicht in der Parameterliste auftaucht. Auf diese Weise ist der Standardwert nicht auf einen konstanten Ausdruck beschränkt, er kann vom Zustand des Objekts oder von anderen Parametern abhängen.

Beispiel aus der Praxis für eine Funktion, die ein GUI-Dialogfeld anzeigt:

fn showDialog(message: ~str,
              parent: Option<Widget>,
              title: Option<~str>,
              type: Option<DialogType>,
              icon: Option<Icon>) { ... }

// Display an info box in the middle of the screen.
// Set the title to "information", translated in the current locale
// Set the icon to an "info" icon
showDialog(~"hello, world!");

// Display a warning box in the middle of the screen.
// Set the title to "warning", translated in the current locale
// Set the icon to a "warning" icon
showDialog(~"sick, sad world!", None, None, WarningDialog);

// Display a warning box in the middle of the screen.
// Set the title to "warning", translated in the current locale
// Set the icon to a custom icon
showDialog(~"sick, sad world!", None, None, WarningDialog, bugIcon);

In diesem Beispiel:

  • der Standardwert von parent ist None (kein übergeordnetes Element)
  • der Standardwert von type ist InfoDialog
  • der Standardwert von icon hängt vom Wert von type
  • der Standardwert von title hängt vom Wert von type und vom aktuellen Gebietsschema ab

Sie schlagen jetzt auch vor, Option als neuen primitiven Typ hinzuzufügen. Es ist im Moment eine reine Bibliotheksfunktion, da es sich um ein einfaches altes enum .

Ich möchte hinzufügen, dass IMO-Standardargumente mit Schlüsselwortargumenten eine Milliarde Mal besser sind. Ich mag die Idee von Standardargumenten nicht, aber keine Schlüsselwortargumente.

eins ist ein Sprungbrett zum anderen und Standardeinstellungen sind für sich genommen immer noch nützlich, Sie haben einige zusätzliche Flags.. und können sie weglassen, wenn Sie nur die Standardeinstellungen möchten.
Sie sind sich einig, dass sie sich gut kombinieren lassen, wenn Sie beides haben.
Der Knackpunkt besteht darin, sie tatsächlich umzusetzen :)
wie man es in den Typechecker/Type Inferer nachrüstet.. oder wie man es sonst einsteckt.

Standardwerte für Positionsargumente führen zu kryptischem Code. Warum nicht nur Standardwerte für Schlüsselwortargumente haben? (Wenn diese implementiert sind).

IMO ... es ist nicht "entweder-oder".
Sie sind beide nützlich. Eine ist einfacher zu implementieren, da weniger Auswahlmöglichkeiten in der Syntax zu treffen sind;
Es macht also Sinn für uns, dies zu tun oder zuerst zu versuchen, das als Sprungbrett zu implementieren ...

Inwiefern ist das Auslassen von Werten, die standardmäßig voreingestellt sind, kryptischer als normale Funktionsaufrufe? C++-Programmierer sind es bereits gewohnt, Parameter dafür zu sortieren. Normalerweise eine Art Control-Flags-Wort mit einer sinnvollen Vorgabe am Ende.. einfach den Wert weglassen, anstatt zu schreiben (..,BLAH_DEFAULT) was auch immer.. nur durch Auflisten wichtiger Informationen klargestellt wird. Leute, die Rust brauchen, haben sich wahrscheinlich bereits mit C++ beschäftigt und sind daher daran gewöhnt.

Ich habe heute früh erkannt, dass mein obiger Vorschlag (der versucht hat, die vorhandene Struktur-Erweiterungs-Syntax zu nutzen, um etwas in der Nähe von Standardargumenten bereitzustellen, da ich Struktur als bereits die Zutaten für Schlüsselwortargumente ansehe), ein wichtiges Problem ignorierte: die Syntax, die ich vorschlug angenommen, dass man in der Lage wäre, einen Standardwert für _jedes_ Argument bereitzustellen (und dann würden sie alle in den einzelnen struct-Parameter gehoben).

Dies gilt im Allgemeinen natürlich nicht: Man hat oft einige Argumente, die erforderlich sind, und andere, die optional sind.

Ich würde immer noch gerne einen Weg finden, die Funktionen, die wir bereits haben, zu nutzen, anstatt weiter mit der Syntax der Funktionsdeklaration herumzubasteln, hauptsächlich weil Funktionsdeklarationen _schon haarig_ sind, dank der verschiedenen Möglichkeiten, wie man Funktionen/Schließungen/Methoden aufschreiben kann.


Mein aktuelles Brainstorming hat sich darauf konzentriert, ob wir die vorhandene Struktursyntax nutzen könnten, vielleicht durch Hinzufügen von Standardausdrücken für Felder in der Definition eines Strukturelements. Das würde dann erlauben, einige Felder zu haben, die gelöscht werden könnten und andere, die erforderlich wären. Etwas wie:

struct Foo { x: int = 0, y:int = 0, z:int = 0 }

fn bar(f: Foo) {
    printf!(f);
}

struct Baz { x: int, y: int, z:int = 0 }

fn quux(g: Baz} {
    printf!(g);
}

fn main() {
    bar(Foo{y: 5});
    quux(Baz{y: 5}); // ~ERROR: required field, `x`
}

@pnkfelix Ich mag diesen Vorschlag, vielleicht wäre es nicht allzu schwierig, die gleichen Interna zu nutzen, aber eine anonyme Struktur zu generieren. In Ihrem Beispiel wäre dies also effektiv derselbe Code mit ein wenig Compiler-Magie, um die Struktur abzuleiten:

fn bar({ x: int = 0, y:int = 0, z:int = 0 }) {
    printf!(f);
}

fn quux({ x: int, y: int, z:int = 0 }) {
    printf!(g);
}

fn main() {
    bar({ y: 5 });
    quux({ y: 5 }); // ~ERROR: required field, `x`
}

@visionmedia @bstrie Müssen wir uns wirklich Sorgen machen, den Strukturnamen loszuwerden? Löst eine

Dh mit ServerOpts-Beispiel, das mir bstrie postuliert hat:

fn main() {
  use O = html::StartServerOptions;
  ...
  html::start_server(O{port: 10088});
}

@pnkfelix gefällt mir

struct Foo {
   x: int = 10,
   y: float = 1.0
}

Syntax, aber ich glaube nicht, dass wir sie brauchen, um vernünftige obligatorische Argumente zu erhalten (leider nicht benannt), übergeben Sie die obligatorischen einfach separat:

struct FooOptions { ... }
static default: FooOptions = FooOptions { ... };
fn foo(compulsory: int, required: uint, necessary: float, optionals: FooOptions) { ... }

FWIW, ich mag auch die Idee mit der Struktur mit Standardwerten. Ich denke, das wäre eine Abkürzung für:

struct Test {
    m: int,
    y: int
}

static DEFAULT: Test = Test{m: 10, y: 15};

Test{y: 5, ..DEFAULT}

Ich mag auch die Strukturvorgaben in der Definition

Ich habe diese auch mit dieser Syntax geparst, aber nicht wirklich verwendet. Ich kenne mich immer noch nicht mit der Typechecker-Quelle usw. aus...

Vielleicht erleichtert die Tatsache, dass es dort bereits einen Standardmechanismus gibt, die Implementierung etwas.

betreffend. Anonyme Strukturen Ich glaube, sie hatten diese ursprünglich, mussten sie aber "wegen seltsamer Interaktionen" entfernen?
Am liebsten hätte ich beide Mechanismen zur Verfügung. vielleicht würden Parameterstrukturen die Notwendigkeit von _keyword_-Argumenten verringern, aber nachgestellte Standardwerte wie in c++ wären immer noch praktisch

als Referenz -
http://www.parashift.com/c++-faq-lite/named-parameter-idiom.html
lol - interessante, aber wahre Schlüsselwortargumente würden die Notwendigkeit von schweren Hacks wie diesem vermeiden

betreffend. Anonyme Strukturen Ich glaube, sie hatten diese ursprünglich, mussten sie aber "wegen seltsamer Interaktionen" entfernen?

Ursprünglich waren alle Strukturtypen strukturell. Ich glaube, sie wurden wegen umständlicher Interaktionen mit Merkmalen entfernt (durch nominale Strukturtypen ersetzt).

Ich mag die Standardwerte in der Idee der Strukturdefinitionen. Dies scheint leicht zu verstehen und vermeidet zumindest, beim Erstellen einer Struktur jedes Feld aufzählen zu müssen - dies ist ein Problem für Strukturen vom Typ "Optionen", da dies bedeutet, dass beim Hinzufügen eines neuen "optionalen" Felds jede Anrufseite geändert werden muss zu initialisieren Sie es auf 0/Keine/usw.

Ausgehend von Perl (wo Standardwerte ein Ad-hoc-Muster sind), Python 2 (wo Sie foo() takes at least 2 arguments (2 given) ) und Python 3 (wo Sie nur benannte Argumente _ohne_ Standardwerte haben können), schlage ich bescheiden vor: add ein _teeny_ bisschen Zucker, um eine Struktur als letztes Argument für eine Funktion deklarieren zu können, aber lassen Sie den Aufrufer die Strukturfelder _inline_.

z.B

impl int {
    struct FromStrOptions {
        radix: uint = 10
    }
    fn from_str(s: str, opts: FromStrOptions) -> int {
        // ...
    }
}

int::from_str("4", radix: 10);

Vorteile:

  • Keine neue Syntax für die Funktionsdefinition, die sich eigentlich gar nicht darum kümmern muss, wie der Aufrufer sie aufruft. Dies ist ähnlich wie das Übergeben von Blöcken mit do : Es ist ausschließlich Sache des Aufrufers. (Muss es eine Möglichkeit geben, sich an- oder abzumelden, für den Fall, dass ein letztes Argument eine Struktur ist, die jedoch nicht für Kwargs verwendet werden soll? Würde es jemanden interessieren? Ich glaube nicht, dass sich jemand für do interessiert
  • Binäres Layout und C-Aufrufsemantik usw. sind immer noch ziemlich gut definiert.
  • Das Überladen nach Typ oder Arität ist eines Tages nicht mehr schwierig zu implementieren, außer dass der Typ des letzten Arguments nicht zur Typüberladung beitragen kann. Sollte auch nicht mit variadischen Argumenten interferieren, falls diese jemals passieren sollten.
  • Dieselben Standardeinstellungen können in mehreren Funktionen wiederverwendet werden.
  • Etwas wie **kwargs kostenlos: Erstellen Sie einfach die Struktur und übergeben Sie sie stattdessen als positionelles Argument.
  • Erforderliche Schlüsselwortargumente ergeben sich natürlich daraus: Geben Sie einfach keinen Standardwert in der Struktur ein, und Sie sind gezwungen, _etwas_ als Namen zu übergeben.
  • Keine verrückte Verwirrung beim Übergeben von Positionsargumenten nach Namen; das kannst du einfach nicht.

Nachteile:

  • Leicht magisch. Aber nicht mehr, als 5 und den Typ aus der argspec der Funktion abzuleiten, denke ich.
  • Unklar, wie es mit dem vorhandenen do Idiom interagieren würde. Könnte leicht genug sein zu sagen, dass es das letzte Argument _geschrieben_ sein muss, dh es funktioniert nur als vorletztes Argument, wenn es mit do .

FWIW int::from_str("4", radix: 10) würde mit der zuvor vorgeschlagenen Verwendung von : als Typzuweisungsoperator kollidieren.

Flüche, vereitelt durch ASCII.

int::from_str("4", radix☃ 10)

Ich würde für die Verwendung von = für Schlüsselwortargumente stimmen, verbietet nur Cs Idee von Zuweisungen innerhalb von Unterausdrücken mögliches Beispiel dafür, wie es in eine c-ähnliche Syntax passt?

Gibt es irgendwo ein Brainstorming, wie man dies implementieren kann? Ich habe mich gefragt, ob der variable argumentmnt-Stil intern wie eine Funktionsüberladung basierend auf der Argumentanzahl implementiert werden könnte (fast so, als ob die Anzahl der Argumente auf den Funktionsnamen postfixiert wäre ..)

Darüber haben wir gestern Abend in der wöchentlichen

Ich denke, alle waren sich einig, dass weitere Komplikationen bei den Funktionsdeklarations-/Aufrufformularen nicht gut sind und dass die Erweiterung von struct-Deklarationen, um Feld-Standardwerte (verbunden mit const-Ausdrücken) zuzulassen, wie in meinem früheren Kommentar vorgeschlagen , ein vernünftiger Weg wäre, um zu erhalten ein Großteil dessen, was hier gewünscht wird.

Das Team entschied auch, dass die Lösung dieses Problems in irgendeiner Weise keine Priorität für die Version 1.0 hat. Projektleiter @brson schlug vor, dass wir das Prototyping für diese Aufgabe für Post-1.0 (zB für ein 1.1-Release)

Das ist also ein starker Hinweis an alle Mitwirkenden: Hacken Sie alles, was Sie wollen, aber senden Sie bitte keine Pull-Requests oder r+ für diesen Fehler bis zur Veröffentlichung nach Version 1.0.

Zweifellos ist Rust eine erstaunliche Sprache. Es macht so viele Dinge richtig, dass es nicht einmal lustig ist. Aber ich halte es für einen Fehler, diese Funktion für Post 1.0 zu verschieben.

Lassen Sie mich das näher erläutern.

Standardfunktionsargumente sind _kritisch_ für ein gutes API-Design; ihr Fehlen erschwert die ansonsten einfacheren APIs. Beispiele aus der std-Bibliothek:

Und andere.

All diese zusätzlichen Funktionsnamen zu haben, die mit Standardargumenten entfernt werden könnten, erhöht die kognitive Belastung unnötig. Der Benutzer muss nun statt einem zwei (oder mehr) Namen kennen.

Wenn Standardargumente nach 1.0 verschoben werden, können diese Funktionen aus der std-Bibliothek aufgrund der Abwärtskompatibilität nicht entfernt werden. Dies bedeutet, dass selbst wenn in Zukunft Standardargumente hinzugefügt werden und die "Vanilla"-Version der Funktionen jetzt die Arbeit der anderen Varianten übernehmen kann, die alten Varianten bestehen bleiben und Entwickler sie kennen müssen, weil sie sicherlich jemand verwenden wird sie, wenn auch nur aus Versehen.

Diese zusätzliche kognitive Belastung wird also _für immer_ bleiben.

[NB Das gleiche Argument kann für das Überladen von Parametertyp-basierten Funktionen angeführt werden (die aktuelle merkmalsbasierte Problemumgehung ist zu umständlich und die Funktionen der Standardbibliothek verwenden sie nicht), aber dieses Thema ist nicht der Ort für diese Diskussion.]

Rust-Entwickler, danke, dass ihr an Rust gearbeitet und es großartig gemacht habt!

Ich füge den Meilenstein "ferne Zukunft" hinzu, um die extreme Entmutigung hervorzuheben, die wir im Kernteam jedem mitteilen möchten, der sich eine Sekunde lang mit diesem Thema beschäftigt. Wenn Sie jetzt zu Rust beitragen möchten, gehen Sie bitte einen der 41 offenen Fehler bei Meilenstein 1 (gut definiert) an:

https://github.com/mozilla/rust/issues?milestone=12&state=open

oder einer der 104 offenen Fehler bei Meilenstein 2 (Abwärtskompatibilität):

https://github.com/mozilla/rust/issues?milestone=13&state=open

oder einer der 68 offenen Fehler bei Meilenstein 3 (Funktionen, auf die wir uns irgendwann geeinigt haben, sind entscheidend für die Veröffentlichung von Rust 1.0):

https://github.com/mozilla/rust/issues?milestone=14&state=open

oder kommentieren Sie einfach einen dieser Fehler, um um Klärung zu bitten oder Wege vorzuschlagen, um Fortschritte zu erzielen. Das sind 213 Fehler zur Auswahl; Es wäre für Rust viel wertvoller, an dieser Stelle Fortschritte bei einem von ihnen zu machen als dieses Thema. Und jedem, der einen dieser Fehler schließen kann, gilt unser größter Dank.

Ich kann sehen, dass das Kernteam viel wichtigere Dinge zu tun hat, aber es scheint eine Schande zu sein, "extreme Entmutigung zu kommunizieren" :(
+1 für Vallorics-Kommentare. Namensgebung ist schwer, Dokumentation zu durchforsten und mehr Namen zu lernen ist abstoßend..; Standardargumente/Überladung machen es einfacher; mit Schlüsselwort args hätten Sie die Möglichkeit, in dieser Hinsicht über C++ hinauszugehen. (Ich wünschte, Github hätte abstimmen können, ich könnte einfach +1 drücken, anstatt hier ein bisschen mehr zu schimpfen lol)

@Valloric Ich stimme

Es besteht kein Konsens darüber, dass dies eine gute Sprachfunktion wäre. Es gibt auch keinen konkreten Vorschlag mit allen ausgearbeiteten Details für benannte/Standardparameter. Bei 1.0 wird das nicht passieren, weil es eine Reihe von Kernfunktionen für Sprachen gibt, die entweder repariert oder entfernt werden müssen, bevor man sich sehr unwesentlichen syntaktischen Zucker ausdenkt.

'träumen' klingt wie etwas Fantasievolles, aber diese Eigenschaften wurden in anderen Sprachen bewiesen. Es ist nicht nur Syntaxzucker - es reduziert die Menge an Code, durch die Sie navigieren müssen, und reduziert die Anzahl der Symbole, die Sie lernen müssen

Bei 1.0 wird das nicht passieren, weil es eine Reihe von Kernfunktionen für Sprachen gibt, die entweder repariert oder entfernt werden müssen, bevor man sich sehr unwesentlichen syntaktischen Zucker ausdenkt.

Ich sage nicht, dass die anderen Sprachfunktionen, die behoben/implementiert werden müssen, nicht wichtiger sind; sie sind wahrscheinlich. Aber es ist kein entweder/oder. Ich sage nur, dass diese Funktion für 1.0 (zusätzlich zu den anderen Funktionen) stark in Betracht gezogen werden sollte, da sie die Qualität der APIs in der Standardbibliothek _für immer_ nicht beeinträchtigt.

Dies ist wahrscheinlich eine umstrittene Meinung, aber IMO-Programmiersprachen leben und sterben mehr von den APIs, die sie bereitstellen, als von den Kernfunktionen in ihnen. Pythons "Batterien enthalten"-Std-Bibliothek verkaufte die gesamte Sprache. CPAN hält Perl am Leben. .Net macht das Schreiben von C#-Code großartig und LINQ ist das Beste seit geschnittenem Brot. Einer der größten Fehler von C++ ist das Fehlen guter, standardisierter APIs. Etc.

Die APIs sind entscheidend für den Erfolg einer Sprache, daher sollten Funktionen, die die Erstellung von _guten_ APIs ermöglichen, nicht als „unwesentlicher syntaktischer Zucker“ verworfen werden.

@dobkeratops : indem ich _dreaming up_ sage, versuche ich zu betonen, dass kein vollständiger Vorschlag gemacht wurde, also nicht in einem Entscheidungsschritt

Diese Diskussion ist unproduktiv. Damit es nicht noch mehr Zeit beansprucht, schließe ich das Thema. Wenn jemand es in einem Jahr oder später wieder öffnen möchte (oder eine neue Ausgabe beginnen möchte), ist das in Ordnung.

Wenn jemand einen Vorschlag mit fast allen ausgearbeiteten Details (Grammatik, Semantik) macht, schlage ich vor, ihn an die Mailingliste zu senden. Wenn Konsens darüber herrscht, dass dies der _richtige Weg_ ist, dann ist es sinnvoll, ein Issue zu eröffnen und es als experimentelles Feature hinter einem Flag wie once Funktionen zu implementieren.

Ich zweite @thestinger -- wenn jemand (oder eine kleine Gruppe von Leuten) einen vollständigen Vorschlag hat oder einen Vorschlag mit ein paar klar formulierten leeren Stellen, die zur Diskussion stehen, wäre es angemessen, dass diese Person ihn leitet durch die Mailingliste. Das ist kein Versprechen, dass wir diesen Vorschlag umsetzen werden, aber die Arbeit, die Idee zu formalisieren und zu erklären, wie sie mit anderen Sprachfunktionen interagiert, würde den Wert des Vorschlags erheblich steigern.

@thestinger @catamorphism Danke euch beiden, dass ihr aufgeschlossen seid! Wenn ich Zeit finde, erstelle ich einen Vorschlag und schicke ihn an rust-dev.

Ich habe ein Pad erstellt, um über diese Funktion zu diskutieren und eine klare Spezifikation darüber zu schreiben: https://pad.riseup.net/p/hvbg6dQQnEe7

Ich öffne das wieder. Es hat keine Priorität - es gibt schon viel zu viel zu tun - aber es ist eine Funktion, die die Leute wollen und die nicht ganz vom Tisch ist. Ich möchte kein Gespräch zu diesem Thema beenden.

@brson Danke!

Ich habe das ursprüngliche Problem mit einem Link zum Etherpad bearbeitet.

Hallo.
Seit der Pad-Erstellung wurde es durch viele Designvorschläge, Fragen, Probleme usw. vervollständigt...
Also habe ich ein zweites Pad erstellt, um das Diskussionspad "zusammenzufassen", dieses Pad wird verwendet, um die Funktionsanfrage genau zu beschreiben.
Pad-URL hier: https://pad.riseup.net/p/Ca5PBeDjUGxW

@KokaKiwi alle Pads sind jetzt leer. Wo sind die Inhalte geblieben?

@cmr , ich vermute:

WARNUNG: Dieses Pad wird GELÖSCHT, wenn 30 Tage ohne Änderungen vergangen sind. Es gibt KEINE MÖGLICHKEIT, das Pad wiederherzustellen, nachdem dies passiert ist, seien Sie also vorsichtig!

:die Stirn runzeln:

Ich durchsuche das Pad, das ich auf der Mozilla Etherpad-Instanz erstellt habe (um diesen Fall zu verhindern), aber ich kann es nicht in meinem Verlauf finden und habe vergessen, den Link hier zu veröffentlichen:(

@cmr @huonw @KokaKiwi , hier sind zwei Links in meinem Browserverlauf:

https://etherpad.mozilla.org/CQEDa85jLX
https://etherpad.mozilla.org/78FA1bozLd

@dram Das habe ich gesucht! Danke :smiley:
Vielleicht sollte das Thema mit den neuen Links bearbeitet werden, denke ich.

(Bearbeitet.)

Ich habe nichts Sinnvolles hinzuzufügen, außer dies ist eines der Dinge, die ich genau beobachte, bevor ich mehr Zeit in die Sprache investiere. Ausgehend von Sprachen (Objective-C und Python), die über diese Funktion verfügen, glaube ich, dass benannte Parameter eine indirekte große Steigerung der Produktivität sind, da sie dazu führen, dass der Code anderer Leute besser lesbar wird.

Ein konkreter Vorschlag sollte über den neuen RFC-Prozess erfolgen: https://github.com/rust-lang/rfcs

Es gibt hier zu viele widersprüchliche Ideen und divergierende Gesprächsstränge, als dass es ein nützlicher Diskussionsbereich wäre.

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen