Andere Programmiersprachen

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
BlackJack

Als Alternative zu SmallTalk könnte man sich auch Io anschauen, was ebenfalls "nur" aus Objekten und Nachrichten besteht. Auf der Seite gibt's einen Vergleich der "Einfachheit" des Sprachkerns anhand der Anzahl der Schlüsselworte. Da kommt Python mit 28 Schlüsselworten an Platz 4. Platz 1 ist Io mit 0, gefolgt von SmallTalk mit 5 Schlüsselworten. Zwischen SmallTalk und Python ist noch Lua.

Io ist allerdings noch sehr jung und noch in Bewegung, da kann sich auch mal die API von grundlegenden Objekten ändern.

@sma: Was fehlt Python denn zur eigentlichen Objektidee von Kay? Ich dachte immer den üblichen Verdächtigen fehlt nur die Möglichkeit auch auf Nachrichten zu reagieren, für die kein "Slot" vorgesehen ist. Das kann Python ja mit `__getattr__()` und `__setattr__()`. Oder müssen die Nachrichten zwingend asynchron sein? Sind sie dass denn automatisch bei SmallTalk? Ich kenne das nur von Rexx.
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

BlackJack hat geschrieben:@sma: Was fehlt Python denn zur eigentlichen Objektidee von Kay? Ich dachte immer den üblichen Verdächtigen fehlt nur die Möglichkeit auch auf Nachrichten zu reagieren, für die kein "Slot" vorgesehen ist. Das kann Python ja mit `__getattr__()` und `__setattr__()`.
Ja, das dachte ich mir eben auch, dass es in Python ja auch so eine Art "Nachrichtenübertragung" gibt in die man mit den magischen Methoden eingreifen kann (und auch mit Properties). Allerdings muss ich sagen, dass ich in Smalltalk nichts gemacht habe, weil mich die Implementierungen immer abgeschreckt haben.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Io ist eine nette kleine Sprache, war die letzten Jahre aber doch sehr im Fluss. Und ob man die Einfachheit nun wirklich nur an der Anzahl der Schlüsselworte zählen kann?

Bei Smalltalk und Io gibt es wie bei Scheme das Streben nach einer uniformen Syntax. Insbesondere lassen sich dadurch Kontrollstrukturen (Schleifen, bedingte Anweisungen) in der Sprache ausdrücken und auch erweitern. Das ist ein mächtiges Feature, doch es macht die Sprache nicht unbedingt einfacher. Die (operationelle) Semantik von Io lässt sich nicht so einfach beschreiben, man muss sie aber kennen, um das Programm zu verstehen. Ich finde sie - aber das ist wahrscheinlich Gewohnheit, bei Smalltalk und Scheme einfacher.

Am einfachsten ist vielleicht Scheme: Ein Atom ist ein Ausdruck ohne Klammern, z.B. eine Zahl, ein String oder ein Symbol. Bis auf Symbole, die als Variablen aufgefasst werden und durch durch den an sie gebundenen Wert ersetzt werden, sind die anderen Atome direkt Werte. In Listen der Art (e0 e1 e2 ... eN) werden die Unterausdrücke e0 bis eN rekursiv ausgewertet. Das Ergebnis von e0 muss ein Funktionswert sein, der dann mit den anderen Werten als Argumente aufgerufen wird.

Ausnahme für diese Regel sind bestimmte Symbole für e0, etwa "if" oder "quote" oder "lambda". Hier passiert trotz gleicher Syntax wie bei einem Funktionsaufruf etwa anderes.

Außerdem muss man bei Scheme noch wissen, wie die Gültigkeitsbereiche von Variablen sind und dass Endrekursion den Stack nicht wachsen lässt, damit man Schleifen mittels rekursiver Funktionen effizient implementieren kann. Das war's dann aber auch schon.

Bei Smalltalk ist es ähnlich. Es gibt Literale (Zahlen, Strings, Symbole), die zu sich selbst ausgewertet werden. Es gibt drei Arten von Nachrichtenausdrücken, unäre, wo dem Objekt ein Nachrichtenname ohne Argument folgt, binäre, wo dem Namen ein Argument folgt und Schlüsselwortnachrichten, wo sich der Name der Nachricht aus Schlüsselworten zusammensetzt, denen jeweils ein Argument folgt.

Code: Alles auswählen

