Godot: Optionale Eingabe in GDScript

Erstellt am 25. Aug. 2017  ·  38Kommentare  ·  Quelle: godotengine/godot

Ich weiß nicht, ob dies für einen Entwickler erforderlich oder interessant wäre, aber ich denke, es könnte nützlich sein, optionale Eingaben in GDScript zu haben, und hoffte, hier eine Diskussion darüber zu führen.

Ich habe die Programmiersprache "Nim" gelernt, weil sie ein ziemlich freundliches Modul für GDNative hat und ich daran interessiert bin zu lernen, dort einen Beitrag zu leisten (da C++ für mich visuell schwierig ist, ABER nichts damit zu tun hat, nur etwas Hintergrund). Nim ist statisch typisiert, von dem ich weiß, dass GDSCript dynamisch ist, aber ich bin auf eine Situation gestoßen, in der ich möglicherweise den Typ angeben möchte, um die Dinge zu vereinfachen.

Vor allem beim Schreiben einer Funktion wäre es schön, wenn ich den Typ einer Variablen angeben könnte, die übergeben werden darf, damit die Codevervollständigung weiß, wovon ich spreche, aber ich bin mir sicher, dass es noch andere Vorteile gibt Dies.

Etwas wie

func myfunction(a, b : int, c : InputEvent , d : String = "Default Value"):
    (...)

Wo a dynamisch ist, sind bd statisch, zeigen aber, wie die Syntax meiner Meinung nach aussehen könnte.

Ich weiß, dass Sie den Typhinweis für den Editor angeben können, wenn Sie das Schlüsselwort export verwenden, aber Sie müssen die Variable nicht auf diesem Typ belassen, sobald das Skript ausgeführt wird. Diese Idee könnte gegen das sein, was GDScript sein soll, oder zu viel Arbeit für sehr wenig Gewinn, aber lass uns reden.

discussion feature proposal gdscript

Hilfreichster Kommentar

Dies sollte in Godot 3.1 verfügbar sein.

Alle 38 Kommentare

Es sieht so aus, als könntest du es tun

func myfunction(something = InputEvent):

und das gibt Ihnen ein leeres Eingabeereignis, wenn sie nichts übergeben, aber erzwingt nicht, dass die Variable dieser Typ ist

myfunction('string')

würde gut funktionieren.

@Hinsbart
Irgendwelche Hinweise darauf, wie es aussehen könnte oder wie es mit anderen Teilen von GDScript funktioniert? Oder ist es nur eine Idee auf der Roadmap?

Leider nein, außer einigen IRC-Diskussionen gibt es dazu noch keine Referenzen.

CC @reduz , um einige Hinweise zu geben, was er sich dabei

@LeonardMeagher2 sollte so aussehen, wie du es geschrieben hast

Wie wäre es mit:

func myfunction(something is InputEvent, something_else is String):

Das finde ich ziemlich gut, macht das Schlüsselwort is nützlicher und den Code verständlicher

Ich bin nicht einverstanden. Mir wäre entweder String something oder something : String . something is String sieht für mich schlecht aus.

String something oder something is String beides gut für mich. Die Verwendung eines Doppelpunkts führt meiner Meinung nach zu vielen Doppelpunktzeichen im Code.

Wenn is nicht verwendet wird, würde ich String something bevorzugen, da ich diese Notation aus anderen Sprachen gewohnt bin und ich denke, dass sie häufiger vorkommt.

@neikeq
Wenn Sie den Typ auch beim Erstellen einer Variablen angeben könnten, z. B. var something:String = " " würde ich zustimmen

Ansonsten denke ich, dass es verwirrender wäre, das Semikolon nur in Funktionsdefinitionen zu verwenden.

"Is" wird jetzt verwendet, um in gdscript nach der Vererbung zu fragen, also warum konnte es nicht erforderlich sein?

C wie Typpräfix wäre IMO besser. Es ist kürzer, schneller zu tippen und kann bei der Verwendung von Typen var oder func ersetzen.

Beispiel:

int some_variable

int some_function(int some_argument):
    return some_variable + some_argument

Meine Stimme dazu.

@juan-garcia-m
Ich glaube nicht, dass es für jemanden verständlicher wird, der nicht mit c wie dem Tippen vertraut ist. Ich denke, es ist vielleicht einfacher zu tippen, aber viel weniger lesbar.
Ich bevorzuge es, auf einen Blick zu wissen, was eine Funktion oder Variable ist, und das Ersetzen dieser Schlüsselwörter durch Typen würde diese Fähigkeit für mich entfernen; Dies ist einer der Gründe, warum C und C++ für mich schwierig sind.

