Warum hat Ruby Creator das Konzept der Symbole gewählt?

15

tl; dr: Gibt es eine sprachunabhängige Definition von Symbolen und einen Grund, sie in anderen Sprachen zu haben?

Warum verwendete der Ruby-Schöpfer das Konzept von symbolsin der Sprache?

Ich frage dies aus der Perspektive eines Nicht-Rubin-Programmierers. Ich habe viele andere Sprachen gelernt und in keiner festgestellt, dass ich angeben muss, ob ich mit Ruby zu tun habe oder nicht symbols.

Die Hauptfrage ist, symbolsob das Konzept von in Ruby für die Aufführung existiert oder nur etwas, das aufgrund der Art und Weise, wie die Sprache geschrieben ist, benötigt wird.

Wäre ein Programm in Ruby leichter und / oder schneller als das entsprechende Programm in Python oder Javascript? Wenn ja, würde es daran liegen symbols?

Da es Rubys Absicht ist, für Menschen einfach zu lesen und zu schreiben, konnten die Entwickler den Codierungsprozess nicht vereinfachen, indem sie diese Verbesserungen im Interpreter selbst implementierten (wie es in anderen Sprachen sein könnte)?

Es sieht so aus, als würde jeder nur wissen wollen, was symbolses ist und wie man es benutzt, und nicht, warum es überhaupt da ist.

Yuri Ghensev
quelle
Scala hat Symbole auf meinem Kopf. Ich denke viele Lisps tun es.
D. Ben Knoble

Antworten:

17

Der Schöpfer von Ruby, Yukihiro "Matz" Matsumoto, hat erklärt, wie Ruby von Lisp, Smalltalk, Perl beeinflusst wurde (und Wikipedia sagt auch Ada und Eiffel):

Ruby ist eine Sprache, die in den folgenden Schritten entwickelt wurde:

  • Nehmen Sie eine einfache Lisp-Sprache (wie vor CL).
  • entfernen Sie Makros, S-Ausdruck.
  • füge einfaches Objektsystem hinzu (viel einfacher als CLOS).
  • füge Blöcke hinzu, inspiriert von Funktionen höherer Ordnung.
  • füge Methoden hinzu, die in Smalltalk gefunden wurden.
  • Funktionalität in Perl hinzufügen (auf OO-Weise).

Ruby war also ursprünglich ein Lisp.

Nennen wir es ab jetzt MatzLisp. ;-)

In jedem Compiler verwalten Sie Bezeichner für Funktionen, Variablen, benannte Blöcke, Typen usw. Normalerweise speichern Sie sie im Compiler und vergessen sie in der erstellten ausführbaren Datei, es sei denn, Sie fügen Debugging-Informationen hinzu.

In Lisp sind solche Symbole erstklassige Ressourcen, die in verschiedenen Paketen gehostet werden. Dies bedeutet, dass Sie zur Laufzeit neue Symbole hinzufügen und diese an verschiedene Arten von Objekten binden können. Dies ist nützlich bei der Metaprogrammierung, da Sie sicher sein können, dass Sie keine Namenskollisionen mit anderen Teilen des Codes haben.

Außerdem werden Symbole zum Zeitpunkt des Lesens interniert und können anhand ihrer Identität verglichen werden. Dies ist ein effizienter Weg, um neue Arten von Werten (wie Zahlen, aber abstrakt) zu erhalten. Mit dieser Hilfe können Sie Code schreiben, in dem Sie symbolische Werte direkt verwenden, anstatt Ihre eigenen Aufzählungstypen mit Ganzzahlen zu definieren. Außerdem kann jedes Symbol zusätzliche Daten enthalten. So kann Emacs / Slime beispielsweise Metadaten aus Emacs direkt in die Eigenschaftsliste eines Symbols einfügen.

Der Begriff des Symbols spielt in Lisp eine zentrale Rolle. Ausführliche Beispiele finden Sie unter PAIP (Paradigmen der Programmierung künstlicher Intelligenz: Fallstudien in Common Lisp, Norvig).

Core-Dump
quelle
5
Gute Antwort. Ich bin jedoch anderer Meinung als Matz: Ich würde niemals daran denken, eine Sprache ohne Makros als Lisp-Dialekt zu bezeichnen. Die Laufzeit-Metaprogrammierungs-Möglichkeiten von lisp sind genau das, was dieser Sprache ihre unglaubliche Kraft verleiht und ihre abgrundtief vereinfachte, unaussprechliche Grammatik ausgleicht.
cmaster
11

Warum mussten Ruby-Schöpfer das Konzept von symbolsin der Sprache verwenden?

Nun, sie mussten es nicht unbedingt, sie entschieden sich dafür. Beachten Sie auch, dass Symbols streng genommen nicht Teil der Sprache sind, sondern Teil der Kernbibliothek. Sie haben eine Literal-Syntax auf Sprachebene, aber sie funktionieren genauso gut, wenn Sie sie durch Aufrufen erstellen müssten Symbol::new.

Ich frage aus der Perspektive eines Nicht-Rubin-Programmierers, der versucht, es zu verstehen. Ich habe viele andere Sprachen gelernt und in keiner festgestellt, dass ich angeben muss, ob ich mit Ruby zu tun habe oder nicht symbols.

Sie haben nicht gesagt, was diese "vielen anderen Sprachen" sind, aber hier ist nur ein kleiner Auszug von Sprachen, die einen SymbolDatentyp wie Ruby's haben:

Es gibt auch andere Sprachen, die die Funktionen von Symbols in einer anderen Form bereitstellen . In Java sind die Funktionen von Ruby's Stringbeispielsweise in zwei (tatsächlich drei) Typen unterteilt: Stringund StringBuilder/ StringBuffer. Andererseits werden die Merkmale des Ruby- SymbolTyps in den Java- StringTyp zusammengefasst: Java Stringkann interniert werden , Literalzeichenfolgen und Strings, die das Ergebnis von zur Kompilierungszeit bewerteten konstanten Ausdrücken sind, werden automatisch interniert, dynamisch erzeugte Strings können durch Aufrufen interniert werden die String.internMethode. Ein Internierter Stringin Java ist genau wie ein Internierter in SymbolRuby, wird jedoch nicht als separater Typ implementiert, sondern ist nur ein anderer Status als ein JavaStringkann in sein. (Hinweis: In früheren Ruby-Versionen wurde diese Methode String#to_symfrüher aufgerufen String#internund ist heute noch als Legacy-Alias ​​vorhanden.)

Die Hauptfrage könnte lauten: Existiert das Konzept von symbolsin Ruby als Leistungsabsicht über sich selbst und andere Sprachen,

Symbols sind in erster Linie ein Datentyp mit spezifischer Semantik . Diese Semantik ermöglicht es auch, einige performante Operationen (z. B. schnelle O (1) -Gleichheitstests) zu implementieren, aber das ist nicht der Hauptzweck.

oder nur etwas, das aufgrund der Art und Weise, wie die Sprache geschrieben ist, existieren muss?

Symbols werden in der Ruby-Sprache überhaupt nicht benötigt, ohne sie würde Ruby problemlos funktionieren. Sie sind eine reine Bibliotheksfunktion. Es gibt genau eine Stelle in der Sprache, die mit Symbols verknüpft ist : Ein defMethodendefinitionsausdruck ergibt einen SymbolWert, der den Namen der Methode angibt, die definiert wird. Dies ist jedoch eine relativ neue Änderung. Zuvor wurde der Rückgabewert einfach nicht angegeben. Die MRT- nilUntersuchung ergab lediglich eine Bewertung , Rubinius eine Bewertung für ein Rubinius::CompiledMethodObjekt und so weiter. Es wäre auch möglich, zu einem UnboundMethod... oder nur zu einem ... zu bewerten String.

Wäre ein Programm in Ruby leichter und / oder schneller als das Gegenstück in Python oder Node? Wenn ja, würde es daran liegen symbols?

Ich bin nicht sicher, was Sie hier fragen. Leistung ist meist eine Frage der Implementierungsqualität, nicht der Sprache. Außerdem ist Node nicht einmal eine Sprache, sondern ein ereignisgesteuertes E / A-Framework für ECMAScript. Durch Ausführen eines entsprechenden Skripts in IronPython und MRI ist IronPython wahrscheinlich schneller. Das Ausführen eines entsprechenden Skripts auf CPython und JRuby + Truffle, JRuby + Truffle ist wahrscheinlich schneller. Dies hat nichts mit Symbols zu tun, sondern mit der Qualität der Implementierung: JRuby + Truffle verfügt über einen aggressiv optimierenden Compiler sowie die gesamte Optimierungsmaschinerie einer leistungsstarken JVM. CPython ist ein einfacher Interpreter.

Da es Rubys Absicht ist, für Menschen leicht zu lesen und zu schreiben, könnten die Entwickler den Codierungsprozess nicht dadurch vereinfachen, dass sie diese Verbesserungen im Interpreter selbst implementieren (wie es in anderen Sprachen der Fall sein könnte)?

Nr. SymbolS sind keine Compiler-Optimierung. Sie sind ein separater Datentyp mit spezifischer Semantik. Sie sind nicht wie die Flonums von YARV , die eine private interne Optimierung für Floats darstellen. Die Situation ist nicht die gleiche wie für Integer, Bignumund Fixnum, was sein soll , eine unsichtbare private interne Optimierung Detail, aber leider ist es nicht. (Dies wird endlich in Ruby 2.4 behoben, das nur entfernt Fixnumund Bignumund verlässt Integer.)

Wenn Sie dies wie in Java tun, bedeutet dies, Stringdass Sie als normaler Zustand immer vorsichtig sein müssen, ob sich Ihre Strings in diesem speziellen Zustand befinden und unter welchen Umständen sie sich automatisch in diesem speziellen Zustand befinden und wann nicht. Das ist eine viel höhere Belastung als ein separater Datentyp.

Gibt es eine sprachunabhängige Definition von Symbolen und einen Grund, sie in anderen Sprachen zu haben?

Symbolist ein Datentyp, der das Konzept bezeichnet Namen oder Etikett . Symbols sind Wertobjekte , unveränderlich, normalerweise unmittelbar (wenn die Sprache so etwas unterscheidet), staatenlos und haben keine Identität. Zwei Symbolgleiche sind garantiert auch identisch, dh zwei Symbolgleiche sind eigentlich dasselbe Symbol. Dies bedeutet, dass Wertgleichheit und Referenzgleichheit dasselbe sind, und somit ist Gleichheit effizient und O (1).

Die Gründe, sie in einer Sprache zu haben, sind wirklich die gleichen, unabhängig von der Sprache. Einige Sprachen stützen sich mehr auf sie als andere.

In der Lisp-Familie gibt es zum Beispiel kein Konzept von "Variable". Stattdessen haben Sie Symbols mit Werten verknüpft.

In Sprachen mit reflektierenden oder introspektiven Fähigkeiten, Symbolwerden s häufig verwendet , um die Namen von reflektiertem Einheiten in dem Reflexions APIs zu bezeichnen, zum Beispiel in Ruby, Object#methods, Object#singleton_methods, Object#public_methods, Object#protected_methods, und Object#public_methodseine Rückkehr Arrayvon Symbols (obwohl sie genauso gut könnten eine Rückkehr Arrayvon Methods). Object#public_sendNimmt eine SymbolBezeichnung für den Namen der zu sendenden Nachricht als Argument (obwohl auch eine akzeptiert wird String, Symbolist dies semantisch korrekter).

In ECMAScript sind Symbols ein grundlegender Baustein, um ECMAScript in Zukunft funktionssicher zu machen. Sie spielen auch eine große Rolle bei der Reflexion.

Jörg W. Mittag
quelle
Erlang-Atome wurden direkt aus Prolog entnommen (Robert Virding erzählte mir das irgendwann)
Zachary K
2

Symbole sind in Ruby nützlich und werden im gesamten Ruby-Code angezeigt, da jedes Symbol bei jedem Verweis wiederverwendet wird. Dies ist eine Leistungsverbesserung gegenüber Zeichenfolgen, da bei jeder Verwendung einer Zeichenfolge, die nicht in einer Variablen gespeichert ist, ein neues Objekt im Speicher erstellt wird. Wenn ich zum Beispiel dieselbe Zeichenfolge mehrmals als Hash-Schlüssel verwende:

my_hash = {"a" => 1, "b" => 2, "c" => 3}
100_000.times { |i| puts my_hash["a"] }

Die Zeichenfolge "a" wird 101.000-mal im Speicher erstellt. Wenn ich stattdessen ein Symbol verwendet habe:

my_hash = {a: 1, b: 2, c: 3}
100_000.times { |i| puts my_hash[:a] }

Das Symbol :aist immer noch ein Objekt im Speicher. Dies macht Symbole wesentlich effizienter als Zeichenfolgen.

UPDATE Hier ist ein Benchmark (entnommen aus der Codecademy ), der den Leistungsunterschied demonstriert:

require 'benchmark'

string_AZ = Hash[("a".."z").to_a.zip((1..26).to_a)]
symbol_AZ = Hash[(:a..:z).to_a.zip((1..26).to_a)]

string_time = Benchmark.realtime do
  100_000.times { string_AZ["r"] }
end

symbol_time = Benchmark.realtime do
  100_000.times { symbol_AZ[:r] }
end

puts "String time: #{string_time} seconds."
puts "Symbol time: #{symbol_time} seconds."

Hier sind meine Ergebnisse für mein MBP:

String time: 0.1254125550040044 seconds.
Symbol time: 0.07360960397636518 seconds.

Es gibt einen klaren Unterschied zwischen der Verwendung von Zeichenfolgen und Symbolen, um nur die Schlüssel in einem Hash zu identifizieren.

Keith Mattix
quelle
Ich bin mir nicht sicher, ob dies der Fall ist. Ich würde erwarten, dass eine Ruby-Implementierung denselben Code mehrmals ausführt und den Code nicht bei jeder Iteration erneut analysiert. Selbst wenn jedes lexikalische Vorkommen von "a"in der Tat eine neue Zeichenfolge ist, denke ich, dass es in Ihrem Beispiel genau zwei geben wird "a"(und eine Implementierung den Speicher sogar teilen könnte, bis eine von ihnen mutiert ist). Um Millionen von Zeichenfolgen zu erstellen, müssten Sie wahrscheinlich String.new ("a") verwenden. Aber ich kenne mich mit Ruby nicht gut aus, also irre ich mich vielleicht.
Coredump
1
In einer Lektion der Codecademy wird ein Vergleich zwischen Zeichenfolgen und Symbolen erstellt, ähnlich wie in meinem Beispiel. Ich werde es der Antwort hinzufügen.
Keith Mattix
1
Vielen Dank, dass Sie den Benchmark hinzugefügt haben. Ihr Test zeigt den erwarteten Gewinn, der durch die Verwendung von Symbolen anstelle von Zeichenfolgen aufgrund eines schnelleren Tests in der Hash-Tabelle (Vergleich von Identität und Zeichenfolge) erzielt wird. Wir können jedoch nicht ableiten, dass Zeichenfolgen bei jeder Iteration zugewiesen werden. Ich habe eine Version mit hinzugefügt, string_AZ[String.new("r")]um zu sehen, ob das einen Unterschied macht. Ich bekomme 21ms für Strings (Originalversion), 7ms mit Symbolen und 50ms mit jeweils neuen Strings. Daher würde ich sagen, dass Zeichenfolgen in der Literalversion nicht so häufig zugewiesen werden "r".
Coredump
1
Ah, also habe ich noch ein bisschen gegraben, und in Ruby 2.1 werden die Zeichenfolgen tatsächlich geteilt. Ich habe dieses Update anscheinend verpasst. Danke, dass Sie darauf hingewiesen haben. Ich gehe auf die ursprüngliche Frage zurück und denke, beide Benchmarks zeigen den Nutzen von Symbolen gegenüber Zeichenfolgen.
Keith Mattix