Tatsächlich hat Microsoft die erste Spec für die 3.0-Version zur PDC
2005 herausgebracht, auch wenn noch nicht einmal C# 2.0 aus der
Beta-Phase heraus ist. Finde ich aber gut, das gibt einem das Gefühl,
die Sprache wird gepfegt und ist ihnen wichtig. Leider wird auch in
Version 3.0 die Sprache nicht kleiner sondern noch größer... dafür
kommen jetzt langsam die interessanten Features.
Es gibt lokale Typinferenz. Man kann einfach
var i = 5;
var orders = new Dictionary<int, Order>();
schreiben und der Compiler kann sich den rest zusammenreihmen. Ach, wie
wäre es schön, wenn es das auch in Java gäbe...
Man kann auch existierende Klassen um neue Methoden ergänzen (AspectJ
nennt sowas open classes, Ruby oder Smalltalk nennt es eine
Selbstverständlichkeit). Die Syntax sieht wie ein Hack aus, der es wohl
auch ist. Man definiert eine statische Funktion in der man den Receiver
mittels "this" markiert:
public static class Extensions {
public static String reverse(this String receiver) {
...
}
}
Nach einem "using Extensions" kann man dann einfach
"Hallo".reverse();
schreiben. Der Compiler wird das dann wahrscheinlich unsichtbar für den
Anwender zu Extensions.reverse("Hallo") umschreiben. Ich vermute mal,
dass Vererbung hier nicht geht, aber die müsste man bei C# ja sowieso
explizit mit dem Schlüsselwort "virtual" einschalten. Also einfachster
billiger Syntaxzucker ohne großen Wert IMHO.
Bereits C# 2.0 kennt anoynme Funktionen, die AFAIK auch closures bilden
können. Die Syntax dafür ist aber stark an delegates angenähert und
immer noch fast so geschwätzig wie anonyme innere Klassen bei Java. Mit
C# 3.0 gibt es eine leichtgewichtige Syntax für lambda-Ausdrücke:
x => x + 1
Dies entspricht
delegate(int x) { return x + 1; }
und funktioniert, da es ja lokale Typinferenz gibt. Wer einen
Parameter-Typ angeben will, muss
(int x) => x + 1
benutzen und wer auf ein (redundantes) Return-Statement besteht, muss
(int x) => { return x + 1; }
formulieren. Gleichzeitig sagt aber die Spec, dass die {}-Syntax noch
nicht vom Microsoft-Compiler verstanden wird.
Ein weiteres Feature, was man sich bei bei funktionalen Sprachen und
Lisp angeschaut hat, ist der generische Initialisierer. Immer noch
etwas schwerfällig, aber man kann jetzt
new Point() { X = 1, Y = 2 }
schreiben, was dann einem
var p = new Point();
p.X = 1;
p.Y = 2;
entspricht. Zu beachten ist, dass X und Y properties sind, also
Methoden, die nur so aussehen, als wären es einfache Felder.
Wenn die Klasse klar ist, etwa ein ein Rectangle zwei Punkte origin und
corner erwartet, kann man diese auch weglassen:
new Rectangle { Origin = { X = 0, Y = 0 }, Corner = { X = 10, Y = 10 }}
Und man kann das in C# hinschreiben, was generationen von
Java-Entwicklern schon vermisst haben (ich habe es jedenfalls):
var l = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
keine schwachsinnig umständliches gefummel mit
List<Integer> l = Arrays.asList(new Integer[]{ 0, 1, ... });
Nach anonymen Funktionen gehen auch anonyme Typen, was auf den ersten
Blick ein bisschen komisch ist. Gemeint sind aber Tupel. Man kann
zusammen mit der obigen Syntax jetzt sowas machen:
var x = { A = 1, B = "Hallo" }
All dies ist nötig, Strukturen a la XML in der Sprache definieren zu
können, ohne das man gleich drei Millionen Zeilen boilerplate-Code
schreiben oder generieren muss. Sehr gut.
Es wird aber noch besser. Man kann jetzt
from c in customers where c.City == "Kiel" select c
schreiben, was einem die Mächtigkeit von SQL in die Sprache zieht und
natürlich die Vorbereitung dafür ist, dass man typsicher und direkt in
C# genauso auf internen Datenstrukturen wie collections und streams wie
auf XML-Strukturen oder Datenbanken arbeiten kann. Das sollte endlich
die Impedanz-Lücke zwischen Objekten und Relationalen Daten auf die
einzig sinnvolle Weise schließen - man erlaubt select und co direkt in
der Sprache.
Obiger Ausdruck wird übrigens zu einem
customers.where(c => c.City == "Kiel")
und man erkennt hier bereits die Macht der anonymen Funktionen. Etwas
wie where() ist trivial für collections zu implementieren, entspricht
doch das dem "select" von Smalltalk oder dem "filter" vieler
funktionaler Sprachen. Aber was rede ich, das äquivalent zu
from c in customers where c.City == "Kiel" select c.Name
ging in Smalltalk natürlich schon immer mit
customers
select: [:each | each city = "Kiel"]
thenCollect: [:each | each name]
was vielleicht nicht ganz so optimal benannt ist, aber der Namensgebung
von Smalltalk entspricht. Es kostet mich 10sek, eine Methode
Collection>>where: clause select: view
^self select: clause thenCollect: view
zu schreiben. Schön, das C# hier jetzt auch angekommen ist -
vorausgesetzt sie machen ihr Collection-API mächtig genug. Group by und
Sort und was SQL sonst noch so kann ist ebenfalls in der neuen Syntax
abgedeckt.
Das letzte vielleicht spannendeste Feature wird leider in ihrer jetzigen
Spec nur angerissen. Es geht darum *trommelwirbel* Code und Daten
gleichzusetzen und speziell lambdas auch als Datenstrukturen zu
repräsentieren. Ich vermute doch ganz stark, dass sie das natürlich
brauchen, um z.B. in einem SQL-Mapper die lambdas jetzt in einen
SQL-String umzurechnen.
Die Idee ist, dass man
Expression<Func<int,int>> e = x => x + 1;
hinschreiben kann und dann hat man ein Objekt "e" der Klasse
System.Query.Expression, was man dann wohl in subexpressions zerlegen
kann. Das scheint aber noch Zukunftsmusik zu sein.
Alles in allem muss ich sagen, dass mir die neue Richtung sehr gut
gefällt. Interessante Features wurden aus C-Omega in C# übernommen und
das erklärte Ziel, Funktionen höherer Ordnung für einen stärker
funktionalen Programmierstil und eine SQL- und XQuery-kompatible
Abfragesprache zur Verfügung zu stellen scheint erreicht.
PS: Die Machen von Nemerle verkünden übrigens stolz (daher habe ich
überhaupt die Referenz auf C# 3.0), dass sie das und mehr auch schon in
ihrer Sprache haben. Als funktionale Sprache (mit dennoch klassischer
C-artiger Syntax was IMHO doch recht schwerfällig wirkt) haben sie das
alles natürlich tatsächlich schon, einfach weil es Standard in diesen
Sprachen ist.
--
Stefan Matthias Aust // Lassen Sie uns durch, wir sind Arzt!
> Es gibt lokale Typinferenz. Man kann einfach
>
> var i = 5;
> var orders = new Dictionary<int, Order>();
>
> schreiben und der Compiler kann sich den rest zusammenreihmen. Ach, wie
> wäre es schön, wenn es das auch in Java gäbe...
Hilfe, bitte nicht ....
> Man kann auch existierende Klassen um neue Methoden ergänzen (AspectJ
> nennt sowas open classes, Ruby oder Smalltalk nennt es eine
> Selbstverständlichkeit). Die Syntax sieht wie ein Hack aus, der es wohl
> auch ist. Man definiert eine statische Funktion in der man den Receiver
> mittels "this" markiert:
>
> public static class Extensions {
> public static String reverse(this String receiver) {
> ...
> }
> }
alles wird unpflegbarer und unleserlicher ... ich weiss nicht, genau das
will ich eigentlich nicht.
> new Point() { X = 1, Y = 2 }
>
> schreiben, was dann einem
>
> var p = new Point();
> p.X = 1;
> p.Y = 2;
>
> entspricht. Zu beachten ist, dass X und Y properties sind, also
> Methoden, die nur so aussehen, als wären es einfache Felder.
hm, das ist noch ganz in Ordnung, daran könnte ich mich noch gewöhnen.
Gruss Achim
Such dir eine winzigweiche Spielwiese und müll hier nicht
die Java-NG so sinnlos voll.
Alfred
Ist das eine Aufforderung an dich selbst? ;)
SCNR
Markus
Naja, dafür lassen sich auch in Java Initializer benutzen oder?:
class Point {
public int x;
public int y;
}
new Point() {{x = 1; y = 2;}};
Übrigens finde ich es frustrierend und verantwortungslos von dir, hier
immer wieder klarzumachen dass Java nicht das einzig Wahre ist ;)
Timo
Timo Stamm wrote:
> Übrigens finde ich es frustrierend und verantwortungslos von dir, hier
> immer wieder klarzumachen dass Java nicht das einzig Wahre ist ;)
Was heisst "nicht das einzig Wahre"? Laut SAM ist Java überhaupt nicht
das wahre! ;-)
Ciao,
Ingo
Stefan Matthias Aust wrote:
> Ganz in der Tradition, mir hier OT-Themen herauszupicken, hier ein paar
> Informationen über C# 3.0.
Ganz in der Tradition, speziell deine Postings zu lesen (und ihnen ab
und an auch zu widersprechen), hier meine 0,02 Euro:
> Es gibt lokale Typinferenz. Man kann einfach
>
> var i = 5;
> var orders = new Dictionary<int, Order>();
>
> schreiben und der Compiler kann sich den rest zusammenreihmen. Ach, wie
> wäre es schön, wenn es das auch in Java gäbe...
Fände ich OK, auch wenn ich es nicht allzu sehr vermisse.
> Man kann auch existierende Klassen um neue Methoden ergänzen...
Habe ich auch schon vermisst. Vor allem z.B. auch, bereits existierende
Methoden "auszutauschen". Ob das gut ist, ist allerdings eine andere
Frage. (Dürfte zu schwer debugbarem und schwer wartbarem Code führen.)
Ausserdem frage ich mich, ob die neuen Methoden dann auch Zugriff auf
private members habe. Kann ich dann java.lang.String immutable machen?
(Und damit den SecurityManager umgehen!)
> Bereits C# 2.0 kennt anoynme Funktionen, die AFAIK auch closures bilden
> können. Die Syntax dafür ist aber stark an delegates angenähert und
> immer noch fast so geschwätzig wie anonyme innere Klassen bei Java. Mit
> C# 3.0 gibt es eine leichtgewichtige Syntax für lambda-Ausdrücke:
> ...
Schöne Sache, allerdings ist die Syntax diskussionswürdig.
> Ein weiteres Feature, was man sich bei bei funktionalen Sprachen und
> Lisp angeschaut hat, ist der generische Initialisierer. Immer noch
> etwas schwerfällig, aber man kann jetzt
>
> new Point() { X = 1, Y = 2 }
>
> schreiben, was dann einem
>
> var p = new Point();
> p.X = 1;
> p.Y = 2;
>
> entspricht.
Timos Idee mit dem Initialiser kannte ich noch gar nicht! Damit hätte
Java das ja schon quasi! (Ich fürchte zwar, dass die wenigsten
Code-Formatter das vernünftig hinbekommen, aber naja...)
> Zu beachten ist, dass X und Y properties sind, also
> Methoden, die nur so aussehen, als wären es einfache Felder.
Also sowas wie per automatischer CodeGenerierung erzeugte getter und
setter? Hmm... muss ich mich erstmal dran gewöhnen (macht den Code
kürzer aber die Sprache komplexer...)
> Wenn die Klasse klar ist, etwa ein ein Rectangle zwei Punkte origin und
> corner erwartet, kann man diese auch weglassen:
>
> new Rectangle { Origin = { X = 0, Y = 0 }, Corner = { X = 10, Y = 10 }}
Hmm.... (*)
> Und man kann das in C# hinschreiben, was generationen von
> Java-Entwicklern schon vermisst haben (ich habe es jedenfalls):
>
> var l = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
>
> keine schwachsinnig umständliches gefummel mit
>
> List<Integer> l = Arrays.asList(new Integer[]{ 0, 1, ... });
Yes, schön! (Also, die C#-Variante! :-)
> Nach anonymen Funktionen gehen auch anonyme Typen, was auf den ersten
> Blick ein bisschen komisch ist. Gemeint sind aber Tupel. Man kann
> zusammen mit der obigen Syntax jetzt sowas machen:
>
> var x = { A = 1, B = "Hallo" }
Vielleicht ganz brauchbar als kombinierte Rückgabewerte für Methoden?
(Vielleicht verleitet das aber auch zu Missbrauch?) Ich weiss es nicht.
> All dies ist nötig, Strukturen a la XML in der Sprache definieren zu
> können, ohne das man gleich drei Millionen Zeilen boilerplate-Code
> schreiben oder generieren muss. Sehr gut.
Argh, XML! ;-(
> Es wird aber noch besser. Man kann jetzt
>
> from c in customers where c.City == "Kiel" select c
> ...
Puh... (*)
> Das letzte vielleicht spannendeste Feature wird leider in ihrer jetzigen
> Spec nur angerissen. Es geht darum *trommelwirbel* Code und Daten
> gleichzusetzen und speziell lambdas auch als Datenstrukturen zu
> repräsentieren. Ich vermute doch ganz stark, dass sie das natürlich
> brauchen, um z.B. in einem SQL-Mapper die lambdas jetzt in einen
> SQL-String umzurechnen.
>
> Die Idee ist, dass man
>
> Expression<Func<int,int>> e = x => x + 1;
>
> hinschreiben kann und dann hat man ein Objekt "e" der Klasse
> System.Query.Expression, was man dann wohl in subexpressions zerlegen
> kann. Das scheint aber noch Zukunftsmusik zu sein.
Klingt stark funktional. Für kleinere Dinge IMHO gut zu gebrauchen, für
komplexere, produktive Applikationen IMHO ähnlich schlecht geeignet wie
einige funktionale Sprachen. In dem Fall dann nämlich zu
unübersichtlich, unlesbar, schlecht wartbar,... (*)
Ciao,
Ingo
(*) Nenn mich konservativ, aber in der Hinsicht sind mir das zu viele
Änderungen auf einmal in eine Sprache gequetscht!
Also, speziell die Postings von SMA finde ich fast ausnahmslos *sehr*
lesenswert (wenngleich ich ihnen auch ab und an widerspreche):
Ich habe keine große Lust (und Zeit), mich tief in C# einzusarbeiten,
aber ich möchte schon über den Tellerrand blicken und wissen, was da so
vor sich geht. SMA hat das (und anderes) sehr schön kompakt und
informativ zusammengefasst. Ich finde das toll!
Filtere einfach auf "[OT]", "prOsT", "Smalltalk", "Ruby", "OCAML", etc,
dann wirst Du nicht weiter mit behelligt. ;-)
Ciao,
Ingo
Ich denke das war mehr ein Joke von Timo. Ich glaube nicht, dass er ernsthaft vorschlägt massenhaft
anonyme Klassen zu definieren. (Noch dazu weil im Initializer nur auf finale lokale Variablen und
field members des umgebenden Scopes zugegriffen werden kann.)
cu
--
We can live without religion and meditation,
but we cannot survive without human affection.
Dalai Lama
Hallo,
> Filtere einfach auf "[OT]", "prOsT", "Smalltalk", "Ruby", "OCAML", etc,
> dann wirst Du nicht weiter mit behelligt. ;-)
de.comp.lang.misc?
Vor allem hätten dann auch Leute etwas davon, die
sich nicht für Java interessieren.
tschuess
[|8:)
>
>
> Such dir eine winzigweiche Spielwiese und müll hier nicht
> die Java-NG so sinnlos voll.
>
> Alfred
Da muss ich aber entschieden widersprechen!
SMAs Postings haben immer ein gehobenes Niveau
und schauen auch mal über den Tellerrand hinaus!
Grüsse,
Patrick
Wolfgang
Jede Möglichkeit schon zur Compilezeit möglichst umfangreiche
Typsicherheit garantieren zu können ist ein Gewinn. Ob ich dann
aber ein solches "Feature" wirklich will? Ich zumindest finde, dass da
ein wenig das "Typbewußtsein" beim Entwickeln leidet...
> Man kann auch existierende Klassen um neue Methoden ergänzen (AspectJ
> nennt sowas open classes, Ruby oder Smalltalk nennt es eine
> Selbstverständlichkeit). Die Syntax sieht wie ein Hack aus, der es wohl
> auch ist. Man definiert eine statische Funktion in der man den Receiver
> mittels "this" markiert:
>
> public static class Extensions {
> public static String reverse(this String receiver) {
> ...
> }
> }
>
> Nach einem "using Extensions" kann man dann einfach
>
> "Hallo".reverse();
>
> schreiben. Der Compiler wird das dann wahrscheinlich unsichtbar für den
> Anwender zu Extensions.reverse("Hallo") umschreiben. Ich vermute mal,
> dass Vererbung hier nicht geht, aber die müsste man bei C# ja sowieso
> explizit mit dem Schlüsselwort "virtual" einschalten. Also einfachster
> billiger Syntaxzucker ohne großen Wert IMHO.
>
ACK. Ich finde aber eh, dass eine solche Sache etwas vom "reinen"
OO-Gedanken abweicht. Wenn ich eine Klasse um etwas ergänzen will oder
muss, dann sollte man davon Ableiten, da die "neue" Klasse eine
erweiterte, also speziellere Variante der Ursprünglichen ist. Wenn mir
aber jemand eine wirklich sinnvolle Situation nennen kann, in der diese
Variante "besser" ist möge er sie mir darstellen - Mir fällt da nichts
ein. Nur mal so... Was würde eigentlich passieren, wenn String schon
eine Methode reverse() hätte? Überschrieben durch die Extension? Oder
verstehe ich das Ding komplett falsch?
> Und man kann das in C# hinschreiben, was generationen von
> Java-Entwicklern schon vermisst haben (ich habe es jedenfalls):
>
> var l = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
>
> keine schwachsinnig umständliches gefummel mit
>
> List<Integer> l = Arrays.asList(new Integer[]{ 0, 1, ... });
>
Hm, ich selbst habe bisher eher seltener Listen oder allgemein
Collections so mit initialen Werten füllen müssen, da sie ja wirklich
meistens etwas mit Laufzeitwerten zu tun haben. Daher habe ich es nie
direkt vermisst. Aber schöner siehts aus. ;-)
> Nach anonymen Funktionen gehen auch anonyme Typen, was auf den ersten
> Blick ein bisschen komisch ist. Gemeint sind aber Tupel. Man kann
> zusammen mit der obigen Syntax jetzt sowas machen:
>
> var x = { A = 1, B = "Hallo" }
>
> All dies ist nötig, Strukturen a la XML in der Sprache definieren zu
> können, ohne das man gleich drei Millionen Zeilen boilerplate-Code
> schreiben oder generieren muss. Sehr gut.
...
var bestellung = GetOrder(custID);
bestellung.amount++;
print(bestellung.name);
...
Also wenn ich an solche Dinge denke - Ne danke! Das eröffnet völlig neue
Möglichkeiten für einen wirklich miesen Stil. So im Vorbeigehen lassen
sich "Objekte definieren" die man halt mal "gerade braucht". In der
funktionalen Programmierung sind Tupel angebracht und sinnvoll, aber
hier wähle ich doch lieber die Krücke mir den Code generieren zu lassen.
Das ist meiner Meinung nach das kleinere Übel.
>
> Es wird aber noch besser. Man kann jetzt
>
> from c in customers where c.City == "Kiel" select c
>
> schreiben, was einem die Mächtigkeit von SQL in die Sprache zieht und
> natürlich die Vorbereitung dafür ist, dass man typsicher und direkt in
> C# genauso auf internen Datenstrukturen wie collections und streams wie
> auf XML-Strukturen oder Datenbanken arbeiten kann. Das sollte endlich
> die Impedanz-Lücke zwischen Objekten und Relationalen Daten auf die
> einzig sinnvolle Weise schließen - man erlaubt select und co direkt in
> der Sprache.
>
So was kann man mit Embedded SQL aber schon länger ;-)
Beispiel?
> Es wird aber noch besser. Man kann jetzt
>
> from c in customers where c.City == "Kiel" select c
>
> schreiben, was einem die Mächtigkeit von SQL in die Sprache zieht und
> natürlich die Vorbereitung dafür ist, dass man typsicher und direkt in
> C# genauso auf internen Datenstrukturen wie collections und streams wie
> auf XML-Strukturen oder Datenbanken arbeiten kann. Das sollte endlich
> die Impedanz-Lücke zwischen Objekten und Relationalen Daten auf die
> einzig sinnvolle Weise schließen - man erlaubt select und co direkt in
> der Sprache.
>
> Obiger Ausdruck wird übrigens zu einem
>
> customers.where(c => c.City == "Kiel")
>
> und man erkennt hier bereits die Macht der anonymen Funktionen. Etwas
> wie where() ist trivial für collections zu implementieren, entspricht
> doch das dem "select" von Smalltalk oder dem "filter" vieler
> funktionaler Sprachen.
gut aber wenn die Datenbank irgendwo auf einem Server liegt, dann will
ich ja nicht lokal auswerten. Da genügt es nicht einen Lambdaausdruck
c=>c.City=="Kiel", zu haben, man braucht strukturelle informationen
darüber um daraus eine SQL-Anweisung zu machen oder anderes. Wenn man
sich dabei auf Collections konzentriert, dann ja gut. Alos ich weiss nicht.
[...]
> Das letzte vielleicht spannendeste Feature wird leider in ihrer jetzigen
> Spec nur angerissen. Es geht darum *trommelwirbel* Code und Daten
> gleichzusetzen und speziell lambdas auch als Datenstrukturen zu
> repräsentieren. Ich vermute doch ganz stark, dass sie das natürlich
> brauchen, um z.B. in einem SQL-Mapper die lambdas jetzt in einen
> SQL-String umzurechnen.
davon Sprach ich ja gerade... und wo ist das bei C#?
> Die Idee ist, dass man
>
> Expression<Func<int,int>> e = x => x + 1;
>
> hinschreiben kann und dann hat man ein Objekt "e" der Klasse
> System.Query.Expression, was man dann wohl in subexpressions zerlegen
> kann. Das scheint aber noch Zukunftsmusik zu sein.
also hat man es noch nicht.
Gruss theo
>> var orders = new Dictionary<int, Order>();
> Ob ich dann aber ein solches "Feature" wirklich will? Ich zumindest finde,
dass da
> ein wenig das "Typbewußtsein" beim Entwickeln leidet...
Wieso? In der Masse der Fälle soll die Variable doch genau vom Typ des
erzeugten Objektes sein, wozu dann nochmal hinschreiben?
--
Alle gegen Alle.
http://www.fightclub-berlin.de
> > Such dir eine winzigweiche Spielwiese und müll hier nicht
> > die Java-NG so sinnlos voll.
> >
> Wiederspruch - das ist alles andere als "sinnlos"!
> Und wieder der Hinweis an Dich, dass Du die Filterfunktion Deines
> NewsReaders auch verwenden darfst...
Ich nutze die Filterfunktion meines Newsreaders und lese
Alfred gar nicht. Macht ihr es doch auch so :-)
Paul
--
Die Homepage von de.comp.lang.java: http://www.dclj.de
Pauls Package-Sammlung: http://purl.org/NET/ePaul/#pps
Man sollte dabei nicht vergessen, dass nicht nur der Compiler, sondern
auch der Leser sich den Rest zusammenreimen muss.
>
> Man kann auch existierende Klassen um neue Methoden ergänzen (AspectJ
Wer's braucht.
> Ein weiteres Feature, was man sich bei bei funktionalen Sprachen und
> Lisp angeschaut hat, ist der generische Initialisierer. Immer noch
> etwas schwerfällig, aber man kann jetzt
>
> new Point() { X = 1, Y = 2 }
>
> schreiben, was dann einem
>
> var p = new Point();
> p.X = 1;
> p.Y = 2;
>
> entspricht. Zu beachten ist, dass X und Y properties sind, also
> Methoden, die nur so aussehen, als wären es einfache Felder.
Gefällt mir.
> var l = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
>
> keine schwachsinnig umständliches gefummel mit
>
> List<Integer> l = Arrays.asList(new Integer[]{ 0, 1, ... });
Gefällt mir auch.
>
> Nach anonymen Funktionen gehen auch anonyme Typen, was auf den ersten
> Blick ein bisschen komisch ist. Gemeint sind aber Tupel. Man kann
> zusammen mit der obigen Syntax jetzt sowas machen:
>
> var x = { A = 1, B = "Hallo" }
>
Womit deutlich wird, dass man als Leser nun intern schon fast einen
Compiler anschmeissen muss. Ich möchte gar nicht wissen, welche
Konstrukte sich auf diese Weise noch bilden lassen... Gefällt mir
überhaupt nicht.
>
> Es wird aber noch besser. Man kann jetzt
>
> from c in customers where c.City == "Kiel" select c
>
> schreiben, was einem die Mächtigkeit von SQL in die Sprache zieht und
> natürlich die Vorbereitung dafür ist, dass man typsicher und direkt in
> C# genauso auf internen Datenstrukturen wie collections und streams wie
> auf XML-Strukturen oder Datenbanken arbeiten kann. Das sollte endlich
> die Impedanz-Lücke zwischen Objekten und Relationalen Daten auf die
> einzig sinnvolle Weise schließen - man erlaubt select und co direkt in
> der Sprache.
Prinzipiell finde ich das sehr gut. Wie funktioniert das Ganze im
Zusammenhang mit Datenbanken?
Gruß
Michael
Moinsen,
| Such dir eine winzigweiche Spielwiese und müll hier nicht
| die Java-NG so sinnlos voll.
Bitte fass dich an die eigene Nase ... nutz deine Filter ... SMA soll da
ruhig weitermachen! Er hat Spass dran und ich bin dankbar, dass er das
macht! Auch wenn ich ein völlig anderes Verständnis für eine brauchbare
Sprache habe...
Gruss Achim
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.2.5 (GNU/Linux)
Comment: Using GnuPG with Thunderbird - http://enigmail.mozdev.org
iD8DBQFDKRX3m6WJJF5tK2sRAkfwAKCpxpJpvcOmLB5rmkySFTnV2EcyGgCdEbEP
UEWzlXzlw7ODw4MitVFjZts=
=EfOQ
-----END PGP SIGNATURE-----
Nachdem die lokale Typinferenz eben nur für lokal deklarierte Variablen
funktioniert, ist das wohl üblicherweise nicht sonderlich kompliziert. Und
speziell für generische Typen wesentlich angenehmer zu schreiben:
Dictionary<int, Order> orders = new Dictionary<int, Order>()
Kniffliger wird es natürlich für Sachen wie das hier:
var foo = expression ? "Bar" : null; // welchen Typ hat jetzt foo - ist das
ein String oder ein Object?
Andreas
Sebastian Baechle wrote:
>> Es gibt lokale Typinferenz. Man kann einfach
>>
>> var i = 5;
>> var orders = new Dictionary<int, Order>();
>>
>> schreiben und der Compiler kann sich den rest zusammenreihmen. Ach,
>> wie wäre es schön, wenn es das auch in Java gäbe...
>
> Jede Möglichkeit schon zur Compilezeit möglichst umfangreiche
> Typsicherheit garantieren zu können ist ein Gewinn. Ob ich dann
> aber ein solches "Feature" wirklich will? Ich zumindest finde, dass da
> ein wenig das "Typbewußtsein" beim Entwickeln leidet...
Ich seh' das ähnlich wie Steffen: Stell dir einfach vor, der Compiler
würde per Cut&Paste (eigentlich müsste es ja Copy&Paste heissen ;-)
einfach das "var" durch das ersetzen, was zwischen dem "new" und dem
"()" steht. Fände ich OK.
>> Nach einem "using Extensions" kann man dann einfach
>>
>> "Hallo".reverse();
>>
>> schreiben. Der Compiler wird das dann wahrscheinlich unsichtbar für
>> den Anwender zu Extensions.reverse("Hallo") umschreiben. Ich vermute
>> mal, dass Vererbung hier nicht geht, aber die müsste man bei C# ja
>> sowieso explizit mit dem Schlüsselwort "virtual" einschalten. Also
>> einfachster billiger Syntaxzucker ohne großen Wert IMHO.
>
> ACK. Ich finde aber eh, dass eine solche Sache etwas vom "reinen"
> OO-Gedanken abweicht. Wenn ich eine Klasse um etwas ergänzen will oder
> muss, dann sollte man davon Ableiten, da die "neue" Klasse eine
> erweiterte, also speziellere Variante der Ursprünglichen ist. Wenn mir
> aber jemand eine wirklich sinnvolle Situation nennen kann, in der diese
> Variante "besser" ist möge er sie mir darstellen - Mir fällt da nichts
> ein.
Es hätte einfach den Vorteil, dass man einige Util-Klassen "hübscher"
intergrieren könnte.
Anstatt "myPackage.Util.reverse(aList);" könntest Du einfach schreiben
"aList.reverse();", und das sieht IMHO irgendwie eher nach OO aus.
Das Sicherheits-Problem, immutable-Klassen plötzlich mutable zu machen,
sehe ich aber auch (vgl. mein anderes Posting).
>> Nach anonymen Funktionen gehen auch anonyme Typen, was auf den ersten
>> Blick ein bisschen komisch ist. Gemeint sind aber Tupel. Man kann
>> zusammen mit der obigen Syntax jetzt sowas machen:
>>
>> var x = { A = 1, B = "Hallo" }
>>
>> All dies ist nötig, Strukturen a la XML in der Sprache definieren zu
>> können, ohne das man gleich drei Millionen Zeilen boilerplate-Code
>> schreiben oder generieren muss. Sehr gut.
>
> ...
> var bestellung = GetOrder(custID);
> bestellung.amount++;
> print(bestellung.name);
> ...
>
> Also wenn ich an solche Dinge denke - Ne danke! Das eröffnet völlig neue
> Möglichkeiten für einen wirklich miesen Stil. So im Vorbeigehen lassen
> sich "Objekte definieren" die man halt mal "gerade braucht".
Das ist in der Tat gewöhnungsbedürftig. Allerdings wäre die statische
Typsicherheit dadurch nicht gefährdet, also ist es IMHO nicht so schlimm.
> In der
> funktionalen Programmierung sind Tupel angebracht und sinnvoll, aber
> hier wähle ich doch lieber die Krücke mir den Code generieren zu lassen.
> Das ist meiner Meinung nach das kleinere Übel.
Kann ich nicht völlig widersprechen... :-)
Ciao,
Ingo
> Es gibt lokale Typinferenz. Man kann einfach
>
> var i = 5;
> var orders = new Dictionary<int, Order>();
>
> schreiben und der Compiler kann sich den rest zusammenreihmen. Ach, wie
> wäre es schön, wenn es das auch in Java gäbe...
ACK!!!!!!!!
> Bereits C# 2.0 kennt anoynme Funktionen, die AFAIK auch closures bilden
> können. Die Syntax dafür ist aber stark an delegates angenähert und
> immer noch fast so geschwätzig wie anonyme innere Klassen bei Java. Mit
> C# 3.0 gibt es eine leichtgewichtige Syntax für lambda-Ausdrücke:
>
> x => x + 1
>
Großartig!
> Und man kann das in C# hinschreiben, was generationen von
> Java-Entwicklern schon vermisst haben (ich habe es jedenfalls):
>
> var l = new List<int> { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
>
> keine schwachsinnig umständliches gefummel mit
>
> List<Integer> l = Arrays.asList(new Integer[]{ 0, 1, ... });
... Sprache, die für dich dichtet und denkt (Schiller).
Leute, die mit Java großgeworden sind, vermissen so etwas nicht. Leute
die z.B. zuerst perl konnten, vermissen sowas schmerzlichst.
> Nach anonymen Funktionen gehen auch anonyme Typen, was auf den ersten
> Blick ein bisschen komisch ist. Gemeint sind aber Tupel. Man kann
> zusammen mit der obigen Syntax jetzt sowas machen:
>
> var x = { A = 1, B = "Hallo" }
Warum nicht: var x = (1, "Hallo");
Laß mich raten: es gibt noch kein pattern matching?
> Es wird aber noch besser. Man kann jetzt
>
> from c in customers where c.City == "Kiel" select c
>
> schreiben,
Wo ist der Unterschied zu filter(c=>c.City == "Kiel", customers)? Oder,
in perl-Notation
grep { $_->{City} eq "Kiel" } @customers
> Obiger Ausdruck wird übrigens zu einem
>
> customers.where(c => c.City == "Kiel")
>
> und man erkennt hier bereits die Macht der anonymen Funktionen. Etwas
> wie where() ist trivial für collections zu implementieren, entspricht
> doch das dem "select" von Smalltalk oder dem "filter" vieler
> funktionaler Sprachen.
Ebent. Die Selektion ist filter(grep) und die Projektion map.
> Das letzte vielleicht spannendeste Feature wird leider in ihrer jetzigen
> Spec nur angerissen. Es geht darum *trommelwirbel* Code und Daten
> gleichzusetzen und speziell lambdas auch als Datenstrukturen zu
> repräsentieren. Ich vermute doch ganz stark, dass sie das natürlich
> brauchen, um z.B. in einem SQL-Mapper die lambdas jetzt in einen
> SQL-String umzurechnen.
Wozu? Man braucht doch nur einen Mapper, nämlich einen, der
select * from A
in eine lazy evaluierte Liste umwandelt. Dann können alle Klassen, die
mit Listen arbeiten, auch mit DB-tabellen auswerten, ohne daß sie es
wissen.
> Alles in allem muss ich sagen, dass mir die neue Richtung sehr gut
> gefällt.
Klingt wirklich alles sehr interessant. Bei Sun können sie sich schon
mal warm anziehen. Man kann ja gegen MS als Unternehmen haben, was man
will, aber offenbar haben sie in dem Bereich doch sehr gute Leute, wie
z.B. Simon Peyton Jones.
> PS: Die Machen von Nemerle verkünden übrigens stolz (daher habe ich
Die was??? :-)
>> PS: Die Machen von Nemerle verkünden übrigens stolz (daher habe ich
>
> Die was??? :-)
Sollte wohl "Macher" heißen: http://nemerle.org/Main_Page.
Gruß,
Michael
--
Die Adresse im From existiert, wird aber nicht gelesen. Sollte
eine Mail-Antwort auf ein Posting vonnöten sein, bitte folgende
Adresse verwenden: newsreply@<DOMAIN_AUS_DEM_FROM_DIESES_POSTINGS>.
Ich würde da meistens ein +1 anbringen :-)
> -1 Subject {(?i)\bEclipse\b}
> -1 Subject {(?i)\bNetbeans\b}
> -1 Subject {(?i)\bAnt\b}
> -1 Subject {(?i)\bJBoss\b}
> -1 Subject {(?i)\bJDBC\b}
> -1 Subject {(?i)\bJ2EE\b}
> -1 Subject {(?i)\bTomcat\b
> -1 Subject {(?i)\bStruts\b}
> -1 Subject {(?i)\bJSP\b}
> -1 Subject {(?i)\bJ2ME\b}
> -1 Subject {(?i)\bMidlet\b}
> -1 Subject {(?i)\bJNI\b}
> -1 Subject {(?i)\bJNDI\b}
> -1 Subject {(?i)\bHibernate\b}
> -1 Subject {(?i)\bSWT\b}
> -1 Subject {(?i)\bJava3D\b}
> -1 Subject {(?i)\bPDF\b}
> -1 Subject {(?i)\bRMI\b}
Bei denen bin ich aber mit deiner Bewertung konform.
Paul
--
The Java Virtual Machine Specification:
http://java.sun.com/docs/books/vmspec/2nd-edition/html/VMSpecTOC.doc.html
> Man kann auch existierende Klassen um neue Methoden ergänzen (AspectJ
> nennt sowas open classes, Ruby oder Smalltalk nennt es eine
> Selbstverständlichkeit). Die Syntax sieht wie ein Hack aus, der es wohl
> auch ist. Man definiert eine statische Funktion in der man den Receiver
> mittels "this" markiert:
>
> public static class Extensions {
> public static String reverse(this String receiver) {
> ...
> }
> }
>
> Nach einem "using Extensions" kann man dann einfach
>
> "Hallo".reverse();
>
> schreiben. Der Compiler wird das dann wahrscheinlich unsichtbar für den
> Anwender zu Extensions.reverse("Hallo") umschreiben.
Naja, das scheint mir bischen weit hergeholt.
Was ich hingegen tatsächlich manchmal vermisse, ist ein Weg, eine
Klasse quasi nachträglich ein Interface implementieren zu lassen oder
anders implementieren zu lassen.
Vielleicht wäre es am saubersten, die Implementierung von Interfaces
grundsätzlich von der eigentlichen Klasse zu trennen. So á la
class Foo implements Bar {
...
}
Das würde einem so manche Proxy-Klasse ersparen.
Andererseits brächte es natürlich ganz neuartige Probleme, wenn in
einem Programm mehrere Übersetzungseinheiten behaupten, daß sie die
Bar-Implementierung von Foo kennen.
dafuer hab ich mir eine Methode gebaut:
public static <T> List<T> list(T...t)
{
return Arrays.asList(t);
}
dank "static import" sieht das recht huebsch aus.
bernd
>>> var orders = new Dictionary<int, Order>();
>>>
>>> schreiben und der Compiler kann sich den rest zusammenreihmen. Ach,
>>> wie wäre es schön, wenn es das auch in Java gäbe...
>>
>> Man sollte dabei nicht vergessen, dass nicht nur der Compiler, sondern
>> auch der Leser sich den Rest zusammenreimen muss.
>
> Nachdem die lokale Typinferenz eben nur für lokal deklarierte Variablen
> funktioniert, ist das wohl üblicherweise nicht sonderlich kompliziert. Und
> speziell für generische Typen wesentlich angenehmer zu schreiben:
> Dictionary<int, Order> orders = new Dictionary<int, Order>()
Normalerweise nimmt aber doch das Interface und nicht die Implementierung.
var al = new ArrayList <String>;
würde doch zu
ArrayList <String> al = new ArrayList <String> ();
expandiert. Das will man aber meistens doch gar nicht.
Man will doch eher:
List <String> al = new ArrayList <String> ();
MfG Christoph
Aber aufpassen:
Diese Liste kann nicht erweitert werden. add/remove gehen nicht.
Ich hätte mir ja wegen des Interfaces Iterable folgenden Konstruktor
gewünscht:
ArrayList(Iterable<E> iterable)
Dann sollten auch Arrays im Konstruktor funktionieren.
Sascha Broich
--
Schlage nie jemand grundlos.
Ein Grund findet sich immer.
Christoph Hess wrote:
> Normalerweise nimmt aber doch das Interface und nicht die Implementierung.
>
> var al = new ArrayList <String>;
>
> würde doch zu
> ArrayList <String> al = new ArrayList <String> ();
>
> expandiert. Das will man aber meistens doch gar nicht.
>
> Man will doch eher:
> List <String> al = new ArrayList <String> ();
"Normalerweise" sollte das IMHO zumindest bei lokalen Variablen egal
sein (und bei anderen macht die var-Syntax IMHO sowieso keinen Sinn).
Ciao,
Ingo
> Es gibt lokale Typinferenz. Man kann einfach
>
> var i = 5;
> var orders = new Dictionary<int, Order>();
>
> schreiben und der Compiler kann sich den rest zusammenreihmen. Ach, wie
> wäre es schön, wenn es das auch in Java gäbe...
Verstehe ich nicht!
Was soll sich der Compiler zusammenreimen?
Genaugenommen will ich keinen Compiler, der für mich denkt (ist
irgendwie so bescheuert wie die hüpfende Büroklammer in einer
Applikation, deren Name mir gerade entfallen ist).
Sönke
> var foo = expression ? "Bar" : null; // welchen Typ hat jetzt foo - ist das
> ein String oder ein Object?
Was, bitte, ist daran knifflig? Man muß halt definieren, ob weniger
spezielle oder speziellere Typen inferiert werden sollen. Intuitiv ist
klar, daß die Variante "weniger speziell" eher wenig nützlich ist, da
dann alle Typinferenzen "java.lang.Object" liefern würden, sofern ein
Referenztyp vorliegt.
Sinnvoll ist natürlich nur, den speziellsten Typ zu nehmen, zu dem
beide Ausdrücke gehören. Und das ist String.
> Stefan Matthias Aust wrote:
>
> > Es gibt lokale Typinferenz. Man kann einfach
> >
> > var i = 5;
> > var orders = new Dictionary<int, Order>();
> >
> > schreiben und der Compiler kann sich den rest zusammenreihmen. Ach, wie
> > wäre es schön, wenn es das auch in Java gäbe...
>
> Verstehe ich nicht!
> Was soll sich der Compiler zusammenreimen?
Er soll sich zusammenreimen, welchen Typ der Nutzer für i bzw. orders
wohl gern haben möchte.
> Genaugenommen will ich keinen Compiler, der für mich denkt
Kriegst Du auch nicht. Du kriegst, wenn Du Glück hast, einen, der Dich
von fehlerträchtiger Tipparbeit entlastet. In dem oben gezeigten Fall
ist völlig offensichtlich, welchen Typ "orders" haben soll. Wenn man
diesen öffensichtlichen Typ vergeben möchte (und nicht etwa einen
Basistyp davon), dann ist die oben angedeutete Schreibweise
vorteilhaft, weil sie Redundanzen und damit Fehlerquellen beseitigt,
z.B.
Dictoinary<int, Orders> orders = new Dictionary<int, Order>();
(Ich hatte letztens einen solchen Buchstabendreher, der mich fast zur
Verzweiflung gebracht hat, weil ich ihn eine ganze Weile nicht als
solchen erkannt, sondern nach einem richtigen Fehler gesucht habe.).
Es geht hier nicht darum, die Möglichkeit abzuschaffen, einen Typ
explizit vorzugeben. Tatsächlich wäre eine derartiges Feature voll
rückwärtskompatibel, bis auf die Verwendung des Schlüsselwortes var.
Dafür könnte man in Java für lokale Variablen den redundanten
Modifier private verwenden.
Oh, sowas gibt meist lustige Effekte. Inferrenz mit parametrisierbaren
Datentypen. Üblicherweise kann man mit so einem Typsystem natürliche
Zahlen abbilden. D.h. man verwendet rekursive Datentypen, für jede
natürliche Zahl einen. Dann benutzt man die inferenz für Addition und
Subtraktion. Ein Beispiel hierfür ist das Haskell-Typsystem. Mit diesem
lassen sich so komplexe Konstrukte bauen, dass man eigentlich ein
Meta-Typsystem bräuchte.
[...]
> Man kann auch existierende Klassen um neue Methoden ergänzen (AspectJ
> nennt sowas open classes, Ruby oder Smalltalk nennt es eine
> Selbstverständlichkeit).
Also AOP light. Wie verträgt sich das eigentlich mit der Typ-Inferenz?
Und was passiert mit der .NET-Version des Security-Managers?
[...]
> Es wird aber noch besser. Man kann jetzt
>
> from c in customers where c.City == "Kiel" select c
>
> schreiben, was einem die Mächtigkeit von SQL in die Sprache zieht und
> natürlich die Vorbereitung dafür ist, dass man typsicher und direkt in
> C# genauso auf internen Datenstrukturen wie collections und streams wie
> auf XML-Strukturen oder Datenbanken arbeiten kann. Das sollte endlich
> die Impedanz-Lücke zwischen Objekten und Relationalen Daten auf die
> einzig sinnvolle Weise schließen - man erlaubt select und co direkt in
> der Sprache.
>
> Obiger Ausdruck wird übrigens zu einem
>
> customers.where(c => c.City == "Kiel")
>
> und man erkennt hier bereits die Macht der anonymen Funktionen.
Dabei geht aber Information verloren. SQL kann den entsprechenden
Ausdruck optimieren. Die Collection kann das nicht, sie muss der
übergebenen Funktion jedes Objekt zur Prüfung übergeben. Prinzipiell
halte ich es für keine gute Idee, eine domänenspezifische Sprache wie
SQL durch eine universelle Sprache zu ersetzen. An sich war doch MS auf
dem richtigen Weg, indem sie mit .NET eine Plattform geschafen haben,
auf der spezialierte, domänenspezifische Sprachen zusammenarbeiten
können. In Zukunft werden die anderen .NET Sprachen wohl wirklich nur
noch Untermengen von C# mit anderer Syntax sein.
Besser hätte mir gefallen, wenn die Collections eine Teilmenge von SQL
verstehen würden. Alternativ hätte man für die Arbeit mit Collections
auch eine spezielle, domänenspezifische Sprache schaffen können. Die
Collections bekommen dann den Parser-Baum für den entsprechenden
Ausdruck, können diesen dann weiter optimieren und letztendlich
auswerten. Das Typsystem kann solche Ausfrücke zwar üblicherweise nicht
mehr prüfen; das sehe ich aber als das kleinere Übel an.
> Die Idee ist, dass man
>
> Expression<Func<int,int>> e = x => x + 1;
>
> hinschreiben kann und dann hat man ein Objekt "e" der Klasse
> System.Query.Expression, was man dann wohl in subexpressions zerlegen
> kann. Das scheint aber noch Zukunftsmusik zu sein.
Lustig wird's dann mit:
var e = x => x + 1;
Die Typinferenz hat dann richtig was zu tun, um herauszufinden, ob das
nun Expression<Func<int,int>> oder Expression<Func<String,String>> ist.
Noch lustiger wird's mit überladenen Operatoren...
> Alles in allem muss ich sagen, dass mir die neue Richtung sehr gut
> gefällt. Interessante Features wurden aus C-Omega in C# übernommen und
> das erklärte Ziel, Funktionen höherer Ordnung für einen stärker
> funktionalen Programmierstil und eine SQL- und XQuery-kompatible
> Abfragesprache zur Verfügung zu stellen scheint erreicht.
Mir gefällt diese Richtung von C# überhaupt nicht. Die Sprache wird mit
Lösungen für spezielle Probleme überfrachtet und damit unglaublich
komplex. Bei einigen Sachen bin ich mir nicht mal sicher, ob man die
überhaupt zusammen implementieren kann, beispielsweise Aspekte und
Typinferenz. Bin mal gespannt, was als nächstes Kommt. Vielleicht
spezielle Sprachkonstrukte für reguläre Ausdrücke oder für Ein- und
Ausgabe.
Besser wäre es, wenn MS einen Weg finden würde, wie man innerhalb der
selben Methode verschiedene Sprachen mischen kann. Dies wär noch nicht
mal sonderlich schwierig zu implementieren. Einfach den Quelltext als
XML abspeichern und die GUI ein wenig erweitern. Da MS die komplette
Plattform inkl. IDE und Versionsverwaltung kontrolliert, würde der
Anwender von solch einer Änderung kaum was mitbekommen.
Angesichts der Komplexität und der vielen möglichen Probleme, bin ich
mir nicht sicher, ob C# 3.0 jemals implementiert wird. Ich vermute, dass
MS vorher in Richtung MDA umschwenkt. Ist aber natürlich nur eine Vermutung.
Geggo
Klar, genau das ist der Punkt: es ist angenehmer zu /schreiben/ aber
IMHO äußerst unangenehm zu /lesen/.
> Kniffliger wird es natürlich für Sachen wie das hier:
>
> var foo = expression ? "Bar" : null; // welchen Typ hat jetzt foo - ist das
> ein String oder ein Object?
Da geht's doch schon los. Nach Ingo Menger sollte das String ergeben.
Richtig lustig könnte es werden, wenn Generics, die von Stefan
angeführten anonymen Typen und/oder Klassen in den Tiefen einer
gemeinsamen Hierarchie hinzukommen (ich weiß nicht, was da alles möglich
ist). Womoglich funktioniert das Ganze auch noch mit Variablen: var foo
= expression ? a : b;
Gruß
Michael
> Naja, dafür lassen sich auch in Java Initializer benutzen oder?:
>
> class Point {
> public int x;
> public int y;
> }
>
> new Point() {{x = 1; y = 2;}};
Das erzeugt jedes Mal ein Singleton einer anonymen Klasse - das willst
du nicht. In Java könntest du dies machen:
make(Point.class, "x", 1, "y", 2)
und dir dann
<T> T make(Class<T> clazz, Object... args) {
T o = clazz.newInstance();
for (int i = 0; i < args.length; i += 2) {
clazz.getMethod("set" + args[i]).invoke(o, args[i + 1]);
}
return o;
}
wobei ich bei dem getMethod ein bisschen geschummelt habe und das ganze
im Gegensatz zur C#-Lösung nicht statisch typsicher ist (was ich aber
nicht ganz so schlimm finde - tatsächlich empfinde ich diese
make()-Funktion als Verbesserung gegenüber dem normalen "new"-Operator).
> Übrigens finde ich es frustrierend und verantwortungslos von dir, hier
> immer wieder klarzumachen dass Java nicht das einzig Wahre ist ;)
Siehe auch http://jroller.com/page/slava/20050916, wo Slava Pestov mal
wieder gegen Java wettert - kann ich nur unterschreiben ;)
--
Stefan Matthias Aust // Lassen Sie uns durch, wir sind Arzt!
>> from c in customers where c.City == "Kiel" select c
> Prinzipiell finde ich das sehr gut. Wie funktioniert das Ganze im
> Zusammenhang mit Datenbanken?
Genauso. Obiges ist syntaktischer Zucker für
customers.Where(c => c.City == "Kiel")
und wenn "customers" jetzt ein Objekt ist, was Kenntnisse von einer
Datenbank hat und an Customer als Inhaltstyp gebunden ist, kann es die
übergeben Lambda-Funktion in
SELECT name,city,cat,... FROM Customers WHERE name = 'Kiel'
umwandeln, dann gegen die Datenbank schicken, dann das Ergebnis in
Customer-Objekte umwandeln und dann eine Liste (C.Omega hätte einen
Stream daraus gemacht, was noch bessere gewesen wäre) davon zurückgeben.
Ich hoffe aber, C# wird da nicht direkt eine List<Customer> liefern,
sondern doch etwas in der Art Iterator<Customer> o.ä., damit auch
größere Ergebnisse effizient zurückgeliefert werden können und z.B.
sowas möglich ist:
customers.Where(...).Limit(10)
Limit wäre dann eine Methode von Iterator, die nach den ersten 10
Elementen ein Ende des Stroms simuliert.
>> var orders = new Dictionary<int, Order>();
> Fände ich OK, auch wenn ich es nicht allzu sehr vermisse.
Ist aber ein essentielles Feature für lambda-Funktionen.
>> Man kann auch existierende Klassen um neue Methoden ergänzen...
>
> Habe ich auch schon vermisst. Vor allem z.B. auch, bereits existierende
> Methoden "auszutauschen". Ob das gut ist, ist allerdings eine andere
> Frage. (Dürfte zu schwer debugbarem und schwer wartbarem Code führen.)
Ich denke nicht, dass C# austauschen kann. Und ich denke auch, es können
nur nicht überschreibbare (final in Java-Sprech) Methoden ergänzt
werden, sodass ich dieses Feature als eher überflüssig in einer Java-
bzw. C#-Sprache ansehe.
> Ausserdem frage ich mich, ob die neuen Methoden dann auch Zugriff auf
> private members habe.
Denke nein. Es ist einfachster syntaktischer Zucker, dass man
object.method()
statt
method(object)
schreiben kann. Ich glaube wie gesagt nicht, dass aus
object.method()
ein
method_dispatch(object)
mit
static Object method_dispatch(Object object) {
if (object instanceof XY) return method(object)
throw new Error();
}
wird, was dynamischen Dispatch simulieren würde (Stichwort: Generische
Funktionen)
> Kann ich dann java.lang.String immutable machen?
> (Und damit den SecurityManager umgehen!)
Du meinst mutable, oder? Ich denke, das geht da nicht.
>> Bereits C# 2.0 kennt anoynme Funktionen, die AFAIK auch closures
>> bilden können. Die Syntax dafür ist aber stark an delegates
>> angenähert und immer noch fast so geschwätzig wie anonyme innere
>> Klassen bei Java. Mit C# 3.0 gibt es eine leichtgewichtige Syntax für
>> lambda-Ausdrücke:
>> ...
>
> Schöne Sache, allerdings ist die Syntax diskussionswürdig.
Ich finde sie recht gewöhnlich. Das wird in vielen Sprachen so gemacht.
> Timos Idee mit dem Initialiser kannte ich noch gar nicht! Damit hätte
> Java das ja schon quasi! (Ich fürchte zwar, dass die wenigsten
> Code-Formatter das vernünftig hinbekommen, aber naja...)
Nein, siehe meine Antwort.
> > Zu beachten ist, dass X und Y properties sind, also
>> Methoden, die nur so aussehen, als wären es einfache Felder.
>
> Also sowas wie per automatischer CodeGenerierung erzeugte getter und
> setter? Hmm... muss ich mich erstmal dran gewöhnen (macht den Code
> kürzer aber die Sprache komplexer...)
Ja, das könnte man als Motto für C# sehen. Ich glaube, Microsoft würde
auch die Einschätzung gefallen, dass C# die Sprache für Profis ist.
VB.NET ist dann eher die Anfängersprache.
>> var x = { A = 1, B = "Hallo" }
>
> Vielleicht ganz brauchbar als kombinierte Rückgabewerte für Methoden?
> (Vielleicht verleitet das aber auch zu Missbrauch?) Ich weiss es nicht.
Z.B. Aber es wird gebraucht, um Projektionen in SQL abzubilden, also ein
select a, b + 4 from t where ...
wobei t eben mehr spalten als a und b hat und a und b auch berechnet
sein könnten und das Ergebnis der Berechnung einen anderen Typ hat.
> Klingt stark funktional. Für kleinere Dinge IMHO gut zu gebrauchen, für
> komplexere, produktive Applikationen IMHO ähnlich schlecht geeignet wie
> einige funktionale Sprachen. In dem Fall dann nämlich zu
> unübersichtlich, unlesbar, schlecht wartbar,... (*)
Das ist ein IMHO ganz übles und haltloses Vorurteil.
> Jede Möglichkeit schon zur Compilezeit möglichst umfangreiche
> Typsicherheit garantieren zu können ist ein Gewinn.
Typsicherheit die darauf beruht, ob ein Entwickler wohl 2x
hintereinander in der selben Zeile das selbe hinschreiben kann, ist
Schikane, aber nicht hilfreich.
> ACK. Ich finde aber eh, dass eine solche Sache etwas vom "reinen"
> OO-Gedanken abweicht. Wenn ich eine Klasse um etwas ergänzen will oder
> muss, dann sollte man davon Ableiten, da die "neue" Klasse eine
> erweiterte, also speziellere Variante der Ursprünglichen ist.
Definitiv nicht. Zum einem zähle ich Klassen nicht zur Essenz des
OO-Gedankens. Das sind und bleiben Objekte, mit denen man durch Senden
von Nachrichten (bei Java in der Form von Methodenaufrufen die wie
Funktionsaufrufe aussehen) kommuniziert. Es geht einfach darum, wann
und wie man neue Methoden definieren kann. In Java geht das nur, wenn
man den Quelltext der passenden Klasse ändern darf. Das ist IMHO eine
ganz ärgerliche Beschränkung.
> Wenn mir
> aber jemand eine wirklich sinnvolle Situation nennen kann, in der diese
> Variante "besser" ist möge er sie mir darstellen - Mir fällt da nichts
> ein. Nur mal so... Was würde eigentlich passieren, wenn String schon
> eine Methode reverse() hätte? Überschrieben durch die Extension? Oder
> verstehe ich das Ding komplett falsch?
Falls es in C# schon ein Reverse() gibt, wäre mein Beispiel schlecht
gewesen. Ich verstehe das in C# so, dass man neu (finale) Methode
hinzufügen kann, mehr nicht.
Es sieht einfach besser aus, wenn man nicht alle Nase lang
Utils.whatever(receiver, args)
schreiben muss, nur weil die vorhandene Klassenbibliothek nicht mächtig
genug nicht und man leider nicht zur Klasse von receiver noch die
Methode "whatever" hinzufügen kann.
> Hm, ich selbst habe bisher eher seltener Listen oder allgemein
> Collections so mit initialen Werten füllen müssen, da sie ja wirklich
> meistens etwas mit Laufzeitwerten zu tun haben. Daher habe ich es nie
> direkt vermisst. Aber schöner siehts aus. ;-)
Ich habe das häufig, etwa in Unit-Test-Code. Und ich könnte jedes Mal
fluchen, insbesondere, weil ich auch noch nicht einmal JSE 5 mit seinen
Varargs benutzen kann.
> var bestellung = GetOrder(custID);
> bestellung.amount++;
> print(bestellung.name);
> ...
>
> Also wenn ich an solche Dinge denke - Ne danke! Das eröffnet völlig neue
> Möglichkeiten für einen wirklich miesen Stil. So im Vorbeigehen lassen
> sich "Objekte definieren" die man halt mal "gerade braucht". In der
> funktionalen Programmierung sind Tupel angebracht und sinnvoll, aber
> hier wähle ich doch lieber die Krücke mir den Code generieren zu lassen.
Code generieren ist IMHO niemals eine Option sondern ein Zeichen dafür,
dass die Sprache zu ausdruckschwach ist. Irgendjemand muss den ganzen
Scheiss lesen. Und je mehr boilerplate code es gibt, der vielleicht
subtile unterschiede trägt, desto schwerer wird das. Siehe den Link auf
Slavas Blog, den ich an anderer Stelle gepostet hatte.
>> from c in customers where c.City == "Kiel" select c
> So was kann man mit Embedded SQL aber schon länger ;-)
Es geht nicht darum, eine fremde Sprache zu integrieren, sondern die
Möglichkeiten von SQL in C# zu übernehmen, damit diese *universell*
verfügbar sind - also sowohl für interne collections, für XML-Query, für
SQL-Query, für was man sich auch immer später ausdenken wird.
> Kniffliger wird es natürlich für Sachen wie das hier:
>
> var foo = expression ? "Bar" : null; // welchen Typ hat jetzt foo - ist das
> ein String oder ein Object?
Nicht kniffelig: String
Schöner aber wäre in der Tat, wenn man zwischen den Typen "String (ohne
null)" und "String oder Null" unterscheiden könnte.
> ... Sprache, die für dich dichtet und denkt (Schiller).
> Leute, die mit Java großgeworden sind, vermissen so etwas nicht. Leute
> die z.B. zuerst perl konnten, vermissen sowas schmerzlichst.
Ich hätte jetzt ja gesagt, Perl versaut einen syntaktisch komplett und
hätte nicht gerade diese Sprache als Beispiel gewählt, aber ja,
vielleicht muss man sagen, das Leute, die nur Java können, sich
eigentlich glücklich schätzen können, denn sie wissen nicht, was ihnen
entgeht ;)
>> var x = { A = 1, B = "Hallo" }
>
> Warum nicht: var x = (1, "Hallo");
Weil es keine Tupel sind, sondern Strukturen (Records).
> Laß mich raten: es gibt noch kein pattern matching?
Nein. Wie gesagt, wer noch mehr will, für den gibt es F# oder Nemerle...
>> from c in customers where c.City == "Kiel" select c
> Wo ist der Unterschied zu filter(c=>c.City == "Kiel", customers)? Oder,
> in perl-Notation
> grep { $_->{City} eq "Kiel" } @customers
Da gibt es keinen. Warum das jetzt mit syntaktischem Zucker
zugekleistert wird, wo dank lambdas auch einfache Methoden bzw.
Funktionen reichen würden, weiss wohl nur Hejlsberg.
> Wozu? Man braucht doch nur einen Mapper, nämlich einen, der
> select * from A
> in eine lazy evaluierte Liste umwandelt.
Nein, ganz so einfach ist das nicht, wenn man den Anspruch hat, dass das
gesammte Konstrukt als ein SQL-Statement auf der Datenbank ausgeführt
werden soll. Daher kommt ja dieses Code=Data-Konstrukts in Spiel, was
einem erlaubt, über den Code zu reflektieren und daraus SQL zu
generieren und ihn nicht auszuführen, sondern der Datenbank das SQL zu
geben.
Und träge Auswertung will man wohl selbst den C#-Hackern nicht zumuten.
Schade eigentlich, das erlaubt vieles einfacher zu machen. Ich gestehe
aber, ich fühle mich bei strikten funktionalen Sprachen etwas wohler als
bei Haskell.
> Klingt wirklich alles sehr interessant. Bei Sun können sie sich schon
> mal warm anziehen. Man kann ja gegen MS als Unternehmen haben, was man
> will, aber offenbar haben sie in dem Bereich doch sehr gute Leute, wie
> z.B. Simon Peyton Jones.
Sun glaubt halt zu wissen, was gut für Entwickler ist. Wo habe ich
verdamt noch mal gerade erst gelesen, das tatsächlich ein wichtiger
Sun-Mensch in einem Interview gesagt hat, dass sie glauben, dass es nur
wenige gute Entwikler gibt und Java eine Sprache für die nicht so guten
sein sollte...?
Ich kann nur Chuck Moore (den Erfinder von Forth) sinngemäß zitieren,
der meinte, eine gute Sprache (wie Forth) sollte für den intelligenten,
ausgebildeten und professionellen Entwickler sein, gedacht um Macht zu
geben, nicht um zu beschränken. Ich würde das "empower", auch
"bevollmächtigen", gerne aus in dem Sinn verstehen, dass sie mich
(unter)stützen soll. Aber mit dem "niemals beschränken", gehe ich konform.
>> PS: Die Machen von Nemerle verkünden übrigens stolz (daher habe ich
>
> Die was??? :-)
Die _Macher_ von Nemerle meinte ich.
> Was ich hingegen tatsächlich manchmal vermisse, ist ein Weg, eine
> Klasse quasi nachträglich ein Interface implementieren zu lassen oder
> anders implementieren zu lassen.
Scala kann das (Stichwort: Views). In der Tat ist das ein wichtiges
Feature, was eine statisch getypte Sprache IMHO benötigt, um flexibel
mit vorhandenem Code umzugehen, für den man keinen Quelltext hat oder
den man aus anderen Gründen nicht ändern will.
>> customers.where(c => c.City == "Kiel")
>>
>> und man erkennt hier bereits die Macht der anonymen Funktionen.
>
> Dabei geht aber Information verloren.
Nein, das hast du falsch verstanden (glaube ich). Der Trick ist, dass
eine passende "Where"-Implementation SQL generieren und an die DB
schicken kann.
> dem richtigen Weg, indem sie mit .NET eine Plattform geschafen haben,
> auf der spezialierte, domänenspezifische Sprachen zusammenarbeiten
> können. In Zukunft werden die anderen .NET Sprachen wohl wirklich nur
> noch Untermengen von C# mit anderer Syntax sein.
Ja.
> Besser hätte mir gefallen, wenn die Collections eine Teilmenge von SQL
> verstehen würden. Alternativ hätte man für die Arbeit mit Collections
> auch eine spezielle, domänenspezifische Sprache schaffen können. Die
> Collections bekommen dann den Parser-Baum für den entsprechenden
> Ausdruck, können diesen dann weiter optimieren und letztendlich
> auswerten. Das Typsystem kann solche Ausfrücke zwar üblicherweise nicht
> mehr prüfen; das sehe ich aber als das kleinere Übel an.
Genauso funktioniert das - meiner Meinung nach. Dafür dient doch...
>> Expression<Func<int,int>> e = x => x + 1;
das man an den Parsr-Baum heran kommt.
> Lustig wird's dann mit:
> var e = x => x + 1;
> Die Typinferenz hat dann richtig was zu tun, um herauszufinden, ob das
> nun Expression<Func<int,int>> oder Expression<Func<String,String>> ist.
Nein gar nicht. Sie kann es überhaupt nicht entscheiden und dann muss
da ein default gelten, der IMHO nur die Funktion sein kann.
Ich denke ja, einen impliziten Cast zu benutzen, um einen Parse-Tree zu
bekommen ist eine dumme Idee und nicht ohne Grund war dieses Feature
noch nicht näher in der Spec beschrieben. Besser wäre IMHO, wenn man
eine Methode "getAST" bzw. bei C# eine Property "AST" hätte, mit der man
vom Lambda zum AST kommt.
Dann schreibt man einfach
var ast = (x => x + 1).AST
und wenn ich das jetzt so sehe, ist glaube ich eine statische Funktion
besser von der Lesbarkeit, also
var ast = System.Expressions.ConvertToAST(x => x + 1)
> Mir gefällt diese Richtung von C# überhaupt nicht. Die Sprache wird mit
> Lösungen für spezielle Probleme überfrachtet und damit unglaublich
> komplex.
Jein. Der Compiler wird verdammt komplex. Doch das Select ist (für
kenner der Materie) intuitiv verständlich. Ich denke, es hat für
Microsoft auch noch den angenehmen Nebeneffekt, dass es um so schwerer
wird, einen eigenen C#-Compiler zu bauen :) Das hält die Sprache
konsistent.
Bei Sprachen, wo es extrem einfach ist einen INterpreter oder relativ
einfach ist, einen COmpiler zu bauen wie etwa Lisp, explodieren die
Varianten, womit eigentlich niemandem gedient ist. Bei Sprachen, wo das
verdammt schwer ist, Perl fällt mir ein, benutzt jeder die einzige
vorhandene Implementierung und ist glücklich.
> Typinferenz. Bin mal gespannt, was als nächstes Kommt. Vielleicht
> spezielle Sprachkonstrukte für reguläre Ausdrücke oder für Ein- und
> Ausgabe.
Für reguläre Ausdrücke wäre auch tatsächlich ganz nett...
> Besser wäre es, wenn MS einen Weg finden würde, wie man innerhalb der
> selben Methode verschiedene Sprachen mischen kann.
Wozu sollte das gut sein? Ich könnte vielleicht aus abstrusen Gründen
noch verstehen, wenn man zwei Sprachen in einer Klassendefinition
mischen möchte, aber in einer Methode!?
Übrigens das mit mehreren Sprache pro Klasse geht mit .NET u.U. schon,
denn es gibt seit 2.0 ja partielle Klassen, etwa damit es einfacher wird
ein Stückchen ASP und ein Stückchen C# zu mischen. Vielleicht geht das
dann ja auch mit F# und C#, aber ich glaube eigentlich nicht.
> Einfach den Quelltext als
> XML abspeichern und die GUI ein wenig erweitern.
Quelltext als XML ist IMHO eine Schnapsidee - niemand kann mir erzählen,
dass etwa o:XML (http://www.o-xml.org/docs/tutorial/programming.html)
eine gute Idee ist. Für etwas wie
<o:choose>
<o:when test="1 = 0">
<o:log msg="we know this will never happen"/>
</o:when>
<o:when test="$i = 0">
<o:log msg="this might happen"/>
</o:when>
<o:otherwise>
<o:log msg="otherwise this will happen"/>
</o:otherwise>
</o:choose>
habe ich nur Verachtung übrig. Und jetzt komme mir keiner mit dem
Argument, dieser Quelltext sei ja nicht für Menschen gedacht. Dann ist
es auch keine Programmiersprache, über die wir hier reden müssen. Ich
habe das so verstanden, dass der/die Autor(en) annehmen, man möchte als
mensch sowas wirklich schreiben.
Wenn schon dann bitte dies hier
(choose
((= 1 0) (log ..))
((= i 0) (log ..))
(else (log ..)))
Man beachte, dass der XML-Teil zwei Sprachen mixt: Zum einem das XML im
Namensraum o und zum anderen diese herkömmliche String-basierte Sprache,
die wir im Test-Attribut sehen. Das ist IMHO bereits indiskutabel, weil
ich auf diese weise das "o:choose" nicht in einem Ausdruck benutzen
kann, was eine gute ortogonale Sprache aber erlauben muss. Daher wäre
eine korrekte XML-basierte Sprache so zu gestalten:
<o2:when>
<o2:condition>
<o2:equal>
<o2:literal>1</o2:literal>
<o2:literal>1</o2:literal>
</o2:equal>
</o2:condition>
...
und - oh Wunder - das macht auch mein S-Expression-Schnipsel, nur mit
viel weniger Syntax.
> Da MS die komplette
> Plattform inkl. IDE und Versionsverwaltung kontrolliert, würde der
> Anwender von solch einer Änderung kaum was mitbekommen.
Was du meinst, dass da noch eine oder mehrere neue Sprachen erfunden
werden, die dann auf eine XML-Zwischensprache aufsetzen. Das kann man
machen, aber dann müssen wir diese neuen Sprachen diskutieren und können
die XML-Zwischensprache genauso ignorieren, wie wir Bytecode und
Maschinencode in dieser Diskussion ignorieren.
> Angesichts der Komplexität und der vielen möglichen Probleme, bin ich
> mir nicht sicher, ob C# 3.0 jemals implementiert wird.
Da kannst du dir sicher sein. Ich habe mir die downloadbare Version des
Compilers bereits installiert, allerdings scheint es nur mit einer
englischen C#-IDE zu gehen und ich habe noch nicht geschaut, ob ich
vielelicht eine Kommandozeilen-Version irgendwo auf der Platte finde -
die Doku ist dürftig.
Und C-Omega, was noch mehr kann als C# 3.0, gibt es ebenfalls bereits
von Microsoft-Research als funktionierende Sprache.
> Ich vermute, dass
> MS vorher in Richtung MDA umschwenkt.
Ich hoffe nicht. Dann wäre das auch keine Alternative mehr... Ist aber
durchaus denkbar, weil das die Möglichkeit bietet, teure Tools zu
verkaufen und der Markt sie gierig kaufen würde, in der Hoffnung, dass
damit dann endlich auch mittelmäßig Entwickler zumindest mittelmäßigen
code schreiben generieren lassen können.
>> var foo = expression ? "Bar" : null; // welchen Typ hat jetzt foo -
> gemeinsamen Hierarchie hinzukommen (ich weiß nicht, was da alles möglich
> ist). Womoglich funktioniert das Ganze auch noch mit Variablen: var foo
> = expression ? a : b;
Ich weiss nicht, wo ihr das Problem seht.
Gesucht sei eine Funktion typeof e :: Expr -> Type, die den statischen
Typ eines Ausdrucks e berechnet. Dann ist
typeof c ? t : e = union (typeof t) (typeof e)
wobei
typeof StringLit l = String
typeof NullLit = Null
und
union Null Null = Object
union Null t = t
union t Null = t
union t1 t2 = ..finde den gemeinsamen supertyp...
Letzteres muss die Typhierachie berücksichtigen und bei Java oder C#
auch noch die Interfaces. Eine Implementation, die wahrscheinlich zu
naiv ist, wäre
if t1 < t2 then t2
elif t2 < t1 then t1
else Object
wobei jetzt "<" (die Subtyp-Relation) geeignet definiert werden muss. Dies
data Type = Object | Null | T of [Type]
müsste das Java-Typsystem abbilden. Wir haben Object als "top"-Element,
wir haben Null, was wir speziell behandeln müssen (siehe oben) und wir
haben Class-or-Interface-Types (T), die eine Liste von Supertypen haben
können. Null darf dabei nicht in der Liste der Supertypen auftauchen.
t1 < t2 = t1 = t2
_ < Object = true
Null < _ = true
T [t1] < t2 = t1 < t2
und die Regel für den Fall einer Liste kann sich der geneigte Leser
selbst überlegen :)
Ich glaube - das ist der komplette Typinferrenzer für den Ausdruck und
den halte ich nicht für schwer. In Java würde natürlich der
Boilerplate-Code für die notwendigen Klassen, Konstruktoren und Methoden
den eigentlichen Algorithmus komplett ersticken.
>> All dies ist nötig, Strukturen a la XML in der Sprache definieren zu
>> können, ohne das man gleich drei Millionen Zeilen boilerplate-Code
>> schreiben oder generieren muss. Sehr gut.
>
> Beispiel?
Ein Beispiel hast du doch da bei dem Rectangle schon gesehen.
> gut aber wenn die Datenbank irgendwo auf einem Server liegt, dann will
> ich ja nicht lokal auswerten. Da genügt es nicht einen Lambdaausdruck
> c=>c.City=="Kiel", zu haben, man braucht strukturelle informationen
Siehe meine anderen Antworten.
>> hinschreiben kann und dann hat man ein Objekt "e" der Klasse
>> System.Query.Expression, was man dann wohl in subexpressions zerlegen
>> kann. Das scheint aber noch Zukunftsmusik zu sein.
>
> also hat man es noch nicht.
Doch, es soll für 3.0 kommen, ist aber noch nicht spezifiziert.
> Ingo R. Homann wrote:
>>> Man kann auch existierende Klassen um neue Methoden ergänzen...
>> Habe ich auch schon vermisst. Vor allem z.B. auch, bereits
>> existierende Methoden "auszutauschen". Ob das gut ist, ist
>> allerdings eine andere Frage. (Dürfte zu schwer debugbarem und
>> schwer wartbarem Code führen.)
>
> Ich denke nicht, dass C# austauschen kann. Und ich denke auch, es
> können nur nicht überschreibbare (final in Java-Sprech) Methoden
> ergänzt werden, sodass ich dieses Feature als eher überflüssig in
> einer Java- bzw. C#-Sprache ansehe.
>
>> Ausserdem frage ich mich, ob die neuen Methoden dann auch Zugriff
>> auf private members habe.
>
> Denke nein. Es ist einfachster syntaktischer Zucker, dass man
In http://blogs.msdn.com/cyrusn/archive/2005/09/13/465390.aspx
wird etwas genauer auf die Erweiterungsmethoden eingegangen.
Es ist wirklich 'nur' syntaktischer Zucker. KLingt aber auf
den ersten Blick trotzdem recht nett.
Ulrich
> Ich hoffe aber, C# wird da nicht direkt eine List<Customer> liefern,
> sondern doch etwas in der Art Iterator<Customer> o.ä., damit auch
> größere Ergebnisse effizient zurückgeliefert werden können und z.B.
> sowas möglich ist:
>
> customers.Where(...).Limit(10)
>
> Limit wäre dann eine Methode von Iterator, die nach den ersten 10
> Elementen ein Ende des Stroms simuliert.
Das geht doch auch mit List<Customer> - die List-Implementation
muss ja nicht alles gleichzeitig dahaben, und mit subList() wird
eben eine Teilliste erstellt.
Paul
--
Ich beantworte Java-Fragen per E-Mail nur gegen Bezahlung. Kostenpunkt
100 Euro pro angefangene Stunde. Bitte erwähne in der Anfrage, dass du
das akzeptierst, da ich sonst die E-Mail einfach ignoriere.
(In der Newsgroup fragen ist billiger und oft auch schneller.)
Wie könnte es das, da es ja doch keine feste Syntax hat? :-)
Aber im Ernst, Perl ist unter den eher konventionellen Sprachen immer
noch vorbildhaft, was das betrifft:
- es gibt Integer-Literale
- es gibt String Literale
- es gibt Array-Literale
- es gibt Hash-Literale
- und es gibt sogar Funktions-Literale
> >> var x = { A = 1, B = "Hallo" }
> >
> > Warum nicht: var x = (1, "Hallo");
>
> Weil es keine Tupel sind, sondern Strukturen (Records).
Was genau ist der Unterschied zwischen einem n-Tupel und einem Record?
Ein Record ist einfach nur syntaktischer Zucker für ein n-Tupel plus
n-Funktionen á la
a (x,_) = x
b (_,x) = x
(Wenn das Ding mutable sein soll, rechne noch die Setter-Funktionen
hinzu)
> Und träge Auswertung will man wohl selbst den C#-Hackern nicht zumuten.
> Schade eigentlich, das erlaubt vieles einfacher zu machen. Ich gestehe
> aber, ich fühle mich bei strikten funktionalen Sprachen etwas wohler als
> bei Haskell.
Ich glaube, niemand, auch nicht der funktional Unbeleckte, würde
wollen, daß a?b:c, a&&b oder x||y strikt ausgewertet würden. Alles
nur eine Frage der Gewöhnung.
Ein echte Typinferenz könnte tatsächlich einen passenden Typ
schlussfolgern. Hier scheint es sich aber nur um eine sehr einfache
Typ-Vervollständigung zu handeln. Dies könnte problemlos die IDE mit
übernehmen, als eine Art erwiterte Syntax-Vervollständigung. Nach der
Vervollständigung würde auch unmissverständlich dastehen, welcher Typ
gewählt wurde.
> Ich denke ja, einen impliziten Cast zu benutzen, um einen Parse-Tree zu
> bekommen ist eine dumme Idee und nicht ohne Grund war dieses Feature
> noch nicht näher in der Spec beschrieben. Besser wäre IMHO, wenn man
> eine Methode "getAST" bzw. bei C# eine Property "AST" hätte, mit der man
> vom Lambda zum AST kommt.
[...]
Macht wenig Sinn, darüber zu diskutieren. Die Spezifikation schweigt
sich zu abstrakten Syntaxbäumen noch weitgehend aus. Den Typen kann man
auch nur entnehmen, welche Ein- und Ausgabetypen eine Lambda-Funktion
erwartet. Ob der innere Aufbau der Funktion als Syntaxbaum oder nur als
Bytecode verfügbar ist, lässt sich nur vermuten.
[...]
>> Besser wäre es, wenn MS einen Weg finden würde, wie man innerhalb der
>> selben Methode verschiedene Sprachen mischen kann.
>
>
> Wozu sollte das gut sein?
Nun, für jede Aufgabe gibt es ein passendes Werkzaug und eben auch die
passende Sprache. Es gibt spezielle Sprachen zum Bearbeiten von Strings,
zum generieren von Berichten, zum Aufbau von Benutzeroberflächen, zum
Abfragen von Datensammlungen usw. Wenn nun eine Methode zuerst eien
Datensammlung abfragt und dann das Ergebnis als String aufbereitet, wäre
es sehr sinnvoll, wenn sie sowohl eine Abfragesprache wie SQL oder OQL
als auch eine an Velocity oder auch an prinf angelehnte Sprache
verwenden könnte. Momentan werden solche Spezialsprachen bereits
genutzt. Allerdings werden die entsprechenden Ausdrücke üblicherweise
als Strings übergeben. Sie werden dann erst zur Laufzeit interpretiert.
Dies ist nicht nur langsam, sondern lässt auch dem statischen Typsystem
keine Chance.
Man kann diese Probleme zum Teil umgehen, indem man Codegeneratoren
verwendet. Dann ist aber der Schritt zu einem modularen Compiler nicht
mehr onderlich groß.
[...]
> Quelltext als XML ist IMHO eine Schnapsidee - niemand kann mir erzählen,
> dass etwa o:XML (http://www.o-xml.org/docs/tutorial/programming.html)
> eine gute Idee ist. Für etwas wie
[...]
> habe ich nur Verachtung übrig.
Normalerweise würde ich die Diskussion hier abbrechen. Viele
Programmierer haben eine sehr dogmatische, schon fast religiöse
Sichtweise, was Programmiersprachen betrifft. Ich kann das auch
nachvollziehen --- ich wollte auch nicht mit einer unausgegorenen
Programmiersprache arbeiten müssen. Allerdings sollte es möglich sein,
über solche Themen unvoreingenommen zu diskutieren. Ich riskier's
einfach mal.
Ich dachte eher an eine Sprache in der Art:
<class name="...">
<extends>...</extends>
<method name="...">
<attributes>...</attributes>
<parameters>...</parameters>
<expression language="ExtendedJava" returns="...">
//ganz viel Java kram
int participants=<expression language="OQL" returns="Integer">
SELECT ... WHERE ...
</expression>
String message=\ participants -> <expression language="printf"
returns="string"><parameter type="int"/>Teilnehmer: %i</expression>
return message;
</expression>
</method>
</class>
Mit (\ x y z -> ...) meine ich eine anonyme Labda-Funktion. Die Syntax
habe ich funktionalen Welt abgeguckt, mit "\" soll das griecische
"Lambda" dargestellt werden. Vor dem "->" stehen die Paramter, dahinter
die Implementation der Funktion.
Eine modulare Sprache macht die Arbeit für alle Beteiligten einfacher.
Refactoring- und UML-Werkzeuge bekommen alle nötigen Informationen, der
Meta-Compiler weiss, welchen Ausdrücke mit welchem Compiler übersetzt
werden müssen, das Typsystem kann die Ausdrücke statisch prüfen und der
Programmierer kann für jede noch so kleine Teilaufgabe das passende
Werkzeug verwenden. Für Spezialaufgaben kann man sogar problemlos eigene
Sprachen definieren.
Über die Syntax kann man natürlich streiten. Eine vernünftige IDE müsste
die XML-Konstrukte natürlich verstecken. Alternativ könnte man die
Teilausdrücke auch in extra Dateien auslagern. Zur Not könnte man sogar
den XML-Kram noch mit einem herkömmlichen Editor bearbeiten. Lediglich
mit Sonderzeichen wird's natürlich schwierig. Eine gute IDE könnte
natürlich auch eine stark eingeschränkte Typinferenz benutzen, um die
Typangaben in den XML-Tags automatisch einzutragen.
[...]
> > Ich vermute, dass
>
>> MS vorher in Richtung MDA umschwenkt.
>
>
> Ich hoffe nicht. Dann wäre das auch keine Alternative mehr... Ist aber
> durchaus denkbar, weil das die Möglichkeit bietet, teure Tools zu
> verkaufen und der Markt sie gierig kaufen würde, in der Hoffnung, dass
> damit dann endlich auch mittelmäßig Entwickler zumindest mittelmäßigen
> code schreiben generieren lassen können.
>
Diese grafischen Tools sollten sich ja auch nur mit der Struktur einer
Software befassen. Wenn es für einzelne Aufgaben nützlich ist, eine
grafische Programmiersprache zu verwenden, ist das natürlich auch OK.
Allerdings sollte so eine Sprache nicht mit dem Modellierungs-Werkzeug
verwoben sein. Mit einer modularen Programmiersprache wäre es aber
durchaus möglich, grafische Spprachen mit herkömmlichen Sprachen zu
vermischen. Eben für jede Aufgabe das passende Werkzeug.
Geggo
Das da?
> Ein weiteres Feature, was man sich bei bei funktionalen Sprachen und
> Lisp angeschaut hat, ist der generische Initialisierer. Immer noch
> etwas schwerfällig, aber man kann jetzt
>
> new Point() { X = 1, Y = 2 }
>
> schreiben, was dann einem
>
> var p = new Point(); p.X = 1; p.Y = 2;
>
> entspricht. Zu beachten ist, dass X und Y properties sind, also
> Methoden, die nur so aussehen, als wären es einfache Felder.
>
> Wenn die Klasse klar ist, etwa ein ein Rectangle zwei Punkte origin
> und corner erwartet, kann man diese auch weglassen:
>
> new Rectangle { Origin = { X = 0, Y = 0 }, Corner = { X = 10, Y = 10
> }}
Kann sein dass ich auf dem Schlauch stehe, aber in wie fern hilft das
mir jetzt mit XML? Ich könnte mir ja noch vorstellen, dass man mit
obigem versucht sowas zu erzeugen:
<Rectangle>
<Origin x="0" y="0">
<Corner x="10" y="10">
</Rectangle>
nehmen wir mal an es wäre so... brauche ich dann nicht jedesmal eine
Klasse wenn ich so schreiben will? Und brauche ich dann nicht auch eine
Klasse für das Lesen? Besser gesagt für jede Ebene des xml eine Klasse?
Und wenn nicht, wie "springe" ich dann an die interessante Stelle? Und
wieso ist das da oben besser als das was der XMLEncode und XMLDecoder so
liefern?
Gruss theo
> Was genau ist der Unterschied zwischen einem n-Tupel und einem Record?
Er ist rein syntaktisch.
> Ich glaube, niemand, auch nicht der funktional Unbeleckte, würde
> wollen, daß a?b:c, a&&b oder x||y strikt ausgewertet würden. Alles
> nur eine Frage der Gewöhnung.
Da hast du Recht.
> Ein echte Typinferenz könnte tatsächlich einen passenden Typ
> schlussfolgern. Hier scheint es sich aber nur um eine sehr einfache
> Typ-Vervollständigung zu handeln. Dies könnte problemlos die IDE mit
> übernehmen, als eine Art erwiterte Syntax-Vervollständigung. Nach der
> Vervollständigung würde auch unmissverständlich dastehen, welcher Typ
> gewählt wurde.
Nein, das ist nicht das selbe: Das wäre dann ein Code-Generator und der
kann immer nur ein Workaround sein. Ich will nicht, dass man nur
Tipp-Arbeit sparen kann, sondern ich will, dass man Lese-Arbeit sparen
kann. Aus diesem Grund ist jede Art der Code-Generation
contraproduktiv. Sie erspart mir keine Lese-Arbeit.
Zudem ist es im Falle von C# auch noch teilweise unmöglich, den Typ zu
benennen, weil es ein sog. anonymer Typ ist, der durch Tupel (bzw.
Record)-Bildung entstanden ist, etwa
var q = new { A = 1, B = "2" }
Dies entspricht einem
class Anonym1 {
private int a;
private string b;
public property A { get { return a; } set { a = value; }
public property B { get { return b; } set { b = value; }
// equals und hashcode-Methode...
}
Anonym1 q = new Anonym1();
q.A = 1;
q.B = "2";
> Macht wenig Sinn, darüber zu diskutieren. Die Spezifikation schweigt
> sich zu abstrakten Syntaxbäumen noch weitgehend aus. Den Typen kann man
> auch nur entnehmen, welche Ein- und Ausgabetypen eine Lambda-Funktion
> erwartet. Ob der innere Aufbau der Funktion als Syntaxbaum oder nur als
> Bytecode verfügbar ist, lässt sich nur vermuten.
Aber es macht Sinn, darüber zu spekulieren :) Der Code ist zwar komplett
undokumentiert und auch das dem Linq-Preview beiliegende Beispiel ist
aus diesem Grund nahezu wertlos, aber wenn ich mir anschaue, wie die
Klassen aufgebaut sind, dann rate ich, dass in den Metadaten zu einem
lamda-Ausdruck entweder ein serialisierter Expression-Objekt-Baum steckt
oder etwas (z.B. der Bytecode) woraus man diesen erzeugen kann.
Jedenfalls stellt die Klasse Expression eine ganze Reihe von statischen
Factory-Methoden zur Verfügung, aus denen man Expressions bauen zu einer
Lambda-Funktion zusammenbauen kann und diese dann ganz normal ausführen
kann. Mich interessierte eigentlich die andere Richtung, aber dafür
habe ich keinen Beispielcode gefunden. Man müsste wohl mal einen Blick
in den dekompilierten Bytecode werfen...
Was mir nämlich nicht so ganz klar ist, ist wie man aus einem Ausdruck wie
t.Where(c => c.Name = "sma").Select(c => c.Name)
einen einzigen SQL-String baut. Aus jedem Argument selbst, ist kein
Problem, aber wie über Methodengrenze weg verbiden? Da jede dieser
Funktionen einen Iterator<?> zurückgeben, könnte dieser wissen, dann
stückweise das SQL bauen und erst beim ersten Zugriff die SQL-Query
auslösen. Ob das so gemacht ist?
Iterator Where(this DBTable t, Expression e) {
return new DbIterator() {
String whereClause = makeWhereClause(e);
}
}
Iterator Select(this DbIterator i, Expression e) {
this.selectClause = makeSelectClause(e);
return this;
}
Object Next(this DbIterator i) {
if (firstAccess) {
this.cursor = doQuery();
firstAccess = false;
}
return cusor.Next();
}
>> Wozu sollte das gut sein?
>
> Nun, für jede Aufgabe gibt es ein passendes Werkzaug und eben auch die
> passende Sprache. Es gibt spezielle Sprachen zum Bearbeiten von Strings,
> zum generieren von Berichten, zum Aufbau von Benutzeroberflächen, zum
> Abfragen von Datensammlungen usw.
Unbestritten. Aber mehrere Sprachen in einer Methode mischen empfinde
ich als zu feingranular. Was dabei rauskommt wäre wie JSP oder PHP, wo
man ja auch zwei Sprachen mischt. Das ist im allgemeinen schwer lesbar.
Wenn wir von domain-spezifischen Sprachen sprechen, dann sollte sich
deren Syntax IMHO an die "Hauptsprache" anlehnen, was wiederum bedeutet,
diese muss so ausdrucksstark sein, das dies unter Erhaltung einer
lesbaren Syntax möglich ist. Bei Java habe ich da so meine Zweifel.
Ruby zeigt mit Rails ganz nett, dass es hier möglich ist. Klassisches
Beispiel wäre Lisp, wo alles S-Expressions sind. Damit kann man nahezu
alles einheitlich beschreiben - ganz so wie du es mit dem XML-Beispiel
zeigst (nur mit weniger Syntax ;)
> Man kann diese Probleme zum Teil umgehen, indem man Codegeneratoren
> verwendet. Dann ist aber der Schritt zu einem modularen Compiler nicht
> mehr onderlich groß.
Mein Vorbehalt bzgl. Codegeneratoren schilderte ich bereits oben.
Ich würde sagen, ein Makroprozessor kann helfen, da eine Protion
syntaktischer Zucker zur besseren Lesbarkeit beitragen kann.
> Ich dachte eher an eine Sprache in der Art:
> <class name="...">
> <extends>...</extends>
> <method name="...">
> <attributes>...</attributes>
> <parameters>...</parameters>
> <expression language="ExtendedJava" returns="...">
> //ganz viel Java kram
> int participants=<expression language="OQL" returns="Integer">
> SELECT ... WHERE ...
> </expression>
> String message=\ participants -> <expression language="printf"
> returns="string"><parameter type="int"/>Teilnehmer: %i</expression>
> return message;
> </expression>
> </method>
> </class>
Wir reden hier von einer Programmiersprache, die ein Mensch eintippen
soll, oder? Jedenfalls rede ich davon. Ich rede nicht von einer
Zwischensprache, die ein Computer erzeugt, die man vielleicht mal zum
Debuggen des eigentlichen Sprachcompilers anschaut und die ansonsten
aber nur von Computern gelesen und geschrieben wird. Obiges ist ein AST
in XML-Syntax. Ich möchte das aber so niemals direkt aufschreiben müssen.
Zudem sehe ich in diesem XML-Beispiel genau wie in der o:XML-Sprache
wieder einen Bruch. Neben der äußeren Struktur, die du in XML
beschreibst, habe ich noch String-Einschlüsse, die ich mit einem
"herkömmlichen" Parser parsen muss. Ich habe daher in deinem Beispiel
nicht wie von dir beabsichtigt drei Programmiersprachen (ExtendedJava,
OQL und printf) sondern vier. Das sind für meinen Geschmack zwei zuviel :)
Ich finde zudem etwas wie
(class ...
(extends ...)
(method ... (attributes ...) (parameters ...)
(set participants (oql:select ...))
(set message (fn (participants)
(p:printf (int -> string) "Teilnehmer %i")))))
lesbarer - einfach weil es kürzer und mit weniger redundanz versehen
ist. Beides sind aber einfach nur verschiedene textuelle
Repräsentationen des selben AST wobei du IMHO noch besser die
Möglichkeiten von XML ausnutzen könntest und statt eine Subsprachen über
Strings zu kennzeichnen, lieber alles in XML beschreiben und dann
Namensräume zu benutzen. Diese wären ideal dafür geeignet.
> Über die Syntax kann man natürlich streiten. Eine vernünftige IDE müsste
> die XML-Konstrukte natürlich verstecken.
Aber dann müssen wir auch nicht über das XML reden, sondern über das,
was die IDE stattdessen anzeigt. Wie soll das aussehen? Was gibt man
ein? Wie gibt man das ein?
> mit Sonderzeichen wird's natürlich schwierig. Eine gute IDE könnte
> natürlich auch eine stark eingeschränkte Typinferenz benutzen, um die
> Typangaben in den XML-Tags automatisch einzutragen.
Und ich würde hier sagen, ein guter Compiler kann das selbst. Die IDE
soll nicht versuchen, eine unpraktische Sprache benutzbar zu machen,
sondern man sollte lieber die Sprache fixen - solange man das noch kann.
>>> Beispiel?
>>
>> Ein Beispiel hast du doch da bei dem Rectangle schon gesehen.
>
> Das da?
Auch Channel9 gibt es ein einstündiges Video, wo Heljberg die Neuerungen
von Linq zeigt, u.a. auch XML-Erzeugung. Ich meinte aber nicht
XML-Syntax, sondern nur hierachische Strukturen, wo Elemente wieder
Kindelemente haben können.
Ein Beispiel für XML-Erzeugung sieht so aus:
XElement contacts = new XElement("contacts",
from p in persons
select new XElement("contact",
new XElement("name", p.Name),
from ph in p.PhoneNumbers
select new XElement("phone", ph)
)
);
Hat man nicht generische XML-Elemente vorliegen, sondern konkrete
Klassen wie Contact und Phone, wäre das noch kürzer:
from p in persons
select new Contact {
Name = p.Name,
PhoneNumbers = new List {
from ph in p.PhoneNumbers
select new Phone(ph)
}
)
Stimmt, das will ich nicht; ich will mit möglichst wenig
Tastenanschlägen ein neues Objekt erzeugen und typsicher mit benannten
Werten initialisieren.
Wenn ich dabei eine anonyme Klasse erstelle, so what?
Der überzeugendste Argument kam von Ingo, nämlich dass gängige code
formatter damit nicht klarkommen. Ich hätte zwar kein gutes Gefühl
dabei, so etwas einzusetzen, aber einen konkreten Grund kenne ich nicht.
Timo
[...]
> Hat man nicht generische XML-Elemente vorliegen, sondern konkrete
> Klassen wie Contact und Phone, wäre das noch kürzer:
>
> from p in persons
> select new Contact {
> Name = p.Name,
> PhoneNumbers = new List {
> from ph in p.PhoneNumbers
> select new Phone(ph)
> }
> )
so wird die Sache klarer. thx.
Gruss theo
Fehlt nur noch eine Methode für das angenehme Ändern. Die erste Version
des Quelltext kann ein anderer Programmierer nicht vernünftig lesen und
damit auch kaum ändern. Die zweite Version kann man zwar gut lesen, aber
eben nicht angenehm schreiben. Und damit eben auch nicht angenehm ändern.
Geggo
Ja, meinte ich. Generierung ist besser, aber du hast mich ja auch so
verstanden.
> Wir hatten doch gerade denn Fall, daß jemand schrieb "Das ist
> xwar leichter zu schreiben, aber schwerer zu lesen."
Jemand schrieb das? So, so...
> Wenn nun der Code-Generator aus solchem Code Code generiert,
> der leichter zu lesen ist, dann ist dies ein Gewinn in jeder
> Hinsicht: Der Code war leichter zu schreiben und ist nun
> leichter zu lesen.
Ein Code-Generator generiert meist mehr Code als da schon steht. Damit
muss man mehr lesen als zuvor und das setze ich im Regelfalls mit
schlechter lesbar gleich.
> Beispielsweise ist für mich manchmal:
>
> IFNO(user)
>
> leichter zu schreiben. Es ist aber für andere schwieriger zu
> lesen.
Das ist nicht schwer zu lesen: Iffno. Mir ist nur absolut nicht klar,
was gemeint ist :) Ich würde ein ifFalse vermute (No = Nein) aber
offenbar meinst du ja ifNull. Die beiden "ll"s würde ich mir dann ja
auch noch gönnen. Das wäre ein IMHO akzeptabler Kompromis zwischen
deinem Makro und der Version "if (user == null)".
Villeicht verstehe ich Dich falsch aber ich sehe kein Problem in der
Umsetzung, auch nicht einer typeof-Funktion. Natürlich kann es eine
Funktion geben, die den Typ eines Ausdrucks berechnet.
Wenn der Leser genötigt wird, eine Funktion einzusetzen, nur um den Typ
eines Ausdrucks herauszufinden, dann halte ich das nicht für besonders
schön.
Aber wie bereits angedeutet: ich kenne C# nicht und kann daher auch
völlig daneben liegen.
Unabhängig davon gilt natürlich, dass es stark davon abhängt, wie das
Konstrukt eingesetzt wird. Soll heissen: ich bin überzeugt davon, dass
man sich mit dieser Syntax so ausdrücken /kann/, dass es auch angenehm
zu lesen ist.
Gruß
Michael
> Lustig wird's dann mit:
> var e = x => x + 1;
> Die Typinferenz hat dann richtig was zu tun, um herauszufinden, ob das
> nun Expression<Func<int,int>> oder Expression<Func<String,String>> ist.
> Noch lustiger wird's mit überladenen Operatoren...
Das ist doch trivial. Der Compiler muß nur nachschauen, in welchem
Interface '+' definiert ist, z.B.
interface Num<A> {
...
Num<A> "+"(Num<A>, Integer) { ... }
...
}
Der Aufruf e(a) ist dann okay, wenn a von einem Typ ist, der Num
implementiert. Oder auf deutsch: Man kann 1 nur zu einer Zahl addieren.
Aus systematischen und logischen Gründen sollte der
String-Verkettungsoperator dann natürlich anders heißen als '+'.
David J
-----------------------------------------------------------------------------------------------------
http://KickJava.com - Java API By Example, From Geeks To Geeks.
http://KickJava.com/freeBooks.html - 100s of Free online Java Books.
http://KickJava.com/browse.html - Browse all J2SE, J2EE and J2ME APIs
and examples in one place.
http://kickjava.com/search.html - Searching 20,041,731 lines of Java
source codes.
http://KickJava.com/news - Daily Java news and articles.
Nachtrag: Ich habe mir jetzt die Spezifikation auch mal durchgelesen, es ist
noch weniger knifflig. Ausdrücke, die zur Compilezeit null sein können, sind
nicht erlaubt.
Andreas
Gab es nicht heftigen Protest unter den VB-Entwicklern, als mit VB.NET auf
Shortcut-Evaluation umgestellt werden sollte? Mich ärgert das Fehlen
selbiger zwar jedes Mal, wenn ich was in VBA programmieren muss (sogar in
iff, den ?:-Äquivalent), aber offenbar haben zumindest einige Leute auch
Code produziert, der anders nicht läuft.
Andreas
Wenn du vielleicht ifNo(user) geschrieben hättest und ich nicht mehr zu
so später Stunde geantwortet hätte bzw. das ganze sorgfältiger gelesen
hätte, dann wäre es klar gewesen :)
Klassen um Methoden erweitern, die nicht in der Klasse selbst
angesiedelt sind - hört sich wie ne gute Idee an, ist es vielleicht
auch, aber die Umsetzung gefällt mir nicht. Eine bessere Idee hab ich
jetzt grade leider nicht ...
> Ein weiteres Feature, was man sich bei bei funktionalen Sprachen und
> Lisp angeschaut hat, ist der generische Initialisierer. Immer noch
> etwas schwerfällig, aber man kann jetzt
>
> new Point() { X = 1, Y = 2 }
>
> schreiben, was dann einem
>
> var p = new Point();
> p.X = 1;
> p.Y = 2;
>
> entspricht. Zu beachten ist, dass X und Y properties sind, also
> Methoden, die nur so aussehen, als wären es einfache Felder.
Hmm, IMHO auch eher suboptimal, denn eigenlich wollte man ja
var p = new Point(1, 2);
schreiben - falls ein solcher Konstruktor existiert.
Auch hier habe ich jetzt keine super tolle Idee.
> Es wird aber noch besser. Man kann jetzt
>
> from c in customers where c.City == "Kiel" select c
>
> schreiben, was einem die Mächtigkeit von SQL in die Sprache zieht und
> natürlich die Vorbereitung dafür ist, dass man typsicher und direkt in
> C# genauso auf internen Datenstrukturen wie collections und streams wie
> auf XML-Strukturen oder Datenbanken arbeiten kann. Das sollte endlich
> die Impedanz-Lücke zwischen Objekten und Relationalen Daten auf die
> einzig sinnvolle Weise schließen - man erlaubt select und co direkt in
> der Sprache.
Hier hört's auf!
Erstmal eine Kritik an JDBC bzw. an PreparedStatements an sich:
Ich habe mich grade entschieden, das ich es schrecklich finde, dass man
sowas schreibt wie "SELECT * FROM customers WHERE city=?" - und dann bei
längeren SQL-Statements schlecht wartbaren Code erzeugt, da man sich
selber schon 10mal beim abzählen der Fragezeichen vertan hat.
Ich finde es aber auch Schwachfug, sowas in die Syntax einer Sprache
aufzunehmen! Ich würde lieber die Präprozessortechnik wieder aufgreifen
- aber diesmal anders:
Man erweitere den Java (oder den C#)-Compiler so, dass man er im
Classpath nach Erweiterungen sucht. Würde z.B. JDBC mit einer solchen
Erweiterung wie ich sie mir Vorstelle kommen, so könnte man folgendes
schreiben:
Connection conn = ...
String mycity = "Kiel";
Statement s = conn.prepare{SELECT * FROM customers WHERE city=$mycity};
So. Die Syntax von Java würde nur definieren, wie solche Erweiterungen
der Syntax aussehen (als beispiel eben prepare{}) - das parsing des
Ausdrucks innerhalb der {} würde dann die Compilererweiterung übernehmen
und diese würde dann von mir aus entsprechenden Java/Byte-Code
generieren, der wie früher das ganze mit den "?" macht.
Solche Erweiterungen könnte man dann für Hibernate schreiben usw...
Man kann auch wieder dagegen argumentieren, dass nun ein Wildwuchs von
Syntax-Erweiterungen entstünde - aber diese sind IMHO als solche zu
erkennen, und die Basissyntax der Sprache bleibt klein!
Grüße
Sven
CallableStatement und die PreparedStatements von Oracle kennen named
parameters, weiss nicht wieso das nicht weiter verbreitet ist.
Auf der anderen Seite - wer benutzt das schon handish statt nem OR
Framework.
Gruss
Bernd
Sven Köhler wrote:
> Man erweitere den Java (oder den C#)-Compiler so, dass man er im
> Classpath nach Erweiterungen sucht. Würde z.B. JDBC mit einer solchen
> Erweiterung wie ich sie mir Vorstelle kommen, so könnte man folgendes
> schreiben:
>
> Connection conn = ...
> String mycity = "Kiel";
> Statement s = conn.prepare{SELECT * FROM customers WHERE city=$mycity};
Ich gehe aber (da Du ja auch was von 'Präprozessor' schriebst) mal
davon aus, dass der String dann direkt beim "Instanziieren" ausgewertet
wird, also, dass folgendes dann *nicht* funktioniert:
Connection conn = ...
String mycity = "Bielefeld";
Statement s = conn.prepare{SELECT * FROM customers WHERE city=$mycity};
s.execute();
...
mycity="Halle a.d. Saale";
s.execute();
...
Denn wenn Du sowas erlauben wolltest, würde das zu schwer
durchschaubaren Seiteneffekten führen und zu schlecht wartbarem Code.
Im übrigen: Das kannst Du doch fast genauso jetzt schon so ähnlich
(sogar noch besser) haben, es hindert dich keiner daran, eine API zu
implementieren, die folgendes unterstützt:
Connection conn = ...
Statement s = conn.prepare{SELECT * FROM customers WHERE city=$mycity};
s.put("$mycity","Bielefeld");
Vor allem hättest Du dann (gegenüber deinem Vorschlag) den Vorteil, auch
mit PreparedStatements sinnvoll arbeiten zu können:
Connection conn = ...
Statement s = conn.prepare{SELECT * FROM customers WHERE city=$mycity};
s.put("$mycity","Bielefeld");
s.execute();
...
s.put("$mycity","Halle a.d. Saale");
s.execute();
...
Je mehr ich drüber nachdenke, desto besser gefällt mir das. Das mit den
Fragezeichen hat mich auch schon gestört, aber so richtig denke ich erst
jetzt drüber nach, wo Du es ausgesprochen hast.
Ciao,
Ingo
>> new Point() { X = 1, Y = 2 }
>>
>> schreiben, was dann einem
>>
>> var p = new Point();
>> p.X = 1;
>> p.Y = 2;
>>
>> entspricht. Zu beachten ist, dass X und Y properties sind, also
>> Methoden, die nur so aussehen, als wären es einfache Felder.
>
> Hmm, IMHO auch eher suboptimal, denn eigenlich wollte man ja
> var p = new Point(1, 2);
> schreiben - falls ein solcher Konstruktor existiert.
Wollte man eigentlich nicht. Die Variante mit den Schlüsselworten
(Property-Namen) ist IMHO viel besser. Je mehr Properties ein Objekt
hat, desto mehr Kombinationen von Konstruktoren würdest du brauchen.
Diese neue Methode ist generisch und funktioniert immer.
Sie wäre Ideal bei Java-Beans, wo gefordert wird, dass ein
parameterloser Konstruktor existieren muss, alles andere aber
freigestellt ist.
>> schreiben, was einem die Mächtigkeit von SQL in die Sprache zieht und
>> natürlich die Vorbereitung dafür ist, dass man typsicher und direkt in
>> C# genauso auf internen Datenstrukturen wie collections und streams wie
>> auf XML-Strukturen oder Datenbanken arbeiten kann. Das sollte endlich
>> die Impedanz-Lücke zwischen Objekten und Relationalen Daten auf die
>> einzig sinnvolle Weise schließen - man erlaubt select und co direkt in
>> der Sprache.
>
> Hier hört's auf!
Nein, da geht es los :) Alle Syntaxerweiterungen von C# 3.0 gipfeln in
der Möglichkeit, Abfragen in die Sprache zu integrieren. Und das
empfinde ich als ein sehr gutes Ziel.
> Ich finde es aber auch Schwachfug, sowas in die Syntax einer Sprache
> aufzunehmen! Ich würde lieber die Präprozessortechnik wieder aufgreifen
Im Gegenteil. Das ist eine mögliche Lösung auch für deine (IMHO
berechtigte) Kritik, dass SQL-Fragmente in String-Form keine gute Idee sind.
Ich könnte mir vorstellen, du denkst hier nur an SQL. Doch LINQ
(language integrated queries) geht wesentlich weiter. Es ist ein
generisches Rahmenwerk für Anfragen für Objekte, XML, SQL, was auch
immer. Es bringt ein set neuer Verben in die Sprache, die IMHO längst
überfällig sind.
> Man erweitere den Java (oder den C#)-Compiler so, dass man er im
> Classpath nach Erweiterungen sucht. Würde z.B. JDBC mit einer solchen
> Erweiterung wie ich sie mir Vorstelle kommen, so könnte man folgendes
> schreiben:
>
> Connection conn = ...
> String mycity = "Kiel";
> Statement s = conn.prepare{SELECT * FROM customers WHERE city=$mycity};
Das ist eine Lösung, die viel zu SQL-spezifisch ist. Was ist mit XQuery
für XML-Ausdrücke?
Ich finde es besser, statt deines Beispiels, das mit Connections und
Statements hantiert, die ich überhaupt niemals sehen will, lieber sowas
schreibe:
customers.Where(c => c.city == myCity)
Das funktioniert, egal was genau "customers" ist. Es könnte eine
einfache Collection<Customer> sein, es könnte ein Objekt sein, was eine
Datenbank repräsentiert und all die Details wie Connnection und
Statements kennt - mich aber davor bewahrt. Oder es könnte ein getyptes
XML-Dokument sein.
Beachte auch, dass mein Beispiel viel kürzer ist als deines, was ich
ebenfalls positiv finde.
Schließlich bietet eine IDE (die mit den Erweiterungen wie
Erweiterungsmethoden, lokale Typinferrenz, Lambda, usw. klarkommt)
standardmäßig die übliche IntelliSense-Funktion, was mit deiner
Plug-in-Lösung vielleicht auch, aber deutlich aufwendiger möglich wäre,
da das SQL-Fragment in {} einer ganz anderen Syntax folgt.
> Man kann auch wieder dagegen argumentieren, dass nun ein Wildwuchs von
> Syntax-Erweiterungen entstünde - aber diese sind IMHO als solche zu
> erkennen, und die Basissyntax der Sprache bleibt klein!
Da ist bei Java und insbesondere C# das Kind eh schon in den Brunnen
gefallen - deren Syntax ist nicht mehr klein :)
Stefan Matthias Aust wrote:
> Im Gegenteil. Das ist eine mögliche Lösung auch für deine (IMHO
> berechtigte) Kritik, dass SQL-Fragmente in String-Form keine gute Idee
> sind.
Das ist zwar eine (ebenfalls IMHO berechtigte) Kritik, aber manchmal (in
vielleicht 2-5% aller Fälle?) möchte man seine SQL-Statements dynamisch
zusammensetzen:
String sql="SELECT * FROM table WHERE attrib1=? ";
if(...) {
sql+=" AND attrib2=... ";
}
Wie machst Du sowas ohne Strings?
> Das funktioniert, egal was genau "customers" ist. Es könnte eine
> einfache Collection<Customer> sein, es könnte ein Objekt sein, was eine
> Datenbank repräsentiert und all die Details wie Connnection und
> Statements kennt - mich aber davor bewahrt. Oder es könnte ein getyptes
> XML-Dokument sein.
Die Frage ist allerdings, ob man das mit einer vernünftigen API nicht
auch hinbekommen würde. (Was jetzt nicht unbedingt eine Kritik gegen die
neue C#-Lösung sein soll.)
> Schließlich bietet eine IDE (die mit den Erweiterungen wie
> Erweiterungsmethoden, lokale Typinferrenz, Lambda, usw. klarkommt)
> standardmäßig die übliche IntelliSense-Funktion, was mit deiner
> Plug-in-Lösung vielleicht auch, aber deutlich aufwendiger möglich wäre,
> da das SQL-Fragment in {} einer ganz anderen Syntax folgt.
Das ist für mich fast das wichtigste Argument!
>> Man kann auch wieder dagegen argumentieren, dass nun ein Wildwuchs von
>> Syntax-Erweiterungen entstünde - aber diese sind IMHO als solche zu
>> erkennen, und die Basissyntax der Sprache bleibt klein!
>
> Da ist bei Java und insbesondere C# das Kind eh schon in den Brunnen
> gefallen - deren Syntax ist nicht mehr klein :)
Och doch, im Vergleich zu anderen, imperativen OO-Sprachen dieser
Familie (und nur damit kann man sie vergleichen) ist der Java-Kern doch
recht kompakt. Sicher, Assembler und Haskell z.B. sind kompakter,
SmallTalk, Ruby, OCaml, und wie sie alle heissen, vermutlich auch, aber
IMHO sagt das *alleine* noch nichts aus.
Ciao,
Ingo
> Das ist zwar eine (ebenfalls IMHO berechtigte) Kritik, aber manchmal (in
> vielleicht 2-5% aller Fälle?) möchte man seine SQL-Statements dynamisch
> zusammensetzen:
>
> String sql="SELECT * FROM table WHERE attrib1=? ";
> if(...) {
> sql+=" AND attrib2=... ";
> }
>
> Wie machst Du sowas ohne Strings?
Das ist simpel:
var result = table.Where(t => t.attrib1 == ...);
if (...) {
result = result.Where(t => t.attrib2 == ...);
}
Und nein, das ist nicht weniger effizient, weil die eigentliche
DB-Aktion erst dann ausgelöst wird, wenn man auf die Daten wirklich
zugreift.
Ein "prepared statement" ist übrigens auch kein größeres Problem
var preparedStatement(Table t) {
return s => table.Where(t => t.a == s);
}
Dann geht (hoffe ich doch):
var s = preparedStatemnent(t);
s("Hallo");
s("Welt");
> Die Frage ist allerdings, ob man das mit einer vernünftigen API nicht
> auch hinbekommen würde. (Was jetzt nicht unbedingt eine Kritik gegen die
> neue C#-Lösung sein soll.)
Ahem, genau über diese API reden wir doch - damit diese funktioniert
mussten allerdings einige Spracherweiterungen vorgenommen werden.
Extension methods sind nötig, um an beliebige Klassen, die bestimmte
Interfaces implementieren (IEnumerable<T> so weit ich weiss, was bei C#
auch arrays implementieren) automatisch mit der Query-Sprache nachzurüsten.
Die neuen Konstruktoren, anonyme Typen und lokale Typinferenz sind
notwendig, um auf bequeme Weise Projektionen abzubilden. Die neuen
Konstruktoren sind außerdem praktisch, um ad-hoc records aka benannte
tupel zu definieren.
Die lambda-Funktionen sind (siehe oben) bequem, anonyme lokale
Funktionen zu haben, die überall in den Query benutzt werden.
Der Zugriff auf den AST einer lambda-Funktion schließlich ist notwendig,
um diese in SQL oder XQuery-Syntax umzuwandeln.
Schließlich ergänzt C# noch ein neues Statement from/where/select, damit
das ganze noch ein bisschen einfacher hinzuschreiben ist und vor allen
Dingen IntelliSense schlauer sein kann, ohne bestimmte Methodennamen
kennen zu müssen. Bei
from c in _
könnte IntelliSense an der "_" Stelle auch gleich anbieten, ob man statt
auf ein vorhandenes Objekt zugreifen will, doch nicht vielleicht gleich
eine Datenbank mit passend benannter Tabelle anlegen möchte. Das wäre
bei einfachen Methoden - wo man ja mit diesem Tabellen oder
Collection-Objekt beginnen würde, nicht so einfach wie mit einem
Schlüsselwort, das die IDE erkennen kann.
>> Da ist bei Java und insbesondere C# das Kind eh schon in den Brunnen
>> gefallen - deren Syntax ist nicht mehr klein :)
>
> Och doch, im Vergleich zu anderen, imperativen OO-Sprachen dieser
> Familie (und nur damit kann man sie vergleichen) ist der Java-Kern doch
> recht kompakt. Sicher, Assembler und Haskell z.B. sind kompakter,
> SmallTalk, Ruby, OCaml, und wie sie alle heissen, vermutlich auch, aber
> IMHO sagt das *alleine* noch nichts aus.
t nicht T, es ist ein kleines t in Smalltalk :)
Stefan Matthias Aust wrote:
>> String sql="SELECT * FROM table WHERE attrib1=? ";
>> if(...) {
>> sql+=" AND attrib2=... ";
>> }
>>
>> Wie machst Du sowas ohne Strings?
>
> Das ist simpel:
>
> var result = table.Where(t => t.attrib1 == ...);
> if (...) {
> result = result.Where(t => t.attrib2 == ...);
> }
>
> Und nein, das ist nicht weniger effizient, weil die eigentliche
> DB-Aktion erst dann ausgelöst wird, wenn man auf die Daten wirklich
> zugreift.
Ahja. OK. Schön! :-)
>> Die Frage ist allerdings, ob man das mit einer vernünftigen API nicht
>> auch hinbekommen würde. (Was jetzt nicht unbedingt eine Kritik gegen
>> die neue C#-Lösung sein soll.)
>
> Ahem, genau über diese API reden wir doch - damit diese funktioniert
> mussten allerdings einige Spracherweiterungen vorgenommen werden.
Ahem, nein, ich wollte darauf hinweisen, dass es eben auch *ohne*
Spracherweiterung, sondern *nur* über eine API geht.
(Dann allerdings zugegebenermaßen ohne meine geliebte statische
"Typ-"Sicherheit, also mit vielen Strings als Variablennamen.)
> Extension methods sind nötig, um an beliebige Klassen, die bestimmte
> Interfaces implementieren (IEnumerable<T> so weit ich weiss, was bei C#
> auch arrays implementieren) automatisch mit der Query-Sprache nachzurüsten.
Wieso, die Klassen können in einer neuen API-Version doch einfach
zusätzlich ein neues Interface implementieren, so wie Vector auf einmal
auch List implementiert hat.
> Die neuen Konstruktoren, anonyme Typen und lokale Typinferenz sind
> notwendig, um auf bequeme Weise Projektionen abzubilden.
Genau meine Rede. Nur aus Bequemlichkeitsgründen.
Naja, wenn man es exzessiv nutzt (was in den meisten Anwendungen IMHO
eher nicht der Fall sein wird) ist die Bequemlichkeit sicher ein großer
Vorteil.
> Die neuen
> Konstruktoren sind außerdem praktisch, um ad-hoc records aka benannte
> tupel zu definieren.
OK.
> Die lambda-Funktionen sind (siehe oben) bequem, anonyme lokale
> Funktionen zu haben, die überall in den Query benutzt werden.
Hab' mich mit dem lambda-Kalkül bisher nicht befasst. Es geht doch dabei
im Prinzip nur um sowas wie bessere "Funktionspointer" bzw.
"Funktionsobjekte", also was man in Java etwas unhandlich über
Interfaces und anonyme Klassen realisiert, oder?
> [IDE-Unterstützung]
Das wäre, wie gesagt, für mich (neben der statischen Sicherheit, die ja
damit zusammenhängt) das Hauptargument.
>>> Da ist bei Java und insbesondere C# das Kind eh schon in den Brunnen
>>> gefallen - deren Syntax ist nicht mehr klein :)
>>
>> Och doch, im Vergleich zu anderen, imperativen OO-Sprachen dieser
>> Familie (und nur damit kann man sie vergleichen) ist der Java-Kern
>> doch recht kompakt. Sicher, Assembler und Haskell z.B. sind kompakter,
>> SmallTalk, Ruby, OCaml, und wie sie alle heissen, vermutlich auch,
>> aber IMHO sagt das *alleine* noch nichts aus.
>
> t nicht T, es ist ein kleines t in Smalltalk :)
Eigentlich logisch, schließlich ist es ja kein "kleines Gespräch",
sondern eben "Smalltalk" ;-)
Ciao,
Ingo
Ja stimmt, das wollte ich nicht erlauben. Ich weiss aber auch nicht, wie
du drauf kommst. Schließlich ist es üblich, dass Variablen an Ort&Stelle
ausgewertet werden.
> Im übrigen: Das kannst Du doch fast genauso jetzt schon so ähnlich
> (sogar noch besser) haben, es hindert dich keiner daran, eine API zu
> implementieren, die folgendes unterstützt:
>
> Connection conn = ...
> Statement s = conn.prepare{SELECT * FROM customers WHERE city=$mycity};
> s.put("$mycity","Bielefeld");
>
> Vor allem hättest Du dann (gegenüber deinem Vorschlag) den Vorteil, auch
> mit PreparedStatements sinnvoll arbeiten zu können:
Ja, ist mir schon klar! Aber es ging hier nicht so sehr im die Kritik an
JDBC, sondern wie man eine Sprache um Syntax erweitern kann, ohne alles
in die Sprache selbst und damit in die Syntax von z.B. Java aufnehmen zu
müssen.
Dies sollte nur beschreiben, was man IMHO eine Sprache und einen
Compiler bauen könnte, deren Syntax erweiterbar ist ...
Meine Kritik war aber auch, dass man die Syntax nicht beliebig gross
werden lassen soll!
> Ich könnte mir vorstellen, du denkst hier nur an SQL. Doch LINQ
> (language integrated queries) geht wesentlich weiter. Es ist ein
> generisches Rahmenwerk für Anfragen für Objekte, XML, SQL, was auch
> immer. Es bringt ein set neuer Verben in die Sprache, die IMHO längst
> überfällig sind.
Das war mir egal. Mein Vorschlag zur erweiterung von Sprachen um
zusätzliche Syntax bezieht das IMHO mit ein. Sieh doch die LINQ als eine
Bibliothek inklusive Compilererweiterung.
Die Bibliothek kann natürlich Dinge wie XML, SQL und was auch immer mit
einbeziehen.
Es ging hier _nicht_ draum, eine Alternative für LINQ aufzuzeigen,
sondern eine Alternative der Integration in eine Sprache.
>> Man erweitere den Java (oder den C#)-Compiler so, dass man er im
>> Classpath nach Erweiterungen sucht. Würde z.B. JDBC mit einer solchen
>> Erweiterung wie ich sie mir Vorstelle kommen, so könnte man folgendes
>> schreiben:
>>
>> Connection conn = ...
>> String mycity = "Kiel";
>> Statement s = conn.prepare{SELECT * FROM customers WHERE city=$mycity};
>
> Das ist eine Lösung, die viel zu SQL-spezifisch ist. Was ist mit XQuery
> für XML-Ausdrücke?
>
> Ich finde es besser, statt deines Beispiels, das mit Connections und
> Statements hantiert, die ich überhaupt niemals sehen will, lieber sowas
> schreibe:
>
> customers.Where(c => c.city == myCity)
>
> Das funktioniert, egal was genau "customers" ist. Es könnte eine
> einfache Collection<Customer> sein, es könnte ein Objekt sein, was eine
> Datenbank repräsentiert und all die Details wie Connnection und
> Statements kennt - mich aber davor bewahrt. Oder es könnte ein getyptes
> XML-Dokument sein.
Iss mir wie gesagt wurscht. Mein Konzept deckt das IMHO mit ab - hält
aber die Syntax aus der eigentlich Sprache raus.
> Beachte auch, dass mein Beispiel viel kürzer ist als deines, was ich
> ebenfalls positiv finde.
customers.Where(c => c.city == myCity) stellt aber keine Erweiterung der
Syntax dar. Wenn ich (c => c.city == myCity) als anonyme Funktion sehe,
dann stellt das einen ganz normalen Methodenaufruf dar.
Wenn hier soetwas stattfinden würde, wie eine analyse des ganzen
Ausdrucks, dann wäre das schon super. Es wurde z.B. ein Index von c.city
benutzt, um alle Entities mit c.city=MyCity zu finden. Wenn soetwas
tatsächlich stattfindet, dann ist es Pfusch, denn die Syntax gibt hier
ganz klar eine andere Semantik vor!
>> Man kann auch wieder dagegen argumentieren, dass nun ein Wildwuchs von
>> Syntax-Erweiterungen entstünde - aber diese sind IMHO als solche zu
>> erkennen, und die Basissyntax der Sprache bleibt klein!
>
> Da ist bei Java und insbesondere C# das Kind eh schon in den Brunnen
> gefallen - deren Syntax ist nicht mehr klein :)
Das kann natürlich sein. IMHO ist das Kind noch nicht tief genug im
Brunnen, denn etliche nützliche Konstrukte für den täglichen Bedarf fehlen!
Ich will aber beides: vieles sehr leicht erledigen können, aber eine
simple Syntax.
Wer von uns findet z.B. synchronized{} nicht ziemlicht super? Es ist
einfach sich einen Lock zu holen, dieser wird automatisch wieder
zurückgegeben usw.
Es ist aber IMGO Quatsch, das in die Syntax zu integrieren. Vielmehr
würde man erwarten, dass dieses Konstrukt von der Bibliothek kommt, die
die Threads zur Verfügung stellt.
Das Argument finde ich dumm. Wenn es eine Plugin-Lösung gäbe, und das
JDBC-Plugin beim JDK dabei wäre, dann würden die Eclipse-Entwickler auch
ein Plugin dafür bauen.
Den Grund, alles in die Sprache reinzuhauen nur um damit irgentwelche
IDE-Hersteller den Zwang verspüren es zu implementieren, halte ich für
nicht ausreichend.
> Ahem, nein, ich wollte darauf hinweisen, dass es eben auch *ohne*
> Spracherweiterung, sondern *nur* über eine API geht.
Es geht, aber dann wird es so umständlich, dass man sich die Integration
auch sparen kann. Wenn ich etwa statt
c => c.Customer == "ich"
jetzt
mkLambda(mkParameterList("c"),
mkCall("==", mkAccess(mkVar("c"), "Customer"), "ich"))
schreiben und der "where"-Methoden übergeben muss, dann leidet doch die
Lesbarkeit nicht unerheblich. Und ich bin jeztzt schon davon
ausgegangen, dass man sich diese Funktionen alle per "static import"
einbindet, um einen Präfix wie "Expressions." zu vermeiden.
Auch ist es hier natürlich gerade nicht möglich, statische Typsicherheit
zu bekommen - woher soll der Compiler wissen, dass "c" den richtigen Typ
hat und es dort ein Feld "Customer" gibt?
> (Dann allerdings zugegebenermaßen ohne meine geliebte statische
> "Typ-"Sicherheit, also mit vielen Strings als Variablennamen.)
Genau.
>> Extension methods sind nötig, um an beliebige Klassen, die bestimmte
>> Interfaces implementieren (IEnumerable<T> so weit ich weiss, was bei
>> C# auch arrays implementieren) automatisch mit der Query-Sprache
>> nachzurüsten.
>
> Wieso, die Klassen können in einer neuen API-Version doch einfach
> zusätzlich ein neues Interface implementieren, so wie Vector auf einmal
> auch List implementiert hat.
Die Lösung mit Extension Methods ist aber besser.
Erstens muss man eben nicht existierende Klassen anpacken, was ein
großer Vorteil ist, wenn man etwa die Klassen, nicht aber deren
Quelltext gekauft hat.
Zweitens funktionieren die wie Mixins und man muss sie nicht mehrfach in
Klassen implementieren.
Angenommen du hast etwas wie
class A implement I
class B implement I
und A und B haben keine gemeinsame Oberklasse (außer Object).
Wenn du jetzt das Interface I um eine Methode erweitern wollen würdest,
würden alle Implementierungen betroffen und du müsstest sowohl in A und
in B die neue Methode implementieren.
Dank der extension Method - die ja nur syntaktischer Zucker für eine
statische Funktion ist - ist das jetzt nicht nötig. Insbesondere wird
auch keine dir unbekannte Klasse C betroffen, die ebenfalls das alte I
implementiert hat.
Eine Erweiterung der Interfaces ist also ausgeschlossen.
Extension Methods sind ein guter Weg, diesem Dilemma zu entkommen.
>> Die neuen Konstruktoren, anonyme Typen und lokale Typinferenz sind
>> notwendig, um auf bequeme Weise Projektionen abzubilden.
>
> Genau meine Rede. Nur aus Bequemlichkeitsgründen.
Bequemlichkeit ist aber entscheidend, weil sie direkt in die Lesbarkeit
und damit die Wartbarkeit eingeht.
> Naja, wenn man es exzessiv nutzt (was in den meisten Anwendungen IMHO
> eher nicht der Fall sein wird) ist die Bequemlichkeit sicher ein großer
> Vorteil.
Doch, ich gehe davon aus, dass das exessiv genutzt werden wird - ich
würde es jedenfalls machen, weil ich das einfach so aus 5 Jahren
Smalltalk gewöhnt war und schon immer bei Java vermisst habe.
>> Die lambda-Funktionen sind (siehe oben) bequem, anonyme lokale
>> Funktionen zu haben, die überall in den Query benutzt werden.
>
> Hab' mich mit dem lambda-Kalkül bisher nicht befasst. Es geht doch dabei
> im Prinzip nur um sowas wie bessere "Funktionspointer" bzw.
> "Funktionsobjekte", also was man in Java etwas unhandlich über
> Interfaces und anonyme Klassen realisiert, oder?
Lambdas sind anonyme Funktionen - in OO-Sprech würde man sie wohl
Funktionsobjekte nennen. In Java wäre das sowas:
interface Func0<R> {
R call();
}
interface Func1<R, T1> {
R call(T1 t1);
}
usw.
und dann
new Func1<String, String>() {
String call(String s) {
return "Hallo, " + s;
}
}
was in C# 3.0 eben als
s => "Hallo, " + s
geschrieben werden kann. Das ist IMHO ein entscheidender Fortschritt
(jedenfalls bei Java oder C# ;).
> Lambdas sind anonyme Funktionen - in OO-Sprech würde man sie wohl
> Funktionsobjekte nennen. In Java wäre das sowas:
>
> interface Func0<R> {
> R call();
> }
> interface Func1<R, T1> {
> R call(T1 t1);
> }
> usw.
>
> und dann
>
> new Func1<String, String>() {
> String call(String s) {
> return "Hallo, " + s;
> }
> }
>
> was in C# 3.0 eben als
>
> s => "Hallo, " + s
>
> geschrieben werden kann. Das ist IMHO ein entscheidender Fortschritt
> (jedenfalls bei Java oder C# ;).
Naja, fast.
Func0 gibt es nicht, da konstant. Gut, bei Seiteneffektsprachen braucht
man es wohl doch, ist aber nur eine Abkürzung für Func1<World,
Result>.
Func2 gibt es auch nicht wirklich, da
class Fun2<A,B,C> extends Func1<A, Func1<B,C>> { ... }
Dadurch hat man dann gleich currying mit. Worauf man nicht verzichten
sollte.
Stefan Matthias Aust wrote:
> Es geht, aber dann wird es so umständlich, dass man sich die Integration
> auch sparen kann. Wenn ich etwa statt
>
> c => c.Customer == "ich"
>
> jetzt
>
> mkLambda(mkParameterList("c"),
> mkCall("==", mkAccess(mkVar("c"), "Customer"), "ich"))
Wie wäre es mit
c.iterateWhere(eq("Customer","ich"));
oder so ähnlich? IMHO hast Du es komplizierter als nötig gemacht!
> Auch ist es hier natürlich gerade nicht möglich, statische Typsicherheit
> zu bekommen - woher soll der Compiler wissen, dass "c" den richtigen Typ
> hat und es dort ein Feld "Customer" gibt?
ACK. Das ist der Hauptnachteil (wie ich jetzt, glaube ich, zum dritten
Mal zugebe! ;-)
Daher bin ich ja auch für "deine" neue Spracherweiterung, nur mit der
Syntax bin ich noch nicht richtig glücklich. Aber, wie Mr. Smith sagte:
"Ich werde mich dran gewöhnen!" ;-)
> Erstens muss man eben nicht existierende Klassen anpacken, was ein
> großer Vorteil ist, wenn man etwa die Klassen, nicht aber deren
> Quelltext gekauft hat.
Das ist IMHO kein Argument! Das ist nämlich genau das Argument, wegen
dem Projekte wuchern und nicht wachsen!
> Zweitens funktionieren die wie Mixins und man muss sie nicht mehrfach in
> Klassen implementieren.
>
> Angenommen du hast etwas wie
>
> class A implement I
> class B implement I
>
> und A und B haben keine gemeinsame Oberklasse (außer Object).
>
> Wenn du jetzt das Interface I um eine Methode erweitern wollen würdest,
> würden alle Implementierungen betroffen und du müsstest sowohl in A und
> in B die neue Methode implementieren.
>
> Dank der extension Method - die ja nur syntaktischer Zucker für eine
> statische Funktion ist - ist das jetzt nicht nötig. Insbesondere wird
> auch keine dir unbekannte Klasse C betroffen, die ebenfalls das alte I
> implementiert hat.
>
> Eine Erweiterung der Interfaces ist also ausgeschlossen.
Da hätte man dann Abwärtskompatibilitäts-Probleme, ja.
> Extension Methods sind ein guter Weg, diesem Dilemma zu entkommen.
Aber das geht doch nur, wenn die Implementierung der Methode für alle
Klassen, die das Interface implementieren, gleich sein soll!?!
Ansonsten hat man die Abwärstkompatibilitäts-Probleme sowieso!
>> Naja, wenn man es exzessiv nutzt (was in den meisten Anwendungen IMHO
>> eher nicht der Fall sein wird) ist die Bequemlichkeit sicher ein
>> großer Vorteil.
>
> Doch, ich gehe davon aus, dass das exessiv genutzt werden wird - ich
> würde es jedenfalls machen, weil ich das einfach so aus 5 Jahren
> Smalltalk gewöhnt war und schon immer bei Java vermisst habe.
Böse Zungen würden jeztzt sagen "Du musst dich ganz auf eine Sprache
einlassen und in ihr denken, und nicht versuchen, Sprachkonstrukte von
einer Sprache einfach in die andere zu übertragen" ;-)
Aber ich verstehe deinen Punkt.
>>> Die lambda-Funktionen sind (siehe oben) bequem, anonyme lokale
>>> Funktionen zu haben, die überall in den Query benutzt werden.
>>
>> Hab' mich mit dem lambda-Kalkül bisher nicht befasst. Es geht doch
>> dabei im Prinzip nur um sowas wie bessere "Funktionspointer" bzw.
>> "Funktionsobjekte", also was man in Java etwas unhandlich über
>> Interfaces und anonyme Klassen realisiert, oder?
>
> Lambdas sind anonyme Funktionen - in OO-Sprech würde man sie wohl
> Funktionsobjekte nennen. In Java wäre das sowas:
> ...
Also genau das, was ich verstanden hatte. Mannomann, warum muss man
allem so komplizierte Namen geben wie "Lambda-Kalkül", anstatt einfach
die Bezeichnung "Funktions-Objekt" beizubehalten.
Natürlich ist die Syntax, die Du beschrieben hast, ein großer
Fortschritt, aber vom Kern her ist es doch dasselbe!
Ciao,
Ingo
> Dies sollte nur beschreiben, was man IMHO eine Sprache und einen
> Compiler bauen könnte, deren Syntax erweiterbar ist ...
Ich fände es aber entscheidend, dass sich diese neue Syntax dann
harmonisch einfügt und nicht wie ein Fremdkörper heraussticht. Ganz
schlechtes Beispiel ist etwa, wie HTML (oder andere XML-Sprache wie etwa
SVG) und JavaScript zuammengepfercht werden. Positives Beispiel ist da
dann etwas wie Lisp, wo die S-Expressions so generisch sind, dass sie
immer passen oder REBOL, was recht trickreich mit seiner Blocksyntax
umgeht, um dadurch neue Subsprachen einzubetten.
Wenn man aber eine Sprache wie Java oder C# hat, die schon durch eine
reichliche, teilweise irruläre Syntax "gesegnet" ist, dann ist es IMHO
das kleinere Übel, dort jetzt noch ein halbes Dutzend Änderungen
anzubringen, die alle für sich genommen Vorteile bieten und dann im
Zusammenspiel eine nett aussehende Query-Sprache erlauben.
> Meine Kritik war aber auch, dass man die Syntax nicht beliebig gross
> werden lassen soll!
Das kann ich verstehen, aber gerade dein Ansatz mit beliebigen neuen
Sprachen innerhalb von { } macht doch die Syntax beliebig groß - die
Möglichkeiten sind da unendlich. Da finde ich es für Java und C# schon
besser, wenn die Sprachdesigner vorsichtig ein halbes Dutzend kleine
Änderungen planen und dann ist die Syntax (bis zur nächsten
Sprachversion) wieder konstant.
>> Ich könnte mir vorstellen, du denkst hier nur an SQL. Doch LINQ
>> (language integrated queries) geht wesentlich weiter. Es ist ein
>> generisches Rahmenwerk für Anfragen für Objekte, XML, SQL, was auch
>> immer. Es bringt ein set neuer Verben in die Sprache, die IMHO längst
>> überfällig sind.
>
> Das war mir egal. Mein Vorschlag zur erweiterung von Sprachen um
> zusätzliche Syntax bezieht das IMHO mit ein. Sieh doch die LINQ als eine
> Bibliothek inklusive Compilererweiterung.
Aber nur in der Form
s = { ...hier passiert geschieht ein Wunder... };
will sagen, du hast nicht in der selben Form spezifiziert, was Ausdrücke
in { } genau bedeuten, so wie das die Spracherweiterung von C# für ihre
neue Syntax macht.
Ich glaube, wir reden aneinander vorbei. Ich begrüße die Änderungen an
der Sprache C# insgesamt, weil alle neuen Features von mir als Vorteil
geschätzt werden. Das man damit dann eine nette Query-Sprache bauen
kann, ist quasi ein super Bonus, aber nur ein Beispiel für die
Mächtigkeit der neuen Konstrukte.
Ich habe dich so verstanden, dass dir die Spracherweiterungen egal sind
und du nur nach einer Lösung suchst, wie man SQL (aus JDBC) enger in die
Sprache Java bringen kann.
Die {...} Syntax versagt aber IMHO etwa, wenn es um das Einführen von
lambda-Funktionen geht. Oder um lokale Typinferrenz. Damit ist sie
wesentlich begrenzer in ihren Möglichkeiten, als das, was als Grundlage
für LINQ kommt.
> Es ging hier _nicht_ draum, eine Alternative für LINQ aufzuzeigen,
> sondern eine Alternative der Integration in eine Sprache.
Okay, aber ohne Erweiterungen an Java (Stichwort: Typinferrenz, lokale
Funktionen) wirst du nicht so viel reißen können.
> Iss mir wie gesagt wurscht. Mein Konzept deckt das IMHO mit ab - hält
> aber die Syntax aus der eigentlich Sprache raus.
Dieser Satz ist für mich komisch. Syntax ist doch die Sprache. Wie
kannst du die da raushalten? Du meinst, du möchtest, dass deine neue
Subsprache (etwa für SQL) aus der Syntax von Java heraussticht. Okay.
Ich möchte das genaue Gegenteil. Die Syntax soll einheitlich sein, es
soll keine Grenze geben.
> customers.Where(c => c.city == myCity) stellt aber keine Erweiterung der
> Syntax dar.
Aber natürlich. Das "=>" zum Bau von lambda-Funktionen ist die
entscheidenste Änderung an C# überhaupt und damit eine gewichtige
Erweiterung der Syntax.
> Wenn ich (c => c.city == myCity) als anonyme Funktion sehe,
> dann stellt das einen ganz normalen Methodenaufruf dar.
Natürlich. Darum geht es doch. Aber das "wenn" ist, worüber ich die
ganze Zeit rede. Funktionen gleichberechtigt in die erste Klasse zu
heben und damit z.B. den Weg frei zu machen für Funktionen höherer
Ordnung ist die entscheidene Änderung an der Sprache.
Das man sie dann dann überall - etwa in Methodenaufrufen - benutzen
kann, ist eine Konsequenz, die ich für so selbstverständlich gehalten
habe, dass ich das gar nicht groß erwähnt habe.
> Wenn hier soetwas stattfinden würde, wie eine analyse des ganzen
> Ausdrucks, dann wäre das schon super.
Genau das findet statt - aufgrund der anderen wichtigen Erweiterung: Das
man sich den AST einer lokalen Funktion holen kann, etwa um ihn zu
analysieren und (nur zum Beispiel) in SQL zu transformieren.
>> Da ist bei Java und insbesondere C# das Kind eh schon in den Brunnen
>> gefallen - deren Syntax ist nicht mehr klein :)
>
> Das kann natürlich sein. IMHO ist das Kind noch nicht tief genug im
> Brunnen, denn etliche nützliche Konstrukte für den täglichen Bedarf fehlen!
Zum Beispiel?
> Ich will aber beides: vieles sehr leicht erledigen können, aber eine
> simple Syntax.
Dann bist du IMHO sowohl bei C# und bei Java falsch. Für mich gewinnt
da immer noch Smalltalk (auch wenn viele sagen, sie verstehen die Syntax
nicht) oder aber eine funktionale Sprache a la ML oder natürlich ein
Lisp-Dialekt.
> Wer von uns findet z.B. synchronized{} nicht ziemlicht super? Es ist
> einfach sich einen Lock zu holen, dieser wird automatisch wieder
> zurückgegeben usw.
> Es ist aber IMGO Quatsch, das in die Syntax zu integrieren. Vielmehr
> würde man erwarten, dass dieses Konstrukt von der Bibliothek kommt, die
> die Threads zur Verfügung stellt.
Der Vorteil eines Schlüsselworts ist etwa, das man das in einer IDE
jetzt darstellen kann - Eclipse markiert etwa Methoden, die synchronized
sind (allerdings nur auf Metodenebene nicht als Block) im outline. Ich
glaube, die erste Überlegung war, synchronized auf Methodenebene reicht
und da sticht das Schlüsselwort prima ins Auge und hat
dokumentatorischen Charakter. Dann stellte man fest, dann man die
Bereiche gerne auch mal kleiner machen will und erfand das
synchronized() als Block. Weil die Sprache aber keine Blöcke als Syntax
kennt, und man definitiv kein explizites Lock/unLock haben will, wo man
zu leicht eine Hälfte vergessen kann, hat man noch mehr Syntax erfunden.
Besser wäre IMHO gewesen, lieber Blöcke als Basissyntax in die Sprache
aufzunehmen und wir hätten 80% weniger Probleme.
Die nicht-Existenz von Blöcken haben wir aber wohl genauso wie die
primitiven Datentypen der Zielrichtung, eine reine Interpreter-Sprache
für Settop-Boxen zu sein und Befürchtungen, dass wäre sonst nicht
schnell genug auf der damaligen Hardware zu verdanken.
>> mkLambda(mkParameterList("c"),
>> mkCall("==", mkAccess(mkVar("c"), "Customer"), "ich"))
>
> Wie wäre es mit
>
> c.iterateWhere(eq("Customer","ich"));
>
> oder so ähnlich? IMHO hast Du es komplizierter als nötig gemacht!
IMHO nicht :) Wo ist denn bei dir die Laufvariable "c" abgeblieben?
Weiss das alles die Funktion "eq"? Das scheint mir jetzt einfach nur
eine Verlagerung des Problems. Ich bin davon ausgegangen, dass du den
kompletten AST der lokalen Funktion aufbauen musst, damit das, was auch
immer "Where" damit vorhat (interpretieren oder analysieren oder
compileren) noch möglich ist.
> Daher bin ich ja auch für "deine" neue Spracherweiterung, nur mit der
> Syntax bin ich noch nicht richtig glücklich. Aber, wie Mr. Smith sagte:
> "Ich werde mich dran gewöhnen!" ;-)
Muss du ja gar nicht, es sei denn, du willst C# machen - ob das bei Java
kommt, wage ich sehr zu beweifeln :)
>> Erstens muss man eben nicht existierende Klassen anpacken, was ein
>> großer Vorteil ist, wenn man etwa die Klassen, nicht aber deren
>> Quelltext gekauft hat.
>
> Das ist IMHO kein Argument! Das ist nämlich genau das Argument, wegen
> dem Projekte wuchern und nicht wachsen!
Das ist ein sehr gewichtiges Argument. Die Entwickler wären
(verständlicherweise) sehr böse, wenn Microsoft sie durch Änderungen an
Interfaces dazu zwingen würden, ihren Code umzuschreiben.
Das Trennen von Klassen und Methden ist IMHO eh die bessere Lösung
(leider macht weder Java noch C# das) und führt zu leichter
erweiterbarem Code.
>> Eine Erweiterung der Interfaces ist also ausgeschlossen.
>
> Da hätte man dann Abwärtskompatibilitäts-Probleme, ja.
Eben.
>> Extension Methods sind ein guter Weg, diesem Dilemma zu entkommen.
>
> Aber das geht doch nur, wenn die Implementierung der Methode für alle
> Klassen, die das Interface implementieren, gleich sein soll!?!
Wieso? Das Zauberwort heißt Polymorphismus.
>> Doch, ich gehe davon aus, dass das exessiv genutzt werden wird - ich
>> würde es jedenfalls machen, weil ich das einfach so aus 5 Jahren
>> Smalltalk gewöhnt war und schon immer bei Java vermisst habe.
>
> Böse Zungen würden jeztzt sagen "Du musst dich ganz auf eine Sprache
> einlassen und in ihr denken, und nicht versuchen, Sprachkonstrukte von
> einer Sprache einfach in die andere zu übertragen" ;-)
Hehe.
Das versuche ich ja, aber träumen würd' man ja noch dürfen...
> Aber ich verstehe deinen Punkt.
>> Lambdas sind anonyme Funktionen - in OO-Sprech würde man sie wohl
>> Funktionsobjekte nennen. In Java wäre das sowas:
>> ...
>
> Also genau das, was ich verstanden hatte. Mannomann, warum muss man
> allem so komplizierte Namen geben wie "Lambda-Kalkül", anstatt einfach
> die Bezeichnung "Funktions-Objekt" beizubehalten.
Weil nun mal Lambda-Kalkül eher da war und jeder, der in der Materie
verwurzelt ist, diesen Begriff kennt und jetzt von neuen Begriffen
verwirrt wäre.
Das ist ungefähr das gleiche Argument, warum es car und cdr bei Lisp
gibt, auch wenn die Namen eher ein historischer Witz sind als irgendwie
mit ihrer Semantik verbunden oder das es bei Unix "ls" heißt, wenn man
seine Dateien auflisten möchte. Oder das Java so aussieht wie C - alles
Gewöhnung.
> Natürlich ist die Syntax, die Du beschrieben hast, ein großer
> Fortschritt, aber vom Kern her ist es doch dasselbe!
Das ist es immer und das ist auch die Aussage des Lambda-Kalküls - alles
kann mit lambda-Funktionen ausgedrückt werden - wirklich alles.
> Also genau das, was ich verstanden hatte. Mannomann, warum muss man
> allem so komplizierte Namen geben wie "Lambda-Kalkül", anstatt einfach
> die Bezeichnung "Funktions-Objekt" beizubehalten.
Nein, furchtbar. Es bringt nur Verwirrung, wenn man an alles und jedes
den Suffix "-objekt" anhängt. Funktions-Objekt, Methoden-Objekt,
Objektmethodenobjekt, class Object Objekt.
Also nochmal: Das Lambda-Kalkül ist ein Modell, ähnlich wie die
Turing-Maschine oder die van-Neumann-Architektur. Man könnte zu allen
dreien Kalkül oder Paradigma oder was auch immer sagen.)
Das Lambda-Kalkül ist absolut simpel, es kennt nur anonyme Funktionen,
die außerdem genau ein Argument haben (sog. lambda-Funktionen) sowie
function application, d.h. die "Anwendung" der Funktion auf ein
Argument (das natürlich ebenfalls eine Funktion ist).
Für moderne Programmierung von Bedeutung sind anonyme Funtionen, die
natürlich ebenfalls auf "normale" Datenobjekte anwendbar sein müssen.
Aber eben auch als Parameter verwendet werden können.
Ob jetzt intern Objekte durch lamda-Funktionen dargestellt werden oder
anonyme Funktionen durch Objekte einer Lambda-Klasse ist eher
nebensächlich.
Stefan Matthias Aust wrote:
>> Wie wäre es mit
>>
>> c.iterateWhere(eq("Customer","ich"));
>>
>> oder so ähnlich? IMHO hast Du es komplizierter als nötig gemacht!
>
> IMHO nicht :) Wo ist denn bei dir die Laufvariable "c" abgeblieben?
> Weiss das alles die Funktion "eq"? Das scheint mir jetzt einfach nur
> eine Verlagerung des Problems.
iterateWhere kennt doch c (als this). Und das reicht. Ich hätte auch
c.iterateWhere("eq","Customer","ich")
schreiben können, dann wäre dieser Aspekt offensichtlicher. (Aber ich
wollte zumindest eq als "Konstrukt" haben und nicht als String.)
Anyway, gehen tut das alles. Muss ich wirklich Pseudo-Source-Schnipsel
posten, um das zu beweisen?
>> Daher bin ich ja auch für "deine" neue Spracherweiterung, nur mit der
>> Syntax bin ich noch nicht richtig glücklich. Aber, wie Mr. Smith
>> sagte: "Ich werde mich dran gewöhnen!" ;-)
>
> Muss du ja gar nicht, es sei denn, du willst C# machen - ob das bei Java
> kommt, wage ich sehr zu beweifeln :)
Och, ich denke schon. Beinahe ein Jahrzehnt lang wurden generics von den
Programmierern gefordert, und sun hats einfach ignoriert. Aber seit es
C# gibt ist sun da deutlich innovationsfreundlicher.
>>> Erstens muss man eben nicht existierende Klassen anpacken, was ein
>>> großer Vorteil ist, wenn man etwa die Klassen, nicht aber deren
>>> Quelltext gekauft hat.
>>
>> Das ist IMHO kein Argument! Das ist nämlich genau das Argument, wegen
>> dem Projekte wuchern und nicht wachsen!
>
> Das ist ein sehr gewichtiges Argument. Die Entwickler wären
> (verständlicherweise) sehr böse, wenn Microsoft sie durch Änderungen an
> Interfaces dazu zwingen würden, ihren Code umzuschreiben.
Dafür gibts ja deprecated: Man kann natürlich keine Methode in ein
Interface hinzufügen. Aber man kann ein neues, quasi identisches
Interface anbieten, was zusätzlich diese Methode anbietet, und das alte
Interface deprecaten.
IMHO sollte man dann aber nach 5-10 Jahren tatsächlich auch
deprecated-Methoden und Elemente aus der API entfernen, damit es
übersichtlich bleibt.
>>> Extension Methods sind ein guter Weg, diesem Dilemma zu entkommen.
>>
>> Aber das geht doch nur, wenn die Implementierung der Methode für alle
>> Klassen, die das Interface implementieren, gleich sein soll!?!
>
> Wieso? Das Zauberwort heißt Polymorphismus.
-v, bitte. Wie schaffst Du es mit "Extension Methods" und
"Polymorphismus" das genannte Problem (*Dein* Interface, welches *ich*
implementiere, soll eine zusätzliche Methode bekommen) zu lösen?
>>> Lambdas sind anonyme Funktionen - in OO-Sprech würde man sie wohl
>>> Funktionsobjekte nennen. In Java wäre das sowas:
>>> ...
>>
>> Also genau das, was ich verstanden hatte. Mannomann, warum muss man
>> allem so komplizierte Namen geben wie "Lambda-Kalkül", anstatt einfach
>> die Bezeichnung "Funktions-Objekt" beizubehalten.
>
> Weil nun mal Lambda-Kalkül eher da war und jeder, der in der Materie
> verwurzelt ist, diesen Begriff kennt und jetzt von neuen Begriffen
> verwirrt wäre.
Ah, OK, also tatsächlich dasselbe "Problem", welches man bei OO öfter
antrifft, nämlich dass zwei relativ verschiedene Forschungs-Zweige auf
einmal zusammengewachsen sind. (Für jemanden (nicht ich ;-), der von
Assembler über C zu C++ und Java gekommen ist, ist zunächst nicht klar,
warum structs auf einmal classes und Funktionen auf einmal Methoden
heissen müssen, etc...)
Ciao,
Ingo
> Neben der Möglichkeit, Funktionen als erstklassige Werte
> angeben zu können, sind Lambdas in strikten Sprachen auch
> noch wichtig, um Auswertungen "verzögern" zu können, wie
> der Einsatz von "closures" oder Blöcken in Smalltalk zeigt.
Nunja, Funktionen als erstklassige Werte könnte es auch ohne anonyme
Funktionen (oder Funktionsliterale, wie Du es richtig nennst) geben.
Wenn das einmal geht, möchte man natürlich erst recht anonyme
Funktionen, um die Macht der closures ausnutzen zu können.
Das tragische an Java ist ja, daß dies alles möglich ist, aber halt
umständlich zu machen, so etwa wie oo-Programmierung in C.
Und das, was umständlich zu machen ist, wird halt dann auch nicht
gemacht. Stattdessen preßt man das Problem so gut es geht in das
OO-Prokrustesbett. So wie ein Perl-Programmierer schaut, ob sich das
Problem nicht auf Maniupulation von Strings, Hashes und Arrays
reduzieren läßt. Es geht natürlich immer. Und bestärkt dadurch noch
einmal das alleinseligmachende Paradigma.
> Hier noch einmal einige Makros, mit denen ich gerade
> experimentiere:
[...]
>
> $define FUN new java.util.concurrent.Callable<$1>(){ public $1 call(){ $1 value; return value; }}
Kannst du dafür mal ein Anwendungsbeispiel zeigen?
Es kommt mir so vor, als ob das nicht vollständig wäre.
Paul
--
Die Homepage von de.comp.lang.java: http://www.dclj.de
Pauls Package-Sammlung: http://purl.org/NET/ePaul/#pps
>>> c.iterateWhere(eq("Customer","ich"));
> c.iterateWhere("eq","Customer","ich")
Ich finde das zu kurz geschossen. Was, wenn du etwas wie
int x = 0;
customers.where(c => foo(c) != bar(x++))
umsetzen willst?
> Anyway, gehen tut das alles. Muss ich wirklich Pseudo-Source-Schnipsel
> posten, um das zu beweisen?
Ja :)
>> Muss du ja gar nicht, es sei denn, du willst C# machen - ob das bei
>> Java kommt, wage ich sehr zu beweifeln :)
>
> Och, ich denke schon. Beinahe ein Jahrzehnt lang wurden generics von den
> Programmierern gefordert, und sun hats einfach ignoriert. Aber seit es
> C# gibt ist sun da deutlich innovationsfreundlicher.
Der Bug-Report, Generics hinzuzufügen ist von Juli 97. JSR 14 ist von
Mai 99. Generics gab es für Java ja seit 1998 oder so im Rahmen von
Generic Java und Pizza. Fast das selbe Typsystem wurde übrigens 93 von
Bracha und Griswold für Smalltalk (genannt Strongtalk) vorgestellt.
Generische Typen sind IMHO kein Zeichen dafür, das Sun auf Microsoft
reagiert hat. Allerdings das schnelle Einfügen einer "foreach"-Schleife
in Java war IMHO ein direktes Reagieren auf C# und das ging in der Tat
schnell.
>> Das ist ein sehr gewichtiges Argument. Die Entwickler wären
>> (verständlicherweise) sehr böse, wenn Microsoft sie durch Änderungen
>> an Interfaces dazu zwingen würden, ihren Code umzuschreiben.
>
> Dafür gibts ja deprecated: Man kann natürlich keine Methode in ein
> Interface hinzufügen. Aber man kann ein neues, quasi identisches
> Interface anbieten, was zusätzlich diese Methode anbietet, und das alte
> Interface deprecaten.
Das ist aber ein ganz schlechtes Konzept. Vergleiche die nummerierten
Interfaces bei Eclipse. Ich glaube, bei einigen sind sie jetzt bei der
sechsten Version. Das führt zu häßlichem gecaste auf beiden Seiten des
APIs.
>>> Aber das geht doch nur, wenn die Implementierung der Methode für alle
>>> Klassen, die das Interface implementieren, gleich sein soll!?!
>>
>> Wieso? Das Zauberwort heißt Polymorphismus.
>
> -v, bitte. Wie schaffst Du es mit "Extension Methods" und
> "Polymorphismus" das genannte Problem (*Dein* Interface, welches *ich*
> implementiere, soll eine zusätzliche Methode bekommen) zu lösen?
interface I {}
class A implements I {}
class B implements I {}
static class Extensions {
static void foo(this A a) { ... }
static void foo(this B b) { ... }
}
Leider funktioniert aufgrund der rein statischen Auflösung nicht
I i = ...;
i.foo();
aber zumindest sieht es so aus, dass new A().foo() und new B().foo() gehen.
> einmal zusammengewachsen sind. (Für jemanden (nicht ich ;-), der von
> Assembler über C zu C++ und Java gekommen ist, ist zunächst nicht klar,
> warum structs auf einmal classes und Funktionen auf einmal Methoden
> heissen müssen, etc...)
Es hilft, IMHO sich den konzepten nicht über die (eine mögliche)
Implementierung zu nähern. Klassen sind nicht immer nur statische
Struts. Methoden sind ein Konzept, was beschreibt, wie sich Objekte
verhalten sollen. Das sind nicht zwangsläufig klassische Funktionen.
Stefan Matthias Aust wrote:
>>>> c.iterateWhere(eq("Customer","ich"));
>> c.iterateWhere("eq","Customer","ich")
>
> Ich finde das zu kurz geschossen. Was, wenn du etwas wie
>
> int x = 0;
> customers.where(c => foo(c) != bar(x++))
>
> umsetzen willst?
Da wäre wohl etwas wie
c.iterateWhere(Eq(Foo(c),Bar(Inc(x)))
oder so ähnlich nötig. Das wird zwar etwas unleserlich, allerdings halte
ich dein Beispiel auch für arg konstruiert!
>> Anyway, gehen tut das alles. Muss ich wirklich Pseudo-Source-Schnipsel
>> posten, um das zu beweisen?
>
> Ja :)
Gut, zu welchem (Teil-)Problem konkret? (Eine komplette API programmiere
ich dir jetzt nämlich nicht! ;-)
>>> Muss du ja gar nicht, es sei denn, du willst C# machen - ob das bei
>>> Java kommt, wage ich sehr zu beweifeln :)
>>
>> Och, ich denke schon. Beinahe ein Jahrzehnt lang wurden generics von
>> den Programmierern gefordert, und sun hats einfach ignoriert. Aber
>> seit es C# gibt ist sun da deutlich innovationsfreundlicher.
>
> Der Bug-Report, Generics hinzuzufügen ist von Juli 97. JSR 14 ist von
> Mai 99.
2 Jahre allein vom "Bugreport" bzw. "Feature-Request" und dann nochmal 7
oder 8 Jahre bis zur offiziellen Implementierung, das ist in der IT
schon eine Ewigkeit!
> Generics gab es für Java ja seit 1998 oder so im Rahmen von
> Generic Java und Pizza.
Proprietäre Erweiterungen gibt's zu allem. Das sagt IMHO nicht viel aus.
> Generische Typen sind IMHO kein Zeichen dafür, das Sun auf Microsoft
> reagiert hat.
*Dass* sie reagiert haben, vielleicht nicht. Aber dass sie *zu dem
Zeitpunkt plötzlich* reagiert haben, dafür ist es IMHO schon ein Zeichen.
>> Dafür gibts ja deprecated: Man kann natürlich keine Methode in ein
>> Interface hinzufügen. Aber man kann ein neues, quasi identisches
>> Interface anbieten, was zusätzlich diese Methode anbietet, und das
>> alte Interface deprecaten.
>
> Das ist aber ein ganz schlechtes Konzept. Vergleiche die nummerierten
> Interfaces bei Eclipse. Ich glaube, bei einigen sind sie jetzt bei der
> sechsten Version.
Ich schließe mich da voll Stefan Ram an: Es ist das kleinste Übel und
damit der richtige Weg.
> Das führt zu häßlichem gecaste auf beiden Seiten des APIs.
Kannst Du ein kurzes Beispiel bringen? IMHO sollte man das Problem
umgehen können.
>> -v, bitte. Wie schaffst Du es mit "Extension Methods" und
>> "Polymorphismus" das genannte Problem (*Dein* Interface, welches *ich*
>> implementiere, soll eine zusätzliche Methode bekommen) zu lösen?
>
> interface I {}
> class A implements I {}
> class B implements I {}
>
> static class Extensions {
> static void foo(this A a) { ... }
> static void foo(this B b) { ... }
> }
Dazu musst Du aber meine Interna kennen, und das ist nicht viel besser,
als wenn Du direkt meine Sourcen manipulierst. IMHO ist also dadurch
nicht viel gewonnen.
>> einmal zusammengewachsen sind. (Für jemanden (nicht ich ;-), der von
>> Assembler über C zu C++ und Java gekommen ist, ist zunächst nicht
>> klar, warum structs auf einmal classes und Funktionen auf einmal
>> Methoden heissen müssen, etc...)
>
> Es hilft, IMHO sich den konzepten nicht über die (eine mögliche)
> Implementierung zu nähern. Klassen sind nicht immer nur statische
> Struts. Methoden sind ein Konzept, was beschreibt, wie sich Objekte
> verhalten sollen. Das sind nicht zwangsläufig klassische Funktionen.
Es war mehr eine Feststellung von mir, nicht direkt eine Kritik.
Ciao,
Ingo