Offensichtlich kann die Sprache nicht für mich gemacht werden, aber ich bin sicher, dass ich nicht der einzige bin, dem das so geht.

Ein Problem mit "is" wird derzeit in 3.0 für das verwendet, was früher "extends" war, dh überprüft, ob ein Knoten zu einer Klasse gehört.

@Zireael07
Deshalb erscheint es mir angebracht, den Typ in einer Funktionsdeklaration zu überprüfen.
Extends wird immer noch am Anfang des Skripts verwendet, aber für die Typprüfung wird "is" verwendet (und typeof für Literale, denke ich).

Das Präfix im C-Stil ist im Allgemeinen komplizierter, und für diese Arten von Sprachen scheint der Standard die Variable : Typkonvention zu sein, die verwendet wird

Warum wurde das geschlossen? Es ist noch nicht umgesetzt...

Ich habe es geschlossen, weil es nur eine Diskussion war, aber ich werde es erneut öffnen, um den Fortschritt der gewünschten Funktion zu verfolgen

Ich hoffe wirklich, dass das Präfix im C-Stil überdacht wird. Aus meiner Erfahrung mit Typescript fühle ich mich beim Initialisieren von Variablen weniger produktiv und ich persönlich habe das Gefühl, dass dies zu weniger lesbarem Code führt. Das muss ich schreiben...

var foo: String = 'bar'

...anstatt nur das Schlüsselwort var durch den Typ zu ersetzen...

String foo = 'bar'

...macht mich verrückt. Ich bin mir bewusst, dass das sehr klein und pingelig klingt, aber auf lange Sicht an einem Spiel führt dies zu viel mehr Tastenanschlägen als nötig.

So sollte es aussehen:
godot windows tools 64_2017-12-12_17-21-27

Nach der Python-Syntax (auch ähnlich wie TypeScript und Haxe).

Das ist ziemlich eigensinnig. Ich habe zum Beispiel viel TypeScript-Code geschrieben und mag die Syntax sehr. Wie bei Tabs im Vergleich zu Leerzeichen hat jeder seine eigene Meinung darüber, was am besten ist, normalerweise in Bezug auf seinen Hintergrund. Ich finde es einfacher, var und zu wissen, dass es eine Variable ist, anstatt String und nicht zu wissen, ob es eine Funktion oder eine Variable ist. Ich sehe "schneller zu tippen" nicht als gutes Argument, "schwerer, einen Fehler zu machen" und "leichter zu lesen" sind ziemlich besser (was jedoch immer noch Meinungen unterliegt).

Ein weiterer Punkt für Python: Es ist viel einfacher zu analysieren. Der aktuelle Parser muss nicht viel ändern, um dies zu berücksichtigen. Bei den Typen mit Präfix muss der Kontext noch mehr berücksichtigt werden, um zu entscheiden, was zu tun ist.

Ja, die Lesbarkeit des Codes ist sehr subjektiv. Ich habe Ihr Beispiel so umgeschrieben, wie es mit der Syntax im C-Stil aussehen würde. _Ich_ finde den Code unten lesbarer und ich habe keine Probleme, zwischen Variablen und Funktionen zu unterscheiden.

extends Node

const int MY_CONSTANT = 0
String my_member_variable = "Value"

void my_empty_func():
    pass

float my_other_func(bool arg1, Node2D arg2, bool default = false):
    Vector2 local_variable
    int other_variable = 0

    return float(other_variable)

Ich muss nicht sagen, dass "schneller zu tippen" kein gutes Argument ist, da es sich direkt auf die Produktivität eines Entwicklers auswirkt. Der Python-Stil ist etwas umständlicher zu lesen und zu schreiben als der von C, umso mehr, da wir jetzt -> eingeben müssen, nur um einen Rückgabetyp zu definieren. Die einzigen Argumente, die ich für die Syntax im Python-Stil sehen kann, sind 1. mehr Ansprache für diejenigen, die mit Python vertraut sind, und 2. weil es einfacher zu implementieren ist, und ich denke, dass beides keine besonders guten Argumente sind. Ich behaupte nicht, dass es einfach zu implementieren ist, ich bin mir sicher, dass es schwieriger ist und höchstwahrscheinlich über meinem Können liegt, aber ich denke, es würde sich auf lange Sicht lohnen.

Wie auch immer, das ist nur meine zwei Pfennige wert : smile:. Bisher macht mir die Entwicklung in GDScript Spaß und ich freue mich darauf, ein Sound-Type-System zu haben.

