Setelah beberapa bulan belajar tentang dan bermain dengan Lisp, baik CL dan sedikit Clojure, saya masih belum melihat alasan kuat untuk menulis apa pun di dalamnya daripada C #.
Saya benar-benar ingin beberapa alasan kuat, atau bagi seseorang untuk menunjukkan bahwa saya kehilangan sesuatu yang sangat besar .
Kekuatan Lisp (per penelitian saya):
- Notasi kompak dan ekspresif - Lebih dari sekadar C #, ya ... tapi saya juga bisa mengekspresikan ide-ide itu dalam C #.
- Dukungan implisit untuk pemrograman fungsional - C # dengan metode ekstensi LINQ:
- mapcar = .Pilih (lambda)
- mapcan = .Select (lambda) .Aregregate ((a, b) => a.Union (b))
- mobil / pertama = .Pertama ()
- cdr / rest = .Skip (1) .... dll.
- Lambda dan dukungan fungsi tingkat tinggi - C # memiliki ini, dan sintaksinya bisa dibilang lebih sederhana:
- "(lambda (x) (tubuh))" versus "x => (tubuh)"
- "# (" dengan "%", "% 1", "% 2" bagus di Clojure
- Metode pengiriman terpisah dari objek - C # memiliki ini melalui metode ekstensi
- Pengiriman multimethod - C # tidak memiliki ini secara asli, tapi saya bisa mengimplementasikannya sebagai pemanggilan fungsi dalam beberapa jam
- Code is Data (and Macros) - Mungkin saya belum "mendapatkan" makro, tapi saya belum melihat satu contoh pun di mana gagasan makro tidak dapat diimplementasikan sebagai fungsi; itu tidak mengubah "bahasa", tapi saya tidak yakin itu kekuatan
- DSL - Hanya dapat melakukannya melalui komposisi fungsi ... tetapi berfungsi
- Pemrograman "eksplorasi" yang tidak diketik - untuk struct / kelas, properti otomatis dan "objek" C # bekerja cukup baik, dan Anda dapat dengan mudah meningkat menjadi pengetikan yang lebih kuat saat Anda mengikuti
- Berjalan pada perangkat keras non-Windows - Ya, jadi? Di luar kampus, saya hanya kenal satu orang yang tidak menjalankan Windows di rumah, atau setidaknya VM Windows di * nix / Mac. (Sekali lagi, mungkin ini lebih penting daripada yang saya pikirkan dan saya baru saja dicuci otak ...)
- REPL untuk desain bottom-up - Ok, saya akui ini benar-benar bagus, dan saya melewatkannya di C #.
Hal yang saya lewatkan di Lisp (karena campuran C #, .NET, Visual Studio, Resharper):
- Ruang nama. Bahkan dengan metode statis, saya suka mengikat mereka ke "kelas" untuk mengkategorikan konteksnya (tampaknya Clojure memiliki ini, CL sepertinya tidak.)
- Dukungan kompilasi dan desain-waktu yang hebat
- sistem tipe memungkinkan saya untuk menentukan "kebenaran" dari struktur data yang saya bagikan
- sesuatu yang salah eja digarisbawahi secara realtime; Saya tidak perlu menunggu sampai runtime tahu
- perbaikan kode (seperti menggunakan pendekatan FP bukan yang imperatif) disarankan secara otomatis
- Alat pengembangan GUI: WinForms dan WPF (saya tahu Clojure memiliki akses ke perpustakaan GUI Java, tetapi semuanya sepenuhnya asing bagi saya.)
- Alat Debugging GUI: breakpoints, step-in, step-over, pemeriksa nilai (teks, xml, custom), jam tangan, debug-by-thread, breakpoint bersyarat, jendela panggilan-tumpukan dengan kemampuan untuk melompat ke kode di tingkat mana pun di tumpukan
- (Agar adil, tugas saya dengan Emacs + Slime tampaknya memberikan sebagian dari ini, tapi saya tidak setuju dengan pendekatan yang digerakkan VS GUI)
Saya sangat suka hype di sekitar Lisp dan saya memberikannya kesempatan.
Tetapi adakah yang bisa saya lakukan di Lisp yang tidak bisa saya lakukan dengan baik di C #? Mungkin sedikit lebih verbose di C #, tapi saya juga punya autocomplete.
Apa yang saya lewatkan? Mengapa saya harus menggunakan Clojure / CL?
AND
atauOR
sebagai fungsi. Itu bisa dilakukan (diberikanLAMBDA
, yang juga makro) tapi saya tidak melihat cara yang jelas untuk melakukannya yang tidak akan benar-benar payah.:
(atau::
untuk simbol yang tidak diekspor) untuk penamaan simbol dalam paket, misalnya, contoh Anda di sini akan menggunakanhttp:session
danchat:session
.Jawaban:
Makro memungkinkan Anda menulis kompiler tanpa meninggalkan kenyamanan bahasa Anda. DSL dalam bahasa seperti C # umumnya berjumlah penerjemah runtime. Bergantung pada domain Anda, itu mungkin bermasalah karena alasan kinerja. Kecepatan itu penting , jika tidak Anda akan mengkode dalam bahasa yang ditafsirkan dan bukan C #.
Selain itu, makro juga memungkinkan Anda mempertimbangkan faktor manusia - apa sintaks yang paling mudah digunakan untuk DSL tertentu? Dengan C # tidak banyak yang dapat Anda lakukan tentang sintaks.
Jadi Lisp memungkinkan Anda berpartisipasi dalam desain bahasa tanpa mengorbankan jalan menuju efisiensi . Dan sementara masalah ini mungkin tidak masalah bagi Anda , mereka sangat penting karena mereka mendasari fondasi semua bahasa pemrograman berorientasi produksi yang berguna. Dalam C # elemen dasar ini terpapar kepada Anda dalam bentuk yang sangat terbatas.
Pentingnya makro tidak hilang pada bahasa lain - Template Haskell, camlp4 datang ke pikiran. Tapi sekali lagi ini adalah masalah kegunaan - Lisp macro sampai saat ini kemungkinan merupakan implementasi transformasi waktu kompilasi yang paling ramah pengguna namun kuat.
Jadi, ringkasnya, apa yang umumnya dilakukan orang dengan bahasa seperti C # adalah membangun DSIL (Domain Specific Interpreted Languages). Lisp memberi Anda kesempatan untuk membangun sesuatu yang jauh lebih radikal, DSP (Domain Specific Paradigms). Racket (sebelumnya PLT-Skema) sangat menginspirasi dalam hal ini - mereka memiliki bahasa malas (Lazy Racket), yang diketik (Typed Racket), Prolog, dan Datalog semuanya tertanam secara idiomatis melalui makro. Semua paradigma ini memberikan solusi yang kuat untuk kelas besar masalah - masalah tidak diselesaikan dengan baik oleh pemrograman imperatif atau bahkan paradigma FP.
sumber
Hampir semua yang semula hanya tersedia di Lisps telah untungnya bermigrasi ke banyak bahasa modern lainnya. Pengecualian terbesar adalah filosofi "kode adalah data", dan makro.
Ya, makro sulit untuk grok. Pemrograman secara umum sulit dilakukan. Jika Anda melihat makro apa pun yang dapat diimplementasikan sebagai fungsi, maka programmer salah melakukannya. Macro seharusnya hanya digunakan ketika suatu fungsi tidak bekerja.
Contoh "hello world" dari makro adalah menulis "jika" dalam hal cond. Jika Anda benar-benar tertarik untuk mempelajari kekuatan makro, cobalah mendefinisikan "my-if" di clojure, menggunakan fungsi, dan perhatikan bagaimana hal itu pecah. Saya tidak bermaksud untuk menjadi misterius, saya hanya belum pernah melihat orang mulai memahami makro dengan penjelasan saja, tapi tayangan slide ini adalah intro yang cukup bagus: http://www.slideshare.net/pcalcado/lisp-macros-in -20-menit-menampilkan-presentasi-clojure
Saya memiliki proyek kecil di mana saya menyimpan ratusan / ribuan baris kode menggunakan makro (dibandingkan dengan apa yang akan diambil di Jawa). Banyak Clojure diimplementasikan di Clojure, yang hanya mungkin karena sistem makro.
sumber
(defun my-if (test then &optional else) (cond (test (funcall then)) ('otherwise (funcall else))))
Saya bukan ahli Common Lisp, tapi saya tahu C # dan Clojure cukup baik sehingga mudah-mudahan ini adalah perspektif yang berguna.
Sebagai hasil dari sintaks yang sangat sederhana, filosofi "code is data" Lisp jauh lebih kuat daripada apa pun yang dapat Anda lakukan dengan komposisi fungsi / templat / generik dll. Ini lebih dari sekadar DSL, pembuatan kode dan manipulasi, pada waktu kompilasi , sebagai fitur dasar bahasa. Jika Anda mencoba untuk mendapatkan kemampuan semacam ini dalam bahasa non- homoiconic seperti C # atau Java, Anda akhirnya akan menemukan kembali Lisp, buruk. Karena itulah Aturan Kesepuluh Greenspuns yang terkenal: "Setiap program C atau Fortran yang cukup rumit mengandung implementasi ad hoc, dispesifikasikan secara informal, ditanggulangi, lambat dari setengah Common Lisp". Jika Anda pernah menemukan diri Anda menciptakan bahasa kontrol templating / flow turing lengkap dalam aplikasi Anda maka Anda mungkin tahu apa yang saya maksud .....
Concurrency adalah kekuatan unik Clojure (bahkan relatif terhadap Lisps lain), tetapi jika Anda percaya bahwa masa depan pemrograman akan berarti harus menulis aplikasi yang skala ke mesin yang sangat multi-core, maka Anda benar-benar benar-benar harus melihat ke dalam ini - itu cukup terobosan. Clojure STM (Memori Transaksional Perangkat Lunak) berfungsi karena Clojure mencakup konstruksi ketetapan dan konkurensi bebas-kunci berdasarkan pemisahan baru antara identitas dan negara (lihat video hebat Rich Hickey tentang identitas dan negara ). Akan sangat menyakitkan untuk mencangkokkan kemampuan konkurensi semacam ini ke lingkungan bahasa / runtime di mana sebagian besar API bekerja pada objek yang bisa diubah.
Pemrograman fungsional - Lisps menekankan pemrograman dengan fungsi urutan yang lebih tinggi. Anda benar bahwa itu dapat ditiru dalam objek OO dengan objek penutupan / fungsi dll. Tetapi perbedaannya adalah bahwa seluruh bahasa dan pustaka standar dirancang untuk membantu Anda menulis kode dengan cara ini. Sebagai contoh, urutan Clojure adalah malas , jadi bisa sangat lama tidak seperti ArrayLists atau HashMaps Anda di C # / Java. Ini membuat beberapa algoritma lebih mudah untuk diekspresikan, misalnya yang berikut mengimplementasikan daftar angka-angka fibonacci yang tidak terbatas:
(def fibs (lazy-cat '(0 1) (map + fibs (drop 1 fibs))))
Pengetikan dinamis - Lisps cenderung berada di ujung "dinamis" dari spektrum bahasa pemrograman fungsional (berbeda dengan bahasa seperti Haskell dengan konsepsi statis tipe yang sangat kuat dan indah). Ini memiliki kelebihan dan kekurangan, tetapi saya berpendapat bahwa bahasa yang dinamis cenderung mendukung produktivitas pemrograman karena fleksibilitas yang lebih besar. Pengetikan dinamis ini memiliki biaya kinerja runtime kecil, tetapi jika itu mengganggu Anda, Anda selalu dapat menambahkan petunjuk jenis ke kode Anda untuk mendapatkan pengetikan statis. Pada dasarnya - Anda mendapatkan kenyamanan secara default, dan kinerja jika Anda bersedia melakukan sedikit pekerjaan ekstra untuk mendapatkannya.
Pengembangan interaktif - sebenarnya saya pikir Anda bisa mendapatkan semacam REPL untuk C # 4.0, tetapi di Lisp itu praktik standar daripada hal yang baru. Anda tidak harus melakukan siklus kompilasi / bangun / uji coba sama sekali - Anda menulis kode Anda langsung di lingkungan yang sedang berjalan, dan hanya kemudian menyalinnya ke file sumber Anda. Ini mengajarkan Anda cara yang sangat berbeda untuk kode, di mana Anda melihat hasilnya segera dan memperbaiki solusi Anda secara iteratif.
Beberapa komentar singkat tentang hal-hal yang Anda lihat "hilang":
Saya pribadi menemukan keuntungan dari Clojure / Lisp yang cukup menarik, dan saya tidak berencana untuk kembali ke C # / Java (di luar apa yang saya butuhkan untuk meminjam semua perpustakaan yang bermanfaat!).
Namun, butuh beberapa bulan bagi saya untuk benar-benar memahami semuanya. Jika Anda benar-benar tertarik mempelajari Lisp / Clojure sebagai pengalaman belajar yang mencerahkan, tidak ada pengganti untuk menyelam dan memaksa diri Anda untuk menerapkan beberapa hal .....
sumber
C # punya banyak fitur, tetapi campurannya terasa berbeda. Bahwa beberapa bahasa memiliki fitur masih tidak berarti bahwa itu mudah digunakan, paradigmatik dan terintegrasi dengan baik dengan seluruh bahasa.
Common Lisp memiliki campuran ini:
Sekarang, bahasa lain, seperti C #, memilikinya juga. Namun seringkali tidak dengan penekanan yang sama.
C # telah
Itu tidak membantu bahwa C # memiliki misalnya fitur manipulasi kode, jika mereka tidak banyak digunakan seperti pada Lisp. Di Lisp Anda tidak akan menemukan banyak perangkat lunak yang tidak menggunakan makro dalam berbagai cara sederhana dan kompleks. Menggunakan manipulasi kode benar-benar fitur besar di Lisp. Meniru beberapa penggunaan dimungkinkan dalam banyak bahasa, tetapi itu tidak menjadikannya fitur utama seperti pada Lisp. Lihat buku-buku seperti 'On Lisp' atau 'Practical Common Lisp' untuk contohnya.
Sama untuk pengembangan interaktif. Jika Anda mengembangkan sistem CAD besar, sebagian besar pengembangan akan bersifat interaktif dari editor dan REPL - langsung ke dalam aplikasi CAD yang sedang berjalan. Anda dapat meniru banyak hal itu dalam C # atau bahasa lain - seringkali dengan menerapkan bahasa ekstensi. Untuk Lisp akan bodoh untuk me-restart aplikasi CAD yang sedang dikembangkan untuk menambahkan perubahan. Sistem CAD kemudian juga akan berisi Lisp yang disempurnakan yang akan menambahkan fitur bahasa (dalam bentuk makro dan deskripsi simbolik) untuk menggambarkan objek CAD dan hubungannya (bagian-dari, berisi, ...). Orang dapat melakukan hal serupa di C #, tetapi di Lisp ini adalah gaya pengembangan default.
Jika Anda mengembangkan perangkat lunak yang kompleks menggunakan proses pengembangan interaktif atau perangkat lunak Anda memiliki bagian simbolis yang substansial (pemrograman meta, paradigma lain, aljabar komputer, komposisi musik, perencanaan, ...), maka Lisp mungkin tepat untuk Anda.
Jika Anda bekerja di lingkungan Windows (saya tidak), maka C # memiliki keuntungan, karena itu adalah bahasa pengembangan utama Microsoft. Microsoft tidak mendukung Lisp dalam bentuk apa pun.
Anda menulis:
Lisp umum tidak melampirkan ruang nama ke kelas. Ruang nama disediakan oleh paket simbol dan tidak tergantung pada kelas. Anda perlu mengarahkan kembali pendekatan Anda ke pemrograman berorientasi objek jika Anda menggunakan CLOS.
Gunakan kompiler Lisp. Ini memberi tahu Anda tentang variabel tidak dikenal, mungkin memiliki rekomendasi gaya (tergantung pada kompiler yang digunakan), memberikan petunjuk efisiensi, ... Kompiler adalah keystroke jauh.
Lisps komersial seperti LispWorks dan Allegro CL menawarkan hal serupa di bawah Windows.
Lisps komersial seperti LispWorks dan Allegro CL menawarkan semua itu di bawah Windows.
sumber
Rainier Joswig mengatakan itu yang terbaik.
Bagi saya, kekuatan Lisp tidak dapat dipahami hanya dengan melihat daftar fitur Lisp. Untuk Lisp, dan Common Lisp pada khususnya, keseluruhan lebih besar dari jumlah bagian. Bukan hanya REPL plus s-expressions plus macro plus multi-method dispatch plus closures plus paket plus CLOS plus restart plus X, Y dan Z. Ini seluruh shebang yang semuanya terintegrasi dengan cerdas. Ini adalah pengalaman sehari-hari yang sangat sangat berbeda dari pengalaman sehari-hari dari bahasa pemrograman lain.
Lisp terlihat berbeda, digunakan secara berbeda, seseorang mendekati pemrograman dengan cara yang berbeda saat menggunakannya. Elegan, lengkap, besar dan mulus. Ini bukan tanpa cacat dan peccadillo sendiri. Tentu saja ada perbandingan dengan bahasa lain yang mungkin dibuat di mana itu mungkin tidak keluar di depan. Tapi sama sekali tidak Lisp hanya bahasa X ditambah REPL dan makro, atau bahasa Y ditambah pengiriman multi-metode dan restart.
sumber
Biasanya, makro harus digunakan untuk sesuatu yang tidak dapat diungkapkan sebagai pemanggilan fungsi. Saya tidak tahu apakah ada conditional like switch di C # (mirip dengan yang ada di C, seperti switch (form) {case ...}), tetapi mari kita asumsikan demikian dan telah dekat dengan batasan yang sama ( membutuhkan tipe integral).
Sekarang, mari kita anggap lebih jauh bahwa Anda ingin sesuatu yang terlihat seperti itu, tetapi mengirimkan string. Dalam lisp (well, khususnya di Common Lisp dan mungkin di orang lain), itu adalah "hanya" makro dan jika Anda membatasi pengguna untuk memiliki string konstan (mungkin tidak sulit) untuk dicocokkan, Anda dapat (waktu kompilasi) menghitung urutan minimal tes untuk menentukan kasus apa yang cocok (atau menggunakan tabel hash, menyimpan penutupan, mungkin).
Meskipun dimungkinkan untuk menyatakan bahwa sebagai fungsi (perbandingan string, daftar kasus dan penutupan untuk dieksekusi), itu mungkin tidak akan terlihat seperti "kode normal" dan di situlah makro lisp memiliki kemenangan yang pasti. Anda mungkin telah menggunakan beberapa macro atau bentuk khusus taht yang makro-seperti, seperti
defun
,defmacro
,setf
,cond
,loop
dan banyak lagi.sumber
Ini adalah artikel penjelasan terbaik yang saya temukan yang membawa pembaca dari permukaan advokasi Lisp untuk menunjukkan beberapa implikasi yang lebih dalam dari konsep yang digunakan:
http://www.defmacro.org/ramblings/lisp.html
Saya ingat pernah membaca ide di tempat lain: Bahasa lain telah mengadopsi berbagai fitur Lisp; pengumpulan sampah, pengetikan dinamis, fungsi anonim, penutupan untuk menghasilkan C ++ / C / Algol yang lebih baik. Namun untuk mendapatkan kekuatan penuh Lisp Anda memerlukan semua fitur penting, di mana Anda memiliki dialek lain Lisp, keluarga bahasa yang berbeda yang telah ada dalam satu bentuk atau lainnya selama lebih dari 50 tahun.
sumber