anArray size
3 + 4
anArray at: 1 put: #cool
Das Prinzip ist immer Objekt-Nachricht. Alles ist ein Objekt und versteht Nachrichten, weil jedes Objekt ein Exemplar einer Klasse ist (auch Klassen sind Objekte, nämlich Exemplare von Metaklassen) und Nachrichten lösen die Ausführung von über die Klassenhierarchie zu suchenden und bei den Klassen gespeichertern Methoden aus. Ergebnis ist immer ein Objekt. Ein "super send" ist noch etwas spezieller als die normale Methodensuche, aber wenn man diese beiden Konzepte, Zuweisungen mittels ":=" und "^" als return-Anweisung verstanden hat, kennt man alles.

In Smalltalk muss man nicht lernen, dass "if" wie in Scheme etwas besonderes ist, sondern Kontrollstrukturen werden (jedenfalls augenscheinlich - die Maschine optimiert das in der Regel) direkt in Smalltalk realisiert:

Code: Alles auswählen

a < 10 ifTrue: [stmt1...] ifFalse: [stmt2...]
Dem Objekt in der Variable "a" (sagen wir, eine 5) wird die binäre Nachricht "<" mit dem Argument 10 geschickt. Ergebnis ist das Objekt true (Exemplar der Klasse True) oder false (Exemplar der Klasse False) - in meinem Fall natürlich true - und dem Objekt wird die Nachricht ifTrue:ifFalse: (ja, das ist eine Nachricht!) mit zwei Codeblöcken als Argumente geschickt. Hier sind die Definitionen:

Code: Alles auswählen

True ifTrue: aBlock ifFalse: anotherBlock
  ^aBlock value

False ifTrue: aBlock ifFalse: anotherBlock
  ^anotherBlock value
Auch der Block in [...] ist ein Objekt und versteht die Nachricht "value", wenn er sich auswerten soll.

Die Syntax von Io versucht noch uniformer zu sein als die von Smalltalk, da es nur eine Art von Nachrichtenausdruck gibt - ganz traditionell folgt dem Namen die Liste der Argumente in Klammern. Ohne Argumente kann man die Klammern auch weglassen. Außerdem sind auch Zuweisungen Nachrichten, ein Konzept, was Smalltalk wieder fallen gelassen hat.

Im Gegensatz zu Scheme, wo ("call by value") alle Argumente ausgewertet werden, bevor eine Funktion aufgerufen wird, funktioniert Io hier anders: Nur die ersten N Ausdrücke an Argument-Position werden ausgewertet, beim Rest wird ("call by name") die Nachricht selbst übergeben. Im Prinzip hat man dadurch wieder die Special-Forms von Scheme, kann sie aber meist in Io definieren, weil man über die pseudo-Variable "call" immer an den aktuellen Kontext herankommt (Smalltalk nennt dies "thisContext", eine Pseudovariable, die man dort fast nie braucht und die auch nicht alle Smalltalk-Dialekte zur Verfügung stellen). Die "=" und ":=" werden zudem als Makros aufgefasst und e1 symbol := e2 wird durch e1 setSlot("symbol", e2) und bei "=" durch updateSlot ersetzt. Das e1 kann fehlen, dann wird implizit das aktuelle "locals"-Objekt als Empfänger benutzt. Tatsächlich sagt die Doku, dass auch e symbol als Makro für e getSlot("symbol") aufzufassen ist, aber da beißt sich die Katze in den Schwanz, denn bitte was ist dann "e getSlot"?

Ich glaube, so könnte man das if von Io nachbauen (ist der selbe Trick wie bei Smalltalk, nur das ich ein spezielles "SkipFalse"-Objekt benutze statt einer einzelnen ifTrueIfFalse-Methode):

Code: Alles auswählen

if(a < 10, stmt1..., stmt2...)

if := method(cond,
  cond ifTrue(call evalArgAt(1)) ifFalse(call evalArgAt(2))
)
true ifTrue := method(SkipFalse clone value := call evalArgAt(0))
true ifFalse := method(false)
false ifFalse := method(call evalArg(0))
SkipFalse = object clone
SkipFalse ifFalse := method(self value)
Jetzt habe ich mich aber um eine Antwort gedrückt, warum Python nicht wirklich objektorientiert ist. Ich glaube, ich war zu pauschal in meiner Aussage. Man kann mit Python objektorientiert programmieren, doch Python ist vom Kern eine imperative Programmiersprache mit Anleihen aus der objektorientierten und funktionalen Programmierung. Vielleicht macht das den Erfolg aus (ich glaube ja, Multiparadigmen-Sprachen sind die mächtigsten, allerdings braucht es Entwickler die alle diese Paradigmen kennen) aber Python folgt nicht konsequent dieser Idee. Das print-Statement ist kein Objekt. Klassen wirken nachträglich aufgesetzt und die Implementierung von Methoden als Funktionen scheint komplett durch. Daher folgen die wenigsten Python-Programmierer und Python-Programme der ursprünglichen Idee sondern sind ein Konglomerat aus Konzepten. Gut für die Praxis, weniger gut für's Lernen.