Python unterstützt dies bereits, also denke ich, das Beste, was wir tun können, ist von dort zu kopieren

Für Leute, die das Schlüsselwort var hassen, könnte es möglich sein, die Nim-ähnliche Syntax hinzuzufügen, die so etwas zulässt.

  a:int
  b:float
  c:char

const
  PI:float = 3.1416
  MAX:int = 100

Ich frage mich, warum Rückgabetypen benötigt werden, wenn Sie zurückgeben und diese Ausgabe an eine Variable eines bestimmten Typs geht und sie nicht übereinstimmen, würde es dann geworfen.
Ich denke, Sie würden die Möglichkeit des Editors erhalten, diesen Fehler für Sie zu finden, bevor Sie Ihren Code ausführen.

Der Rückgabetyp void macht für mich nicht viel Sinn, stelle ich mir vor, in diesem Fall wäre es einfacher, die Definition des Rückgabetyps einfach wegzulassen.

Schließlich kommt mir das -> komisch vor, wenn wir Rückgabetypen haben würden, aber ich verstehe, dass Sie vielleicht nicht so etwas tun wie func somefunc(): String: (vielleicht?), aber ich bin kein Fan dieser pfeilartigen Syntax, da sie mir auch nicht viel sagt, scheint direktional zu sein, hat aber nichts damit zu tun, erinnert mich an bitweise Operationen >> und << aber diese sind in gewisser Weise richtungsweisend.

__ wollte das Problem nicht schließen__

Ich kann sehen, dass Rückgabetypen in einigen Fällen als wichtig bekannt sind, da es jetzt möglich sein wird, jede Funktion für einen Knoten, seine Variablen und seinen Rückgabetyp zu lesen.

Es mag jetzt nicht viel Sinn machen, aber die Leute werden in der Lage sein, Plugins und sogar Spielcode zu schreiben, der in seiner Funktionsweise extrem dynamisch sein kann. IE, ein Plugin könnte diese Warnmeldung haben: "Dieser Knoten erfordert eine Funktion mit dem Rückgabetyp x, aber der Knoten, auf den Sie verweisen möchten, hat keine Methode mit der erforderlichen Methodensignatur."

Ich habe ein Plugin für Unity namens AI Behavior geschrieben, das aus Gründen der Benutzerfreundlichkeit ziemlich viel von dieser Art von Sachen (über System.Reflection) verwendet. Ich habe buchstäblich ein Dropdown-Menü, das ich mit verfügbaren Methodennamen fülle und sie auswählen lässt, welche Methode sie als Rückruf verwenden möchten, basierend auf allen Methoden, die die richtige Signatur haben der Umstand, für den es gemacht ist. Wenn für das Objekt/Skript, auf das sie zugreifen möchten, keine Methode vorhanden ist, wird im Inspektor Beispielcode angezeigt, sodass sie einfach ein Skript erstellen können, indem sie den Code kopieren/einfügen und die Klasse umbenennen. :)

Mein Gedanke ist also, dass Godot für Plugin-Autoren so viel erweiterbarer wird, wenn man so viele Skriptinformationen wie möglich haben kann.

Ich denke auch, dass die Syntax im TypeScript-Stil (mit var name: String = "Godot" ) vorzuziehen ist, da sie zu einer Standardsyntax für optionale Eingaben zu dynamischen Sprachen geworden ist.

Auch die folgende Python-Syntax ist eine gute Idee:
https://www.python.org/dev/peps/pep-0484/
https://docs.python.org/3/library/typing.html

Dies wird Benutzern helfen, die aus ähnlichen dynamischen Sprachen mit optionaler Eingabe zu GDScript kommen.

Übrigens, wann / in welcher Version können wir damit rechnen, dass dies implementiert wird? Ich habe 3.1 erwähnt gesehen?

Dies sollte in Godot 3.1 verfügbar sein.

Über die Argumente gegen das Präfix im C-Stil, das komplizierter oder weniger verständlich / lesbar ist und es einladender ist, mit Python-Syntax oder ähnlichem zu arbeiten - verwenden die Dokumente nicht alle diese Art von Präfix im C-Stil mit allen Methoden und so? Zum Beispiel, um schnell eine von der Kameraseite zu ziehen set_orthogonal ( float size, float z_near, float z_far ) Ich habe es zuerst selbst nicht ganz verstanden, aber es dauert nur einen Moment, um es zu lernen, denke ich, und danach macht es absolut Sinn.

Hallo, Vorschlag hier.

