Di bidang apa makro LISP lebih baik daripada "kemampuan" Ruby untuk membuat DSL

21

Salah satu hal yang membuat Ruby bersinar adalah kemampuan untuk membuat Bahasa Khusus Domain menjadi lebih baik

Meskipun orang dapat menduplikasi perpustakaan ini di LISP melalui makro, saya pikir implementasi Ruby lebih elegan. Meskipun demikian, saya pikir ada kasus bahwa makro LISP bisa lebih baik daripada Ruby, meskipun saya tidak bisa memikirkan satu pun.

Jadi, di bidang apa makro LISP lebih baik daripada "kemampuan" Ruby untuk membuat DSL, jika ada?

memperbarui

Saya sudah menanyakan hal ini karena bahasa pemrograman modern mendekati singularitas LISP , seperti

  • C mendapat preprocessor ekspansi makro , meskipun sangat primitif dan rentan terhadap kesalahan
  • C # memiliki atribut, meskipun ini hanya baca, diekspos melalui refleksi
  • Python menambahkan dekorator, yang dapat memodifikasi perilaku fungsi (dan kelas untuk v 3.0), meskipun terasa sangat terbatas.
  • Ruby TMTOWTDI yang membuat DSL elegan, jika perawatan diterapkan, tetapi dengan cara Ruby.

Saya bertanya-tanya apakah makro LISP hanya berlaku untuk kasus khusus dan bahwa fitur bahasa pemrograman lainnya cukup kuat untuk meningkatkan abstraksi untuk memenuhi tantangan saat ini dalam pengembangan perangkat lunak.

OnesimusUnbound
sumber
4
Saya tidak yakin mengapa ada suara dekat tentang ini. Saya pikir ini adalah jenis pertanyaan yang kita inginkan ? Menarik, memprovokasi pemikiran dan ruang lingkup didefinisikan dengan cukup baik.
Tim Post
Cobalah untuk mengimplementasikan sesuatu seperti ini di Ruby: meta-alternative.net/pfront.pdf
SK-logic
@Tim Post: Satu masalah adalah bahwa ini sebenarnya bukan pertanyaan yang mudah dijawab kecuali Anda tahu Common Lisp dan Ruby dengan baik. Lain adalah referensi yang cukup bodoh untuk bahasa lain, yang dapat mengganggu puritan: tidak ada bahasa yang disebut C / C ++, dan bagian penting dari C ++ adalah sistem template, dan saran bahwa sistem makro Common Lisp hanya berlaku untuk kasus khusus. Pada dasarnya, ini adalah pertanyaan yang bagus, tetapi ditulis dengan buruk dan sulit dijawab.
David Thornley
@ David Thornley, saya telah memperbarui posting, khususnya pada C \ C ++, dan menempatkan penekanan pada C. Adapun Common Lisp, saya merasa bahwa karena bahasa pemrograman lain memiliki abstraksi yang lebih tinggi, fitur makro untuk kasus khusus. Saya memposting pertanyaan, berharap orang lain akan menunjukkan kepada saya bahwa makro CL bukan untuk kasus khusus masih merupakan fitur yang kuat.
OnesimusUnbound
@OnesimusUnbound: Saya benar-benar akan memasukkan template C ++ dalam daftar contoh bahasa Anda, karena mereka setidaknya secara teoritis mampu melakukan komputasi apa pun yang dapat dilakukan sistem makro Lisp. Sejauh yang dilakukan oleh Lisp's macro, dapatkan beberapa kode Common Lisp yang baik (ada banyak kode F / OS Lisp di luar sana), dan cari "defmacro" (Anda mungkin ingin melakukan pencarian case-insensitive). Anda mungkin akan menemukan banyak dari mereka. Pertimbangkan bahwa makro lebih sulit daripada fungsi untuk menulis dan mendapatkan yang benar, dan Anda akan menyadari bahwa mereka harus bermanfaat secara umum.
David Thornley

Jawaban:

23

Baca On Lisp dan putuskan sendiri.

Ringkasan saya adalah bahwa Ruby lebih baik dalam menyediakan sintaks yang nyaman. Tetapi Lisp menang, dengan tangan ke bawah, pada kemampuan untuk membuat abstraksi baru, dan kemudian untuk melapisi abstraksi pada abstraksi. Tetapi Anda perlu melihat Lisp dalam praktik untuk memahami hal itu. Karenanya buku ini merekomendasikan.

btilly
sumber
1
"Let Over Lambda" mencakup banyak kesamaan. Juga disarankan membaca.
John R. Strohm
But Lisp wins, hands down, at the ability to create new abstractions, and then to layer abstraction on abstraction.Sekarang saya tahu kenapa. . .
OnesimusUnbound
Bukan masalah besar untuk menyediakan segala bentuk sintaks di atas Lisp. Bahkan, jauh lebih mudah daripada dengan Ruby, terima kasih kepada macro pembaca.
SK-logic
1
@ SK-logic: Benar. Namun Lispers menghindar dari macro pembaca karena begitu Anda turun rute itu tidak lagi terasa seperti Lisp. Bahkan beberapa varian Lisp, seperti Clojure, cadangan makro pembaca untuk perancang bahasa.
btilly
14

Fasilitas Ruby untuk pembuatan DSL tidak mengubah sifat bahasa. Fasilitas metaprogramming Ruby secara inheren terkait dengan sintaksis dan semantik Ruby, dan apa pun yang Anda tulis harus dimasukkan ke dalam model objek Ruby.

Bandingkan dengan Lisp (dan Skema, yang fasilitas makronya berbeda ), di mana makro beroperasi pada program abstrak itu sendiri. Karena program Lisp adalah nilai Lisp, makro adalah fungsi yang memetakan satu sintaksis sewenang-wenang yang lain.

Secara efektif, Ruby DSL masih terasa seperti Ruby, tetapi DSL Lisp tidak harus terasa seperti Lisp.

Jon Purdy
sumber
6
+1: LOOP tidak terasa terlalu membosankan, sebagai contoh DSL.
Frank Shearar
2

DSL Ruby sama sekali bukan DSL, dan saya benar-benar benci menggunakannya karena dokumentasi mereka benar-benar terletak pada bagaimana mereka bekerja. Mari kita ambil ActiveRecord sebagai contoh. Hal ini memungkinkan Anda untuk "menyatakan" asosiasi antara Model:

class Foo < ActiveRecord::Base
    has_one :bar
    has_one :baz
end

Tetapi deklaratif "DSL" ini (seperti deklaratif classsintaksis Ruby itu sendiri) adalah kebohongan mengerikan yang dapat diungkapkan oleh siapa pun yang memahami bagaimana Ruby "DSL" sebenarnya bekerja:

class Foo < ActiveRecord::Base
    [:bar,:baz,:qux,:quux].each do |table|
        has_one table if i_feel_like_it?(table)
    end
    puts "Just for shits and giggles, and to show"
    puts "just how fucked up Ruby really is, we're gonna ask you"
    puts "which SQL table you want the Foo model to have an"
    puts "association with.\n"
    puts "Type the name of a table here: "
    has_one gets.chomp.to_sym
end

(Coba lakukan sesuatu yang dekat dengan itu dalam tubuh defclassbentuk Lisp !)

Segera setelah Anda memiliki kode seperti di atas dalam basis kode Anda, setiap pengembang pada proyek harus sepenuhnya memahami bagaimana Ruby DSL sebenarnya bekerja (bukan hanya ilusi yang mereka buat) sebelum mereka dapat mempertahankan kode. Dokumentasi yang tersedia akan sama sekali tidak berguna, karena mereka hanya mendokumentasikan penggunaan idiomatik , yang mempertahankan ilusi deklaratif.

RSpec bahkan lebih buruk daripada yang di atas, karena memiliki kasus tepi yang aneh yang membutuhkan rekayasa balik yang ekstensif untuk debug. (Saya menghabiskan sepanjang hari mencoba mencari tahu mengapa salah satu kasus pengujian saya dilewati. Ternyata RSpec mengeksekusi semua kasus pengujian yang memiliki konteks setelah kasus pengujian tanpa konteks, terlepas dari urutan kemunculannya di sumber. , karena contextmetode ini menempatkan blok Anda dalam struktur data yang berbeda dari biasanya.)

Lisp DSL diimplementasikan oleh makro, yang merupakan kompiler kecil. DSL yang dapat Anda buat dengan cara ini bukan hanya penyalahgunaan sintaksis Lisp yang ada. Mereka adalah mini-bahasa aktual yang dapat ditulis agar benar-benar mulus, karena mereka dapat memiliki tata bahasa mereka sendiri. Sebagai contoh, LOOPmakro Lisp jauh lebih kuat daripada eachmetode Ruby .

(Saya tahu Anda sudah menerima jawaban, tetapi tidak semua orang yang membaca ini ingin membaca keseluruhan On Lisp .)

Membuang Akun
sumber