Ansonsten hast du Recht, auf unbekannte Nachrichten reagieren zu können ist ein mächtiges Konzept, was nur wenige Sprachen (Ruby kann's übrigens auch) beherrschen. Nachrichten bei Smalltalk sind (im Gegensatz zu Io) übrigens immer synchron. Daher sagte ich ja, Erlang trifft die ursprüngliche Idee fast noch besser...

PS: Es heißt Smalltalk, nicht SmallTalk ;)

Stefan
BlackJack

Das die Sprache keine Schlüsselworte hat, man aber trotzdem die üblichen Kontrollstrukturen hat, diese also mit dem Kern ohne Schlüsselworte implementieren kann, ist schon *ein* Kriterium was man IMHO für die Einfachheit des Sprachkerns bei gleichzeitiger Mächtigkeit, heran ziehen kann.

Auch wenn ``if``/``ifTrue:ifFalse:`` bei Smalltalk und Io im Gegensatz zu Scheme nicht als besondere Syntax behandelt wird muss man trotzdem lernen, dass es etwas besonderes ist, weil die Argumente nicht unbedingt ausgewertet werden.

Bei der Syntax von Io gibt's mehr als eine Art von Nachrichten. Operatoren werden gesondert behandelt, damit ``a + b * c`` das aus der Mathematik bekannte Ergebnis liefert und nicht stur von links nach rechts als ``(a + b) * c`` interpretiert wird.

Eine Vereinfachung der Sprache von Io gegenüber Smalltalk hast Du nicht erwähnt: in Io gibt es vom Sprachkern her keine Klassen, sondern nur Objekte. Eine Sonderstellung von Objekten als "Klassen" gibt sich nur aus der Namenskonvention die "Vorlagen" mit "CamelCase"-Namen zu versehen.

Trotzdem verstehe ich den Einwand gegen Python nicht ganz. Für mich sind OO und imperativ/funktionial orthogonale Konzepte. Weil Python eher imperativ daherkommt, während sich Io sehr funktional anfühlt, sind beide trotzdem für mich sehr objektorientierte Sprachen. ``print`` ist halt ein syntaktischer Sonderfall, der aus genau dem Grund ja auch in Python 3.0 durch eine Funktion abgelöst wird. Und dann ist ``print('hallo')`` eben die Nachricht ``print``, die an das `__builtins__`-Objekt geschickt wird, was mit einem Funktionsobjekt antwortet, dem dann wiederum an den `__call__`-Slot eine Nachricht mit dem Argument 'hallo', einem Zeichenkettenobjekt, geschickt wird. Das Funktionsobjekt schickt dann dem `sys`-Modul-Objekt die Nachricht `stdout` und bekommt ein File-Objekt als Antwort dem dann… usw. Also letztendlich nichts anderes als `writeln()` in Io. Ein Haufen Objekte, die miteinander kommunizieren.

In Io folgen die Programmierer letzendlich auch nicht den ursprünglichen Konzepten sondern programmieren mit den entsprechenden Methoden ebenfalls in einer Mischung aus imperativ oder funktional. Eben weil das orthogonal zu OO ist.

PS: ``e getSlot`` ist natürlich ``e getSlot("getSlot")``. ;-)
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

sma hat geschrieben:Das print-Statement ist kein Objekt.
Das stimmt und nicht nur dir hat das nicht gefallen, sondern auch den Machern von Python. Daher ist ``print`` in den Python 3.0 alphas bereits eine Funktion und somit ein Objekt. Und Python hat ein Schlüsselwort weniger.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

BlackJack hat geschrieben:Auch wenn ``if``/``ifTrue:ifFalse:`` bei Smalltalk und Io im Gegensatz zu Scheme nicht als besondere Syntax behandelt wird muss man trotzdem lernen, dass es etwas besonderes ist, weil die Argumente nicht unbedingt ausgewertet werden.
Oh, dann habe ich das nicht gut beschrieben. Es ist gerade nichts Besonderes. Die Blöcke [ ... ] in Smalltalk sind ein generelles Konzept und haben nichts mit der Implementierung von ifTrue:ifFalse: zu tun. Eine Fingerübung ist, sich ein eigenes case-Statement zu bauen:

Code: Alles auswählen

expr switch
  case: 1 do: [ ... ];
  case: 2 do: [ ... ];
  default: [ ... ].
Eine mögliche Implementierung:

Code: Alles auswählen

Object >> switch
  ^Switch on: self

Switch >> class on: aValue
  ^super new setValue: aValue 

Switch >> setValue: aValue
  value := aValue
  result := self

Switch >> case: aValue do: aBlock
  result == self ifTrue: [
    value = aValue ifTrue: [result := aBlock value]]
  ^result
  
Switch >> default: aBlock
  result == self ifFalse: [result := aBlock value]
  ^result
Für Io gilt das selbe in grün. Argument-Nachrichtenausdrücke werden vom Empfänger nur ausgewertet, wenn dieser es für richtig hält. Das wäre, als wenn bei Scheme alles eine Special-Form ist. Falls jemand den Scheme-Interpreter gesehen hat, den ich neulich hier gepostet hatte - der folgte genau diesem Prinzip.
BlackJack hat geschrieben:Bei der Syntax von Io gibt's mehr als eine Art von Nachrichten. Operatoren werden gesondert behandelt, damit ``a + b * c`` das aus der Mathematik bekannte Ergebnis liefert und nicht stur von links nach rechts als ``(a + b) * c`` interpretiert wird.
Das war aber nicht immer so... muss mir wohl nochmal die aktuelle Sprachbeschreibung durchlesen.
BlackJack hat geschrieben:Eine Vereinfachung der Sprache von Io gegenüber Smalltalk hast Du nicht erwähnt: in Io gibt es vom Sprachkern her keine Klassen, sondern nur Objekte.
Stimmt. Io ist hier beeinflusst von Self (wie übrigens auch JavaScript) und Self ist ein konzeptionell eingedampftes Smalltalk. Irgendwie sind klassenlose Sprachen aber nicht so beliebt, bei JavaScript definiert sich jeder erstmal Klassen hinzu und EcmaScript 4 soll sie ja auch bekommen.
BlackJack hat geschrieben:Trotzdem verstehe ich den Einwand gegen Python nicht ganz. Für mich sind OO und imperativ/funktionial orthogonale Konzepte. Weil Python eher imperativ daherkommt, während sich Io sehr funktional anfühlt, sind beide trotzdem für mich sehr objektorientierte Sprachen.
Grmpf. Ich schreibe zu viel und denke zu wenig. Ich meinte nicht imperativ, sondern prozedural. Ich stimme dir zu. Ich meinte, von der Historie her ist Python eher prozedural (prozedurorientiert) als objektorientiert.
BlackJack hat geschrieben:Und dann ist ``print('hallo')`` eben die Nachricht ``print``, die an das `__builtins__`-Objekt geschickt wird, was mit einem Funktionsobjekt antwortet, dem dann wiederum an den `__call__`-Slot eine Nachricht mit dem Argument 'hallo', einem Zeichenkettenobjekt, geschickt wird. Das Funktionsobjekt schickt dann dem `sys`-Modul-Objekt die Nachricht `stdout` und bekommt ein File-Objekt als Antwort dem dann… usw. Also letztendlich nichts anderes als `writeln()` in Io. Ein Haufen Objekte, die miteinander kommunizieren.
Ganz genau. Aber solange das nicht so erklärt wird, sondern eher mit Hilfe von Operationen und Prozedur oder Funktionsaufrufen (mir ist der Unterschied a la Pascal, wo das eine einen Wert liefert und das andere nicht egal - für mich sind Prozeduren etwas, das imperativ ist, nämlich eine Befehlsfolge beschreibt - ob die nun Seiteneffekte und ein Ergebnis hat oder nicht sei egal - selbst Scheme als durchaus eher funktionale Sprache nennt seine "Funktionen" (dieser meiner Definition folgend korrekt) procs - Prozeduren)
BlackJack hat geschrieben:PS: ``e getSlot`` ist natürlich ``e getSlot("getSlot")``. ;-)
Dir ist aber schon bewusst, dass du da jetzt immer noch sitzen würdest um das komplett aufzulösen. Es fehlt der Fixpunkt :)

Stefan
Antworten