Begründung:
Ich denke, wir brauchen statische Eingaben, aber ich würde es vermeiden, den Benutzer nach Möglichkeit zur Angabe von Typen zu zwingen.
Alle wichtigen kompilierten Sprachen bewegen sich in Richtung statischer Typinferenz.
Ich weiß, dass @reduz Typrückschlüsse für den Godot-Quellcode nicht wirklich mag, und ich kann es aus Gründen der Klarheit verstehen. Ich denke jedoch, dass dieser Kompromiss für GDScript wirklich gut funktionieren kann, da er die Codierungsgeschwindigkeit nicht so stark behindert, mit den meisten Vorteilen der statischen Typprüfung.
Ich bin mir jedoch nicht sicher, ob die Implementierung komplizierter ist.
Dies würde einige zusätzliche Konstrukte als algebraische Datentypen erfordern.
Hier ist eine Sprache, die dieses Prinzip verwendet

https://crystal-lang.org/docs/

Um die Abwärtskompatibilität zu wahren, wird ein neues Schlüsselwort zum Definieren von Funktionen und Variablen benötigt. Oder vielleicht könnten wir einen magischen Kommentar haben, um die statische Typprüfung zu aktivieren/deaktivieren.

Hier ist ein Beispiel dafür, was diese Art von Typensystem tun kann (für diejenigen, die ein bisschen Rubin kennen)

def test(x)
    return true if x
    return "This is false"
end

puts test(false).upcase
puts test(true)

Ausgang:

Error in test.cr:6: undefined method 'upcase' for Bool (compile-time type is (Bool | String))

puts test(false).upcase
                 ^~~~~~

Die Gedanken?

Ich möchte den Typrückschluss verbessern, insbesondere für die Codevervollständigung, aber auch für die Überprüfung nicht vorhandener Eigenschaften/Methoden und der Anzahl ungültiger Argumente zum Zeitpunkt der Bearbeitung. Ich denke, dieser Teil ist ein zweiter Schritt, nachdem die optionale Eingabe abgeschlossen ist, also mache ich mir jetzt keine Sorgen darüber.

Das Beispiel von @m4nu3lf geht ziemlich weit, es @reduz mit GDScript gehen will.

Bedenken Sie, dass Godot ein ziemlich strenges Typsystem hat, aber alle Variablen sind Variant (dh sie können jeden Typ speichern). Da dies nicht dazu gedacht ist, die Kompatibilität zu brechen, müssen sie so bleiben. Wenn Sie also var value = 42 tun, wird nicht automatisch davon ausgegangen, dass die Variable eine Ganzzahl ist, insbesondere bei Variablen auf Klassenebene, die von externen Skripten nach Belieben gesetzt werden können. Das behindert eine Menge, was Typinferenz bewirken kann (kann jedoch zu Vervollständigungszwecken so verwendet werden).

Eine Möglichkeit besteht, wie erwähnt, darin, dies optional zu machen. Vielleicht eine Projekteinstellung, da ich nicht viel Wert darin sehe, dass dies pro Skript aktiviert/deaktiviert wird. Dies ist jedoch ein separater Vorschlag, der in einem nächsten Schritt überlegt werden sollte.

@vnen Theoretisch Kompilierzeit ableiten.
Es handelt sich um statisches Tippen mit vollständiger Typinferenz.
Sagen wir, du hast
auto my_field = 42 in einem Skript könnten Sie einfach auf den Typ int und die zugrunde liegende Variante nur auf Ints beschränken.

Diese Sprache geht jedoch über einfache Variablen hinaus. Es kann den Typ abhängig von den Zweigen auf eine Teilmenge von Typen beschränken.
wie im obigen Beispiel kann der Rückgabetyp nur ein String ODER ein Bool sein.
Aber dafür musst du

  • Instanziieren Sie Funktionen für jede neue Kombination von Argumenttypen und leiten Sie die internen Typen für jeden Rückgabeausdruck ab, speichern Sie die Liste der möglichen Rückgabetypen als Typ des Rückgabewerts.
  • Für Verzweigungen alle möglichen Verzweigungen anzeigen, denen eine Variable zugewiesen ist, um alle möglichen Typen der Variablen zu einem bestimmten Zeitpunkt abzuleiten. Beschränken Sie den Typ der Variante jederzeit auf diese

Außerdem müssen Sie nach "Instanz von" Bedingungen suchen und wie in

if my_var instanceof MyClass:
...

Dann wissen Sie, dass my_var im Body der if-Bedingung nur 'MyClass' sein kann, sodass der Typ noch stärker eingeschränkt wird.
Damit dies von Vorteil ist, müssen Sie nur Methoden aufrufen oder auf Felder zugreifen können, deren Namen von allen möglichen Typen gemeinsam sind.
Sagen wir also

