Mengapa pembuat Ruby memilih untuk menggunakan konsep Simbol?

15

tl; dr: Apakah akan ada definisi bahasa-agnostik dari Simbol dan alasan untuk memilikinya dalam bahasa lain?

Jadi, mengapa pembuat Ruby menggunakan konsep symbolsdalam bahasa?

Saya menanyakan ini dari sudut pandang programmer non-ruby. Saya telah belajar banyak bahasa lain, dan tidak menemukan satu pun dari mereka, kebutuhan untuk menentukan apakah saya berurusan atau tidak dengan apa yang disebut Ruby symbols.

Pertanyaan utamanya adalah, apakah konsep symbolsdi Ruby ada untuk kinerja, atau hanya sesuatu yang diperlukan karena cara bahasa ditulis?

Apakah program di Ruby lebih ringan dan / atau lebih cepat daripada, katakanlah, Python atau Javascript? Jika demikian, apakah karena symbols?

Karena salah satu niat Ruby adalah agar mudah dibaca dan ditulis untuk manusia, tidak dapatkah pembuatnya mempermudah proses pengkodean dengan mengimplementasikan peningkatan itu dalam juru bahasa itu sendiri (seperti dalam bahasa lain)?

Sepertinya semua orang hanya ingin tahu apa symbolsdan bagaimana menggunakannya, dan bukan mengapa mereka ada di tempat pertama.

Yuri Ghensev
sumber
Scala memiliki Simbol, dari atas kepalaku. Saya pikir banyak Lisps lakukan.
D. Ben Knoble

Jawaban:

17

Pencipta Ruby, Yukihiro "Matz" Matsumoto, memposting penjelasan tentang bagaimana Ruby dipengaruhi oleh Lisp, Smalltalk, Perl (dan Wikipedia mengatakan Ada dan Eiffel juga):

Ruby adalah bahasa yang dirancang dalam langkah-langkah berikut:

  • ambil bahasa cadel sederhana (seperti bahasa sebelum CL).
  • hapus macro, s-ekspresi.
  • tambahkan sistem objek sederhana (jauh lebih sederhana dari CLOS).
  • tambahkan blok, terinspirasi oleh fungsi tingkat tinggi.
  • tambahkan metode yang ditemukan di Smalltalk.
  • tambahkan fungsionalitas yang ditemukan di Perl (dengan cara OO).

Jadi, Ruby awalnya adalah Lisp, secara teori.

Sebut saja MatzLisp mulai sekarang. ;-)

Di kompiler mana pun, Anda akan mengelola pengidentifikasi untuk fungsi, variabel, blok bernama, jenis, dan sebagainya. Biasanya Anda menyimpannya di kompiler dan melupakannya di executable yang dihasilkan, kecuali ketika Anda menambahkan informasi debug.

Di Lisp, simbol-simbol tersebut adalah sumber daya kelas satu, dihosting dalam paket yang berbeda, yang berarti Anda dapat menambahkan simbol baru saat runtime, mengikatnya ke berbagai jenis objek. Ini berguna ketika meta-pemrograman karena Anda bisa yakin Anda tidak akan memiliki tabrakan penamaan dengan bagian lain dari kode.

Juga, simbol diinternir pada waktu membaca dan dapat dibandingkan dengan identitas, yang merupakan cara yang efisien untuk memiliki jenis nilai baru (seperti angka, tetapi abstrak). Ini membantu menulis kode tempat Anda menggunakan nilai simbolik secara langsung, alih-alih menentukan jenis enum Anda sendiri yang didukung oleh bilangan bulat. Juga, setiap simbol dapat menampung data tambahan. Begitulah cara, misalnya, Emacs / Slime dapat melampirkan metadata dari Emacs langsung ke daftar properti simbol.

Gagasan tentang simbol adalah sentral dalam Lisp. Lihat misalnya di PAIP (Paradigma Pemrograman Kecerdasan Buatan: Studi Kasus di Common Lisp, Norvig) untuk contoh terperinci.

coredump
sumber
5
Jawaban yang bagus. Namun saya tidak setuju dengan Matz: Saya tidak akan pernah berpikir untuk memanggil bahasa tanpa makro dialek bahasa Inggris. Fasilitas runtime-metaprogramming dari lisp adalah hal yang membuat bahasa ini memiliki kekuatan yang luar biasa, menggantikan tata bahasanya yang sangat sederhana dan tidak ekspresif.
cmaster - mengembalikan monica
11

Jadi, mengapa pembuat Ruby harus menggunakan konsep symbolsdalam bahasa?

Yah, mereka tidak benar-benar "harus", mereka memilih untuk. Juga, perhatikan bahwa secara tegas Symbols bukan bagian dari bahasa, mereka adalah bagian dari perpustakaan inti. Mereka memang memiliki sintaksis tingkat bahasa, tetapi mereka akan bekerja dengan baik jika Anda harus membangunnya dengan menelepon Symbol::new.

Saya bertanya dari sudut pandang programmer non-ruby yang mencoba memahaminya. Saya telah belajar banyak bahasa lain dan tidak menemukan satu pun di antara mereka perlu menentukan apakah saya berurusan atau tidak dengan apa yang disebut Ruby symbols.

Anda tidak mengatakan apa itu "banyak bahasa lain", tapi ini hanya kutipan kecil dari bahasa yang memiliki Symboltipe data seperti Ruby:

Ada juga bahasa lain yang menyediakan fitur Symbols dalam bentuk yang berbeda. Di Jawa, misalnya, fitur Ruby Stringdibagi menjadi dua (sebenarnya tiga) jenis: Stringdan StringBuilder/ StringBuffer. Di sisi lain, fitur Symboltipe Ruby dilipat ke dalam Stringtipe Java : Java Strings dapat diinternir , string literal dan Strings yang merupakan hasil kompilasi waktu ekspresi konstan dievaluasi secara otomatis diinternir, yang dihasilkan secara dinamis Stringdapat diinternir dengan memanggil yang String.internmetode. Diinternir Stringdi Jawa persis seperti Symboldi Ruby, tapi itu tidak diimplementasikan sebagai tipe terpisah, itu hanya keadaan berbeda bahwa JavaStringbisa masuk. (Catatan: di versi Ruby yang lebih lama, String#to_symdulu dipanggil String#interndan metode itu masih ada sampai sekarang sebagai alias warisan.)

Pertanyaan utamanya adalah: Apakah konsep symbolsdalam Ruby ada sebagai maksud kinerja atas dirinya sendiri dan bahasa lain

Symbols adalah datatype pertama dan terpenting dengan semantik tertentu . Semantik ini juga memungkinkan untuk mengimplementasikan beberapa operasi performan (mis. Pengujian cepat O (1)), tetapi itu bukan tujuan utama.

atau hanya sesuatu yang perlu ada karena cara bahasa ditulis?

SymbolKarena tidak diperlukan dalam bahasa Ruby sama sekali, Ruby akan bekerja dengan baik tanpa mereka. Mereka murni fitur perpustakaan. Tepat ada satu tempat dalam bahasa yang terkait dengan Symbols: defekspresi definisi metode mengevaluasi ke yang Symbolmenunjukkan nama metode yang sedang didefinisikan. Namun, itu adalah perubahan yang agak baru, sebelum itu, nilai kembali hanya dibiarkan tidak ditentukan. MRI hanya dievaluasi nil, Rubinius dievaluasi ke Rubinius::CompiledMethodobjek, dan sebagainya. Juga mungkin untuk mengevaluasi ke UnboundMethod... atau hanya a String.

Apakah program di Ruby akan lebih ringan dan / atau lebih cepat daripada, katakanlah, Python atau Node counterpart? Jika demikian, apakah karena symbols?

Saya tidak yakin apa yang Anda tanyakan di sini. Kinerja sebagian besar adalah masalah kualitas implementasi, bukan bahasa. Plus, Node bahkan bukan bahasa, ini adalah kerangka kerja I / O untuk ECMAScript. Menjalankan skrip yang setara pada IronPython dan MRI, IronPython kemungkinan akan lebih cepat. Menjalankan skrip yang setara pada CPython dan JRuby + Truffle, JRuby + Truffle cenderung lebih cepat. Ini tidak ada hubungannya dengan Symbols tetapi dengan kualitas implementasinya: JRuby + Truffle memiliki kompiler yang mengoptimalkan secara agresif, ditambah seluruh mesin optimisasi JVM berkinerja tinggi, CPython adalah penerjemah sederhana.

Karena salah satu maksud Ruby adalah agar mudah dibaca dan ditulis untuk manusia, tidak bisakah penciptanya mempermudah proses pengkodean dengan mengimplementasikan peningkatan itu dalam juru bahasa itu sendiri (seperti dalam bahasa lain)?

Tidak. SymbolS bukan optimasi kompiler. Mereka adalah tipe data terpisah dengan semantik tertentu. Mereka tidak seperti flonum YARV , yang merupakan optimasi internal pribadi untuk Floats. Situasinya tidak sama dengan untuk Integer, Bignumdan Fixnum, yang seharusnya merupakan detail optimasi internal pribadi yang tidak terlihat, tetapi sayangnya tidak. (Ini akhirnya akan diperbaiki di Ruby 2.4, yang menghapus Fixnumdan Bignumdan meninggalkan hanya Integer.)

Melakukannya dengan cara Jawa melakukannya, sebagai keadaan khusus dari Strings normal berarti bahwa Anda selalu perlu waspada tentang apakah Anda Stringberada dalam keadaan khusus dan di bawah keadaan apa mereka secara otomatis dalam keadaan khusus itu dan kapan tidak. Itu beban yang jauh lebih tinggi daripada hanya memiliki tipe data terpisah.

Apakah akan ada definisi bahasa-agnostik dari Simbol dan alasan untuk memilikinya dalam bahasa lain?

Symboladalah tipe data yang menunjukkan konsep nama atau label . Symbols adalah objek bernilai , tidak berubah, biasanya langsung (jika bahasa membedakan hal semacam itu), tanpa kewarganegaraan, dan tidak memiliki identitas. Two Symbols yang sama juga dijamin identik, dengan kata lain, dua Symbols yang sama sebenarnya sama Symbol. Ini berarti bahwa persamaan nilai dan persamaan referensi adalah hal yang sama, dan dengan demikian persamaan tersebut efisien dan O (1).

Alasan untuk memilikinya dalam suatu bahasa benar-benar sama, terlepas dari bahasa tersebut. Beberapa bahasa lebih mengandalkan mereka daripada yang lain.

Dalam keluarga Lisp, misalnya, tidak ada konsep "variabel". Sebaliknya, Anda telah Symboldikaitkan dengan nilai-nilai.

Dalam bahasa dengan kemampuan reflektif atau introspektif, Symbols sering digunakan untuk menunjukkan nama-nama entitas tercermin dalam API refleksi, misalnya di Ruby, Object#methods, Object#singleton_methods, Object#public_methods, Object#protected_methods, dan Object#public_methodsmengembalikan Arraydari Symbols (meskipun mereka bisa juga mengembalikan Arraydari Methods). Object#public_sendmengambil Symboldenoting nama pesan untuk dikirim sebagai argumen (meskipun ia juga menerima String, Symbollebih tepat secara semantik).

Dalam ECMAScript, Symbols adalah blok bangunan mendasar untuk membuat kapabilitas ECMAScript aman di masa depan. Mereka juga memainkan peran besar dalam refleksi.

Jörg W Mittag
sumber
Atom Erlang diambil langsung dari Prolog (Robert Virding mengatakan kepada saya bahwa pada titik tertentu)
Zachary K
2

Simbol berguna di Ruby, dan Anda akan melihatnya di seluruh kode Ruby karena setiap simbol digunakan kembali setiap kali direferensikan. Ini adalah peningkatan kinerja atas string karena setiap penggunaan string yang tidak disimpan dalam variabel menciptakan objek baru di memori. Misalnya, jika saya menggunakan string yang sama beberapa kali sebagai kunci hash:

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

String "a" dibuat 101.000 kali dalam memori. Jika saya menggunakan simbol sebagai gantinya:

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

Simbol :amasih satu objek di memori. Ini membuat simbol jauh lebih efisien daripada string.

PEMBARUAN Berikut ini adalah patokan (diambil dari Codecademy ) yang menunjukkan perbedaan kinerja:

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."

Inilah hasil saya untuk MBP saya:

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

Ada perbedaan yang jelas dalam menggunakan string vs simbol untuk hanya mengidentifikasi kunci dalam hash.

Keith Mattix
sumber
Saya tidak yakin apakah ini masalahnya. Saya mengharapkan implementasi Ruby untuk mengeksekusi kode yang sama beberapa kali, tidak mem-parsing kode lagi dan lagi untuk setiap iterasi. Bahkan jika setiap kemunculan leksikal "a"memang merupakan string baru, saya pikir dalam contoh Anda akan ada tepat dua "a"(dan implementasi bahkan dapat berbagi memori sampai salah satu dari mereka bermutasi). Untuk membuat jutaan string, Anda mungkin perlu menggunakan String.new ("a"). Tapi saya tidak berpengalaman dalam Ruby, jadi mungkin saya salah.
coredump
1
Dalam salah satu pelajaran Codecademy, mereka menghasilkan tolok ukur untuk string vs simbol, seperti contoh saya. Saya akan menambahkannya ke jawabannya.
Keith Mattix
1
Terima kasih telah menambahkan patokan. Tes Anda menunjukkan keuntungan yang diharapkan yang diperoleh dengan menggunakan simbol alih-alih string, karena tes lebih cepat dalam hashtable (perbandingan identitas vs string), tetapi tidak ada cara kami dapat menyimpulkan bahwa string dialokasikan pada setiap iterasi. Saya menambahkan versi dengan string_AZ[String.new("r")]untuk melihat apakah itu membuat perbedaan. Saya mendapatkan 21ms untuk string (versi asli), 7ms dengan simbol dan 50ms dengan string segar setiap kali. Jadi saya akan mengatakan bahwa string tidak dialokasikan sebanyak dengan "r"versi literal .
coredump
1
Ah, jadi saya melakukan beberapa penggalian lagi, dan di Ruby 2.1, string sebenarnya dibagi. Saya tampaknya melewatkan pembaruan itu; terima kasih telah menunjukkan itu. Kembali ke pertanyaan awal, saya pikir kedua tolok ukur menunjukkan kegunaan simbol vs string.
Keith Mattix