Was stört/fehlt euch bei aktuellen Programmiersprachen?



  • Ethon schrieb:

    Ich verstehe das Argument gegen Exceptions nicht. Warum sind Exceptions schlecht nur weil der Programmierer zu faul ist sie am richtigen Ort zu behandeln?
    Aber lieber ne Exception die bis zu main durchfliegt als ignorierte Fehlerbehandlung in den tieferen Funktionen was zu komplett undefinierten Verhaltens des Programm führt.
    Lieber ne ConfigNotFound Exception als ein Programm das am Rad dreht weil es meint ne Config eingelesen zu haben obwohl gar keine da war.

    Die Frage geht wohl an mich.

    Im Grundgedanken liegst Du richtig. Exceptions sind ja auch nicht von dummen Leuten ohne Grund eingeführt worden. Genau das ist das Konzept.

    Es gibt zwei Strategien Sprachfeatures (das gilt also nicht nur für Exceptions) zu bewerten: Das Sprachfeature ist gut, wenn sich der Mensch daran diszipliniert halten würde und das Sprachfeature ist schlecht, weil der Mensch der Mensch sich nicht diszipliniert daran halten kann.

    Exceptions haben nur dann einen Vorteil, wenn sie behandelt werden. Als Entwickler willst Du aber oftmals erstmal Deinen Algorithmus laufen haben, so dass sich unter try ein catch-All wiederfindet. Wenn Du mal professionell entwickelt hast, wirst Du weiterhin mal erlebt haben, dass laufend ein Projektmanager in der Tür steht und fragt, ob Du jetzt fertig bist und Dir neue Aufgaben gibt. Außerdem ist die aktuelle gerade nicht so wichtig und eigentlich ja auch fertig - läuft ja.

    Du wirst in dem Code also reichlich Catch-All finden - die Exception fliegt also nicht zu main durch. Damit sind sämtliche Fehlerindizien weg. Aber das Programm eiert nun genauso rum, wie eine nicht abgefangene Fehlerbehandlung. Du erhältst also irgendwo eine valide Exception, die Dich darauf hinweist (idealerweise kommt wenigstens die zweite durch, aber muss ja nicht). Das ganze wird also recht willkürlich, weil die Exception in einem Bereich auftritt, der mit (eigentlich) validen Daten gefüttert wird auch perfekt getestet ist. Das Catch-All verheimlicht aber, dass es einen Fall gibt, wo die Daten nicht valide sind. Das findet kein Mensch mehr, besonders, wenn erst die dritte oder vierte Exception in der Reihe zum User durchdringt.

    Debuggt man durch Exceptions findet man sich im Schadensfall nicht mehr an der Stelle, wo der Fehler passiert ist. Der Stackframe ist weg. Eventuell löst der Abbau des Stackframes auch neue, andere Exceptions aus.

    Auch die Catch-Anweisung ist fehleranfällig. Gerne werden Exceptions voneinander abgeleitet. Ein Catch behandelt dann auf einmal eine Oberklasse eines Fehlers, ist aber auf die Spezialisierung gar nicht ausgelegt. Der Fehler wird behandelt, aber eben nicht korrekt. Eclipse fördert das auch, in dem es vorschlägt, statt mehrerer Spezialisierungen diese durch die Oberklasse zusammen zu fassen. Bei catch kommt dann nur noch "allgemeiner Programmfehler" an, der muss behandelt werden und das tun die Leute auch. Nur weiß halt keiner, was er eigentlich behandeln müsste und schon haben wir wieder ein Catch-All, weil der Algorithmus muss fertig werden.
    Exceptions waren in meiner bisherigen Karriere für mehr Fehler verantwortlich als die eigentlichen Algorithmen, deren Fehlerbehandlung sie übernehmen sollten.

    Wenn man nun erklärt, dass der Programmierer schuld ist, weil er Exceptions falsch benutzt, dann ist das korrekt. Meiner Erfahrung nach ist die Fehlerbehandung mit Exceptions in größeren Projekten nicht mehr möglich, weil sie sich über das ganze Programm verstreut, statt sich in dem kleinen Rahmen abzuspielen, der für genau einen Funktionsaufruf gilt. Hier kann ich diszipliniert arbeiten.

    Ich gehe den zweiten Ansatz. Der Programmierer kann ein großes Projekt nicht überblicken, also muss ich Konstrukte vermeiden, die ihm zu Fehlern drängen. Exceptions gehören für mich dazu.



  • Xin schrieb:

    Kellerautomat schrieb:

    Dann hast du aber in jeder Funktion Fehlerbehandlungscode, was nicht nur ineffizient, sondern auch unglaublich mühsam ist. Was machst du eigentlich in Konstruktoren?

    Zum einen ist es nicht ineffizient, denn Du musst so oder so fragen, ob das Ergebnis einen Sinn ergibt, wenn Du - falls Du unzufrieden bist - throw rufen möchtest.

    Außerdem kostet try...catch auch dann, wenn alles super ist.

    Wenn ich davon ausgehen kann, dass im Fehlerfall eine Exception geworfen wird, muss ich gar nichts prüfen.
    Bei einem ordentlichen Compiler kosten Exceptions gar nichts, solange sie nicht geworfen werden. Siehe http://lazarenko.me/2011/07/22/c-exception-handling-and-performance/.

    Xin schrieb:

    Konstruktoren sind bei mir im Prinzip normale Funktionen.

    Das heisst, sie können etwas zurückgeben? Ruft man die explizit auf?

    Xin schrieb:

    Kellerautomat schrieb:

    Ich bin mir nicht sicher, ob ich einen int*? überhaupt erlauben soll, ähnlich wie man in C++ keinen Zeiger auf eine Referenz erzeugen kann.
    Ich halte die Semantik zumindest für fragwürdig und denke, dass das Verwirrung stiften würde.

    Dann baust Du Sonderbedingungen ein.

    Nur weil ich definiere, was int*? bedeutet, wird das Konstrukt nicht besser. Wenn sich jedes Mal jemand fragt, was die genaue Semantik davon ist, ist es besser, es gleich nicht zu erlauben.

    Xin schrieb:

    Kellerautomat schrieb:

    Bei Klassentypen ist Nullable auch nützlich: Man erkennt am Interface, ob eine Funktion null entgegennehmen/zurückgeben kann, oder nicht. Das füehrt dazu, dass viel null-Behandlungscode wegfällt, der in z.B. Java üblich ist.

    Wow, das klingt echt gut!

    In der PM stand 2005 ein Bericht, dass Autos per Funk den Ampeln ein Signal geben können, so dass die Ampeln rechtzeitig auf Grün schalten können, damit man nachts nicht einsam und verlassen an einer roten Ampel stehen müsste. Das ist natürlich Zukunftsmusik bis alle Fahrzeuge mit dem passenden Sender ausgestattet sind und die Ampeln mit dem entsprechenden Empfänger.

    Ein Leserbrief erklärte, dass man die Ampeln auch abschalten kann und sich die Autofahrer an den daran befestigten Schildern über die Vorfahrt informieren könnte.

    Bau Dir kein aufwendige Problemlösung für ein bereits perfekt gelöstes Problem:
    Eine Nullable-Reference heißt in C++ Pointer.

    Ich kann deiner Analogie leider nicht folgen.
    Pointer möchte ich, soweit möglich, ersetzen. Nicht, dass ich Pointer für schlecht halte, aber es gibt für die meisten Fälle sicherere Alternativen. Zumal Objekte, wie schon gesagt, per default Reference-Counted sind - ein roher Pointer hat da nicht die richtigen Kopiersemantiken.

    Und nur, um es nochmal hervorzuheben: Eine Referenz ist bei mir - anders als in Java - NICHT nullable. T? ist das, was T in Java ist - mit dem Unterschied, dass es auch mit Primitiven funktioniert.

    Xin schrieb:

    Ich habe bei CSS abgeguckt.

    x: 42;
    

    := ist mir zu lang, dafür wird die Wert-Zuweisung zu oft benutzt.

    Der Vergleich ist ==. Die Gleichsetzung (enums, flags) erfolgt mit =. Grundsätzlich versuche ich den = Operator allerdings eher klein zu halten, weil es die Leute so oder so schon nerven wird, wenn sie Zuweisungen mit = eingeben und dann Fehlermeldungen erhalten. Dann sollten sie wenigstens aussagekräftig und unmissverständlich sein. Das wird schwierig, wenn = im Alltag nicht klar aussortiert werden kann.

    Gefällt mir persönlich überhaupt nicht. Ist halt Geschmackssache.

    Xin schrieb:

    Kellerautomat schrieb:

    Weil ich finde, dass eine Unterscheidung zwischen Initialisierung und Zuweisung den Code klarer macht. Und wenn, dann verwende ich sie konsistent.

    Eigentlich finde ich den Unterschied als Entwickler uninteressant.
    Es wäre nur schön, wenn das Objekt existiert, wenn ich es erstmal nutze.

    Kellerautomat schrieb:

    if y % 2 == 0
    			.y := 42 // nope, bedingte Initialisierung nicht erlaubt --> compiletime error
    

    Genau das will ich haben. Bedingte Initialisierung.

    Das glaube ich nicht. Ich glaube eher, du willst abhängig von einer Bedingung mit einem von zwei Werten initialisieren. Das kannst du mit einer temporären Variable oder ?:.

    Xin schrieb:

    Kellerautomat schrieb:

    Auf Member greift man explizit mit der .member Syntax zu. In C++ gibt es verschiedene Namenskonventionen, etwa m_Member oder member_. Damit baue ich das fest in die Sprache ein.

    Hehehe, hast Du Zugriff auf mein Wiki? 😃
    Das habe ich mir bei Visual Basic abgeguckt, wenn ich mich recht entsinne.

    Du benutzt diese .member Syntax hier bei Funktionen?

    VB kann ich nicht mal, nie gelernt. Ich kam von selbst auf die idee :p

    .member lässt sich innerhalb von Methoden, Konstruktoren und Destruktoren verwenden. Bzw., es muss verwendet werden.



  • Bezüglich Exceptions:

    Ich verstehe das Beispiel mit loadConfig . Wenn die Datei nicht da ist, muss sich die Funktion darum kümmern, irgendwo anders die Config herzubekommen, oder eine neue anzulegen, oder ähnliches. Es wäre falsch, wenn die Exception einfach durchrauscht. Aber loadConfig ruft getFileContents auf und das wiederum openFile . Und was soll nun getFileContents machen, wenn openFile einen Fehler erzeugt? Es kann diesen nur weiter geben, es ist gar nicht in der Position, selbstständig Entscheidungen zu treffen.

    In der Regel gibt es sehr viele dieser Hilfsfunktionen. Und in diesen ist das Standardmuster meistens das folgende:

    func1();
    if(fehler) return fehler;
    func2();
    if(fehler) return fehler;
    usw.
    

    Exception bieten genau das als Voreinstellung, auch über mehrere Ebenen hinweg. Und auf den oberen Ebenen können die Funktionen, die die Anwendungslogik abbilden (z.B. loadConfig ) die Exceptions fangen und basierend darauf Entscheidungen treffen, wie zu verfahren ist.

    Fehlerbehandlung über Rückgabewerte (wie z.B. in Go) auf der anderen Seite erzwingt den Programmierer, die if -Aufrufe aus dem Pseudocode oben immer explizit zu schreiben. Laut meiner Erfahrung macht man das in der Regel nicht, insbesondere, wenn der Fehler unwahrscheinlich ist. Standardbeispiel: wer prüft den Rückgabewert von printf ? Gerade wenn man den Algorithmus erstmal laufen haben will, lässt man die Fehlerbehandlung gerne weg. Das ist hier aber drastischer als im Exception-Fall. Wenn ein Fehler auftritt, ist der Programmzustand in der Regel inkonsistent. Exceptions brechen dann ab und führen das Programm erst dann weiter, wenn jemand meint, sich um den Fehler kümmern zu können. Der Fehler-Rückgabewert wird aber ignoriert und die Programm arbeitet auf einen inkonsistenten Zustand weiter, der Bug taucht dann in der Regel an einer gänzlich anderen Stelle auf.

    Fehler über Rückgabewerte haben den Vorteil, dass der Fehler nicht durchs ganze Programm fliegt, aber sie führen zu verschleppten Bugs und außerdem zu unhandlicherem Code. Aus

    a();
    b();
    c();
    

    wird

    if(err = a()) return err;
    if(err = b()) return err;
    if(err = c()) return err;
    

    Aus

    return parseConfig(readFileContents(filename))
    

    wird

    content, err = readFileContents(filename);
    if(err) return nil, err;
    conf, err = parseConfig(content);
    if(err) return nil, err;
    return conf, nil;
    

    Die Fehlerbehandlung wird monoton und man fragt sich, ob einem das nicht abgenommen werden kann.

    Im Prinzip kann man die Vorteile vereinen und sowohl lokale Fehler und compilergeprüfte Fehlerbehandlung als auch elegenten Code, der nicht mit Fehlerprüfung durchsetzt ist, haben. Wie gesagt durch Sum-Typen und Monaden. Ich kenne keinen anderen Weg, lasse mich aber auch gerne eines besseren belehren. Insbesondere, Xin, würde mich interessieren, wie du dir das vorgestellt hast und wie du die Probleme umgehen möchtest.



  • Kellerautomat schrieb:

    Xin schrieb:

    Kellerautomat schrieb:

    Dann hast du aber in jeder Funktion Fehlerbehandlungscode, was nicht nur ineffizient, sondern auch unglaublich mühsam ist. Was machst du eigentlich in Konstruktoren?

    Zum einen ist es nicht ineffizient, denn Du musst so oder so fragen, ob das Ergebnis einen Sinn ergibt, wenn Du - falls Du unzufrieden bist - throw rufen möchtest.

    Außerdem kostet try...catch auch dann, wenn alles super ist.

    Wenn ich davon ausgehen kann, dass im Fehlerfall eine Exception geworfen wird, muss ich gar nichts prüfen.
    Bei einem ordentlichen Compiler kosten Exceptions gar nichts, solange sie nicht geworfen werden. Siehe http://lazarenko.me/2011/07/22/c-exception-handling-and-performance/.

    Zwei Kommentare dazu:

    1. try und die } nach try kosten Zeit, denn es muss festgelegt werden wo throw im Fehlerfall hinspringen muss und wenn } erreicht wird, muss die Änderung zurückgenommen werden, damit zum übergeordneten catch-Block gesprungen wird. Scott Meyer beschreibt das in Effektive C++, keine Ahnung in welchem Band.
      Wenn Du dir Gedanken darüber machst, wie Du eine Sprache implementierst, muss Dir klar sein, dass Du in einem try anders agierst als außerhalb und diese Änderung musst Du im Programm vermerken und das gibt's nunmal nicht kostenlos.

    2. Das Beispiel mit den zwei Divisionen ist semantisch nicht gleich, entsprechend auch nicht vergleichbar. Es ist vergleichbar unter der Annahme, dass der konstruierte Fall geeignet wäre, einen objektiven Vergleich zu gestalten. Das Exception-Beispiel entspricht nicht dem Beispiel mit dem Rückgabecode, sondern nur dann, wenn der erste Aufruf von divide die gleiche Fehlerbehandlung benötigt, wie der zweite.

    Erzeugt die erste Division eine Statusänderung im Programm, so muss die Fehlerbehandlung der zweiten Division das berücksichtigen. Das ganze funktioniert mit zwei divide()-Funktionen, so dass es gut aussieht. Ich mache keine gut aussehenden Features. Ich will praktische Features.

    Musst Du die Statusänderung zurücknehmen, zerpflückt sich Deine Fehlerbehandlung. Entweder beginnst Du mit temporären Variablen zu jonglieren oder Du baust ganz sauber um beide divides getrennte try-Catch-Blöcke packen. Viel Aufwand, nur um einen Rückgabecode zu prüfen.

    Ansonsten reicht für das Beispiel if( divide(..) && divide(..) ) {} else { einmalige Fehlerbehandlung. }

    Der Artikel hat sich einen Show-Case rausgesucht in dem mehrere Annahmen implizit so getroffen werden, dass try-catch sinnvoll erscheint. Ich treffe meine Entscheidungen lieber aus Schwächen in Produktivcode. Meine Erfahrung und darauf aufbauend meine Entscheidung ist, Ausnahmen regelmäßig zu verwenden, ist ziemlicher Mist.

    Ich gebe zu, den Rest habe ich mir nicht mehr angeguckt. Die Argumentation hatte ich schon etliche Male und da der Autor mit dem gleichen Blödsinn eröffnet erwarte ich später nichts Neues.

    Kellerautomat schrieb:

    Xin schrieb:

    Konstruktoren sind bei mir im Prinzip normale Funktionen.

    Das heisst, sie können etwas zurückgeben? Ruft man die explizit auf?

    Die Möglichkeit gibt es.

    Bzgl. des Aufrufs spiele ich noch mit einer Reihe von Möglichkeiten.

    Kellerautomat schrieb:

    Nur weil ich definiere, was int*? bedeutet, wird das Konstrukt nicht besser. Wenn sich jedes Mal jemand fragt, was die genaue Semantik davon ist, ist es besser, es gleich nicht zu erlauben.

    Das sehe ich anders, hier käme Willkür ins Spiel. Aber da ich kein int*? in der Semantik habe, also auch kein int?, stellt sich die Frage für mich nicht.

    Im Gegensatz zu uninitialized<int> sehe ich nullable<int> tatsächlich als Library-Feature, denn für alle Datentypen lässt sich das notfalls über einen Zeiger ausdrücken, der immer Nullable ist. In C++: int const *: "Da steht das gewünschte int - oder es gibt keins."
    Das ist bei int nicht so schön, wie int?, aber funktioniert für alle KlassenObjekte absolut in Ordnung. Für den seltenen Sonderfall der Primitive baue ich kein eigenes Konstrukt.
    Die Notwendigkeit für int? sehe ich nicht.

    Aber sollten unsere Sprachen weiterhin so große Ähnlichkeit aufweisen, haben wir schonmal einen unbedeutenden Unterschied 😉

    Kellerautomat schrieb:

    Xin schrieb:

    Kellerautomat schrieb:

    Bei Klassentypen ist Nullable auch nützlich: Man erkennt am Interface, ob eine Funktion null entgegennehmen/zurückgeben kann, oder nicht. Das füehrt dazu, dass viel null-Behandlungscode wegfällt, der in z.B. Java üblich ist.

    Wow, das klingt echt gut!

    In der PM stand 2005 ein Bericht, dass Autos per Funk den Ampeln ein Signal geben können, so dass die Ampeln rechtzeitig auf Grün schalten können, damit man nachts nicht einsam und verlassen an einer roten Ampel stehen müsste. Das ist natürlich Zukunftsmusik bis alle Fahrzeuge mit dem passenden Sender ausgestattet sind und die Ampeln rmit dem entsprechenden Empfänger.

    Ein Leserbrief erklärte, dass man die Ampeln auch abschalten kann und sich die Autofahrer an den daran befestigten Schildern über die Vorfahrt informieren könnte.

    Bau Dir kein aufwendige Problemlösung für ein bereits perfekt gelöstes Problem:
    Eine Nullable-Reference heißt in C++ Pointer.

    Ich kann deiner Analogie leider nicht folgen.
    Pointer möchte ich, soweit möglich, ersetzen. Nicht, dass ich Pointer für schlecht halte, aber es gibt für die meisten Fälle sicherere Alternativen. Zumal Objekte, wie schon gesagt, per default Reference-Counted sind - ein roher Pointer hat da nicht die richtigen Kopiersemantiken.

    Hehehe, Du bist der Sprachdesigner. Du entscheidest welche Semantik ein Pointer hat.

    Kellerautomat schrieb:

    Und nur, um es nochmal hervorzuheben: Eine Referenz ist bei mir - anders als in Java - NICHT nullable. T? ist das, was T in Java ist - mit dem Unterschied, dass es auch mit Primitiven funktioniert.

    T ptr ist bei mir, was in Java T ist, was in C++ T * ist, bei Dir T?.
    T ist bei mir, was in Java nicht geht, was in C++ T &, bei Dir T.
    T copy ist bei mir, was in Java auch nicht geht, in C T und bei Dir...?

    ptr sieht erstmal scheiße aus. Das ist beabsichtig. In 95% kann man das ptr weglassen, also mit Referenzen arbeiten. Und genau das ist ja auch das Ziel. Wo kein Null reingehen kann, kann auch keine Null-Pointer-Exception/SegFault kommen.

    Kellerautomat schrieb:

    Genau das will ich haben. Bedingte Initialisierung.

    Das glaube ich nicht. Ich glaube eher, du willst abhängig von einer Bedingung mit einem von zwei Werten initialisieren. Das kannst du mit einer temporären Variable oder ?:.[/quote]
    Mit einer temporären Variable?

    Natürlich muss das Objekt initialisiert sein, wenn es erstmals initialisiert wird.
    Das muss die semantische Analyse halt erfassen.

    ipsec schrieb:

    Die Fehlerbehandlung wird monoton und man fragt sich, ob einem das nicht abgenommen werden kann.

    🙂



  • Xin schrieb:

    1. try und die } nach try kosten Zeit, denn es muss festgelegt werden wo throw im Fehlerfall hinspringen muss und wenn } erreicht wird, muss die Änderung zurückgenommen werden, damit zum übergeordneten catch-Block gesprungen wird. Scott Meyer beschreibt das in Effektive C++, keine Ahnung in welchem Band.
      Wenn Du dir Gedanken darüber machst, wie Du eine Sprache implementierst, muss Dir klar sein, dass Du in einem try anders agierst als außerhalb und diese Änderung musst Du im Programm vermerken und das gibt's nunmal nicht kostenlos.

    Das ist unter x86 so, unter x64 tun wir eine ip map verwenden. wir erkennen anhand der position des ip welchen zustand wir haben und was wir tun müssen wenn hier eine exception fliegt. kostet nur beim start der anwendung ein paar byte speicher zu laden.

    weiter habe ich dann nicht gelesen :p



  • Shade Of Mine schrieb:

    Das ist unter x86 so, unter x64 tun wir eine ip map verwenden. wir erkennen anhand der position des ip welchen zustand wir haben und was wir tun müssen wenn hier eine exception fliegt. kostet nur beim start der anwendung ein paar byte speicher zu laden.

    Warum braucht man dafür eine x64? Ich hatte mir etwas Vergleichbares ausgedacht, aber eben nicht mit try-catch und ich sehe keinen Grund weswegen das mit einem x86 nicht gehen sollte!?

    Punkt tut an Dich gehen tun. 😉
    Obwohl ich mir das eigentlich hätte denken können, dass man die Implementierung überdenkt, die anderen Compilerentwickler sind ja auch nicht blöd. Wieder was gelernt. 🙂
    Das macht throw allerdings noch teurer - oder muss man da auch noch was lesen? 😉

    Fällt ein Minus-Punkt gegen Exceptions weg. Blöderweise war das eher eine Randnotiz, denn der Aufwand ist/war gering, aber eben nicht kostenlos.

    Shade Of Mine schrieb:

    weiter habe ich dann nicht gelesen :p

    Dass Du die anderen Punkte nicht kommentiert hast, ist auch eine Aussage. 😛

    Mal gucken, was der nächste findet. :->



  • Xin schrieb:

    Kellerautomat schrieb:

    Wenn ich davon ausgehen kann, dass im Fehlerfall eine Exception geworfen wird, muss ich gar nichts prüfen.
    Bei einem ordentlichen Compiler kosten Exceptions gar nichts, solange sie nicht geworfen werden. Siehe http://lazarenko.me/2011/07/22/c-exception-handling-and-performance/.

    Zwei Kommentare dazu:

    1. try und die } nach try kosten Zeit, denn es muss festgelegt werden wo throw im Fehlerfall hinspringen muss und wenn } erreicht wird, muss die Änderung zurückgenommen werden, damit zum übergeordneten catch-Block gesprungen wird. Scott Meyer beschreibt das in Effektive C++, keine Ahnung in welchem Band.
      Wenn Du dir Gedanken darüber machst, wie Du eine Sprache implementierst, muss Dir klar sein, dass Du in einem try anders agierst als außerhalb und diese Änderung musst Du im Programm vermerken und das gibt's nunmal nicht kostenlos.

    Nein, da muss nichts festgelegt werden. Das steht in dem Artikel unter "Under the hood" (zero cost exception handling). Ich habe mir das konkrete Beispiel nicht einmal angesehen, es ging mir lediglich um die Funktionsweise. Das hätte ich klarstellen müssen, mein Fehler.

    Xin schrieb:

    Kellerautomat schrieb:

    Xin schrieb:

    Konstruktoren sind bei mir im Prinzip normale Funktionen.

    Das heisst, sie können etwas zurückgeben? Ruft man die explizit auf?

    Die Möglichkeit gibt es.

    Bzgl. des Aufrufs spiele ich noch mit einer Reihe von Möglichkeiten.

    Welche Möglichkeiten ziehst du bisher in Betracht?

    Xin schrieb:

    Kellerautomat schrieb:

    Nur weil ich definiere, was int*? bedeutet, wird das Konstrukt nicht besser. Wenn sich jedes Mal jemand fragt, was die genaue Semantik davon ist, ist es besser, es gleich nicht zu erlauben.

    Das sehe ich anders, hier käme Willkür ins Spiel. Aber da ich kein int*? in der Semantik habe, also auch kein int?, stellt sich die Frage für mich nicht.

    Im Gegensatz zu uninitialized<int> sehe ich nullable<int> tatsächlich als Library-Feature, denn für alle Datentypen lässt sich das notfalls über einen Zeiger ausdrücken, der immer Nullable ist. In C++: int const *: "Da steht das gewünschte int - oder es gibt keins."
    Das ist bei int nicht so schön, wie int?, aber funktioniert für alle KlassenObjekte absolut in Ordnung. Für den seltenen Sonderfall der Primitive baue ich kein eigenes Konstrukt.
    Die Notwendigkeit für int? sehe ich nicht.

    Aber sollten unsere Sprachen weiterhin so große Ähnlichkeit aufweisen, haben wir schonmal einen unbedeutenden Unterschied 😉

    T? ist aber kein T*, es hat Wertesemantik. Nur werden bei Referenztypen die Referenzen kopiert, nicht die Objekte. Daher entspricht es da quasi einem T*.

    Xin schrieb:

    Kellerautomat schrieb:

    Ich kann deiner Analogie leider nicht folgen.
    Pointer möchte ich, soweit möglich, ersetzen. Nicht, dass ich Pointer für schlecht halte, aber es gibt für die meisten Fälle sicherere Alternativen. Zumal Objekte, wie schon gesagt, per default Reference-Counted sind - ein roher Pointer hat da nicht die richtigen Kopiersemantiken.

    Hehehe, Du bist der Sprachdesigner. Du entscheidest welche Semantik ein Pointer hat.

    Ein Pointer ist wie in C und C++, um Interop zu ermöglichen.

    Xin schrieb:

    Kellerautomat schrieb:

    Und nur, um es nochmal hervorzuheben: Eine Referenz ist bei mir - anders als in Java - NICHT nullable. T? ist das, was T in Java ist - mit dem Unterschied, dass es auch mit Primitiven funktioniert.

    T ptr ist bei mir, was in Java T ist, was in C++ T * ist, bei Dir T?.
    T ist bei mir, was in Java nicht geht, was in C++ T &, bei Dir T.
    T copy ist bei mir, was in Java auch nicht geht, in C T und bei Dir...?

    ptr sieht erstmal scheiße aus. Das ist beabsichtig. In 95% kann man das ptr weglassen, also mit Referenzen arbeiten. Und genau das ist ja auch das Ziel. Wo kein Null reingehen kann, kann auch keine Null-Pointer-Exception/SegFault kommen.

    Ich finde es schlecht, so viele Arten zu haben, mit Objekten umzugehen. Das überfordert den Otto-Normal Programmierer, und dann kommt Unfug raus. Ich habe T und T?, für Parameter gibt es noch ref und out.

    Xin schrieb:

    Kellerautomat schrieb:

    Xin schrieb:

    Genau das will ich haben. Bedingte Initialisierung.

    Das glaube ich nicht. Ich glaube eher, du willst abhängig von einer Bedingung mit einem von zwei Werten initialisieren. Das kannst du mit einer temporären Variable oder ?:.

    Mit einer temporären Variable?

    Natürlich muss das Objekt initialisiert sein, wenn es erstmals initialisiert wird.
    Das muss die semantische Analyse halt erfassen.

    Du kannst sowas hier machen:

    let int tmp := void
    
    if(cond)
    	tmp = value1
    else
    	tmp = value2
    
    .x := tmp
    

    Alternativ mit ?:.

    Solltest du tatsächlich eine bedingte Initialisierung wollen, bitte ich um Erklärung wozu. Ich halte das für gefährlich.



  • Kellerautomat schrieb:

    typique schrieb:

    Viele der Features, die du genannt hast, lassen sich verallgemeinern und zusammenmergen.

    Beispiel:

    let int x := void
    

    Wird nur sehr selten gebraucht und wenn, dann lässt sich das durch einen uninitialized<int> lösen, der intern aligned_storage<int> (was sowieso benötigt wird) verwendet. Gewinn: Einfachere Sprache, weniger Fehlerquellen.

    Wenn ich es brauche, will ich es einfach verfügbar haben.

    Der Punkt ist ja, dass es fast nie gebraucht wird. Vielleicht um Container zu schreiben, aber nicht in 0815-Code.

    let int tmp := void
    
    if(cond)
        tmp = value1
    else
        tmp = value2
    
    .x := tmp
    

    ->

    int tmp = [&](){
      if (cond)
        return value1;
      else
        return value2;
    }();
    


  • typique schrieb:

    Der Punkt ist ja, dass es fast nie gebraucht wird. Vielleicht um Container zu schreiben, aber nicht in 0815-Code.

    Im Zusammenhang mit out-Parametern kann man es verwenden.



  • Kellerautomat schrieb:

    typique schrieb:

    Der Punkt ist ja, dass es fast nie gebraucht wird. Vielleicht um Container zu schreiben, aber nicht in 0815-Code.

    Im Zusammenhang mit out-Parametern kann man es verwenden.

    out wird duch Tupel unnötig.



  • typique schrieb:

    Kellerautomat schrieb:

    typique schrieb:

    Der Punkt ist ja, dass es fast nie gebraucht wird. Vielleicht um Container zu schreiben, aber nicht in 0815-Code.

    Im Zusammenhang mit out-Parametern kann man es verwenden.

    out wird duch Tupel unnötig.

    Hmm. Da könntest du Recht haben, das habe ich mir noch nicht überlegt. Gute Idee eigentlich.



  • Die Fragestellung als solche gefällt mir nicht, weil Programmiersprachen und die diese benutzenden Compiler allein ein Hilfsmittel der Programmierung sind und sein sollen.

    Also mal eine konkrete Anforderung: Ein Kunde spricht seine Wünsche zur Programmleistung auf Band. Der Compiler wertet die Spracheingabe aus, erstellt das
    Design und macht ein lauffähiges Programm daraus. Basta! 😞



  • Keiner kommt auf die Idee Bytecode mit zu spezifizieren 😮



  • Zeus schrieb:

    Keiner kommt auf die Idee Bytecode mit zu spezifizieren 😮

    Liegt vlt daran, dass unsere Sprachen nativ laufen sollen. 😉



  • Kellerautomat schrieb:

    Zeus schrieb:

    Keiner kommt auf die Idee Bytecode mit zu spezifizieren 😮

    Liegt vlt daran, dass unsere Sprachen nativ laufen sollen. 😉

    So hab ich nicht gemeint, ich will den Bytecode nicht als Executor auf Anwenderseite haben sondern als Exchanger auf Entwicklerseite.



  • Zeus schrieb:

    Kellerautomat schrieb:

    Zeus schrieb:

    Keiner kommt auf die Idee Bytecode mit zu spezifizieren 😮

    Liegt vlt daran, dass unsere Sprachen nativ laufen sollen. 😉

    So hab ich nicht gemeint, ich will den Bytecode nicht als Executor auf Anwenderseite haben sondern als Exchanger auf Entwicklerseite.

    Was? 😕



  • Kellerautomat schrieb:

    Zeus schrieb:

    Kellerautomat schrieb:

    Zeus schrieb:

    Keiner kommt auf die Idee Bytecode mit zu spezifizieren 😮

    Liegt vlt daran, dass unsere Sprachen nativ laufen sollen. 😉

    So hab ich nicht gemeint, ich will den Bytecode nicht als Executor auf Anwenderseite haben sondern als Exchanger auf Entwicklerseite.

    Was? 😕

    Wenn du eine Library in C++ schreibst, dann kannst du diese nicht gut weiter geben. Du brauchst precompiled Versionen für alle möglichen Plattformen und unterschiedliche Objekt Formate, etc. Und wenn du Templates hast, ist eh hopfen und malz verloren.

    Hier wäre ein Bytecode interessant. Ich kompiliere meine Library in den Bytecode und gebe sie dir. Und ob du diese nun auf Linux oder Windows auf 16 bit oder 128bit Architekturen verwendest ist egal - der Bytecode hat alle notwendigen Infos ohne aber dass er der offene Source Code ist.



  • Sucht ihr immer Sachen, die es schon gibt? Gib doch einfach GIMPLE weiter 🙄 👎



  • Was mich bei derzeitigen Programmiersprachen stört?

    - Viele Programmiersprachen unterstützen noch globale Variablen, dabei werden diese heutzutage nicht mehr verwendet, weil sie unschöne Effekte produzieren.
    Wesentlich schöner wäre es nativ Konstrukte bereitzustellen, welche die Möglichkeit bieten über mehrere Funktionen Variablen zu verteilen (die nicht immer als Parameterlisten übergeben werden müssen) und auch entsprechende Methoden/Operatoren zur Verfügung stellen, um den Lese-/Schreibzugriff zu regeln.

    - Anhand von Python finde ich es sehr schön den Benutzer anhand von Syntax zur Codestrukturierung zu bringen. Nichts ist grausamer als nicht eingerückten und geordneten Code lesen zu müssen. Zusätzlich gefällt mir die native Unterstützung von Dictionaries.

    Als Fazit muss ich daher sagen, Python bietet schon recht vieles was ich mir an einer Programmiersprache wünsche. Jedoch habe ich für meinen ersten Punkt bisher in Python noch keine Möglichkeit dafür gefunden, muss jedoch auch zugegeben mich nicht mehr intensiv mit Python zu beschäftigen, da mein momentaer Fokus auf ANSI-C liegt und ich acuh wie der Thread-Ersteller und einige andere hier an einer eigenen Sprache mit Compiler arbeite. Diese Sprache ist jedoch sehr sehr rudimentär - PL0, falls jemandem dies etwas sagt. Geplant ist ab einem bestimmten Zeitpunkt geeignete Operationen für globale Variablen bereitzustellen um diese zu "Semi-Globalisieren".



  • Nirvash schrieb:

    Was mich bei derzeitigen Programmiersprachen stört?

    - Viele Programmiersprachen unterstützen noch globale Variablen, dabei werden diese heutzutage nicht mehr verwendet, weil sie unschöne Effekte produzieren.
    Wesentlich schöner wäre es nativ Konstrukte bereitzustellen, welche die Möglichkeit bieten über mehrere Funktionen Variablen zu verteilen (die nicht immer als Parameterlisten übergeben werden müssen) und auch entsprechende Methoden/Operatoren zur Verfügung stellen, um den Lese-/Schreibzugriff zu regeln.

    Du meinst Objekte?


Anmelden zum Antworten