auto x = MyClass.new()
if (...):
   x = "hi"

Danach ist x entweder eine MyClass oder ein String. Sie können nur Methoden aufrufen, die beiden gemein sind, um Laufzeitfehler zu vermeiden.

Ich gebe zu, dass dies viel mehr Arbeit von dem ist, was ich ursprünglich dachte.
Daher möchte ich darauf zurückkommen. Vielleicht kann es in Zukunft implementiert werden, oder vielleicht kann ich hoffen, dass jemand für Crystal-lang verbindlich schreibt.
Auf jeden Fall finde ich es ein wirklich cooles Feature

@m4nu3lf Ich verstehe Ihre Anfrage, TypeScript ist sehr ähnlich (eine Sprache, mit der ich besser vertraut bin).

Bei GDScript sind jedoch einige Probleme zu berücksichtigen:

  • Erstens ist es nicht trivial, das zu implementieren. Ich kann mir ansehen, wie es andere Sprachen machen, aber es würde trotzdem lange dauern. Deshalb möchte ich einen Schritt nach dem anderen gehen und später darüber nachdenken. TypeScript wurde von demselben Mann erstellt, der auch Turbo Pascal und C# entwickelt hat, also habe ich dort eine Menge Erfahrung, die ich nicht habe.
  • GDScript wird eine Datei nach der anderen kompiliert. Crystal und TypeScript erfordern beide, dass Sie das gesamte Projekt kompilieren, bevor Sie es verwenden. Das bedeutet, dass es etwas trüb ist, die Interaktion mit anderen Skripten und Singletons zu überprüfen.
  • Der Szenenbaum. Mit get_node() (was häufig verwendet wird) ist es einfach unmöglich zu wissen, welchen Knotentyp Sie erhalten. Sie wissen nur, dass es ein Node aber es kann jeden Untertyp haben. Gleiches gilt für Resources, wenn Sie load() aufrufen, insbesondere wenn Sie es mit einem dynamischen Namen aufrufen. _input(event) hat das gleiche Problem, da event jeder InputEvent-Subtyp sein kann.
if my_var instanceof MyClass:
    ...

Dies ist bereits für die Codevervollständigung abgeleitet. Könnte für die eigentliche Typprüfung wiederverwendet werden. Gerade dieser Fall ist trivial, kann aber ziemlich verwickelt werden. Wenn Sie beispielsweise eine zweite Bedingung hinzufügen: if some value == 42 and my_var is Sprite , funktioniert die Codevervollständigung nicht mehr.


Ich möchte auch Feedback von Benutzern hören, daher ist es (für mich) wichtiger, bald etwas verfügbar zu haben, als nach langer Wartezeit alles perfekt zu haben.

Zusammenfassend: Ich möchte die Typinferenz verbessern, aber sie muss Schritt für Schritt mit kleinen Verbesserungen vorgenommen werden. Sobald die optionale Eingabe verfügbar ist, geht es für den Rest bergauf.

Warum ist das geschlossen?

Ein bisschen falsch gelesen, mein Fehler.

Zurück zum Präfix und Postfix: UML und Kotlin verwenden beide ": type".
Basic hat auch "DIM a as INT" gemacht (und "as" anstelle von ":" oder "is" verwendet)

@programaths Es ist lange her und es wurde immer wieder diskutiert. Außerdem gibt es eine PR, die Postfix implementiert, also kommt es jetzt ein bisschen nicht in Frage :smiley:

Soll ich das schließen?

@LeonardMeagher2 Es wird automatisch geschlossen, wenn mein PR zusammengeführt wird.

@bojidar-bg Ich wollte nur einige Stellen hinzufügen, an denen Postfix verwendet wird, weil es an sich interessant ist. UML ist auch für Analyse- und Nicht-Techniker gedacht. Kotlin ist eine Sprache, die entwickelt wurde, um komfortabler und moderner als Java zu sein und gleichzeitig "effektives Java" zu implementieren.

Wenn Sie also aufschreiben müssen, warum Postfix gewählt wurde, können diese auch aufgenommen werden. Manchmal möchten die Leute Gründe haben und technische Gründe einfach ablehnen, es sei denn, Sie beziehen sie auf den Benutzer.
(zB "Parser kann Mehrdeutigkeiten früher auflösen und hat eine Geschwindigkeit von 100x, was bedeutet, dass das gleiche Programm schneller kompiliert werden kann und mehr Tests ermöglicht)

Es ist immer gut, Material zu haben, oder? ;-)

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen