Haskell, Lisp, dan verbosity [tertutup]

104

Bagi Anda yang berpengalaman di Haskell dan beberapa rasa Lisp, saya penasaran betapa "menyenangkan" (menggunakan istilah yang mengerikan) untuk menulis kode di Haskell vs. Lisp.

Beberapa latar belakang: Saya belajar Haskell sekarang, setelah sebelumnya bekerja dengan Scheme dan CL (dan sedikit terjun ke Clojure). Secara tradisional, Anda dapat menganggap saya penggemar bahasa dinamis karena kesederhanaan dan kecepatan yang mereka sediakan. Saya segera jatuh cinta dengan makro Lisp, karena itu memberi saya cara lain untuk menghindari verbositas dan boilerplate.

Saya menemukan Haskell sangat menarik, karena memperkenalkan saya pada cara-cara pengkodean yang saya tidak tahu ada. Ini pasti memiliki beberapa aspek yang sepertinya akan membantu dalam mencapai ketangkasan, seperti kemudahan menulis fungsi parsial. Namun, saya agak khawatir tentang kehilangan makro Lisp (saya berasumsi saya kehilangan mereka; sejujurnya saya mungkin belum mempelajarinya?) Dan sistem pengetikan statis.

Adakah orang yang telah melakukan sejumlah pengkodean yang layak di kedua dunia keberatan berkomentar tentang bagaimana pengalaman berbeda, mana yang Anda sukai, dan jika preferensi tersebut bersifat situasional?

J Cooper
sumber

Jawaban:

69

Jawaban singkat:

  • hampir semua hal yang dapat Anda lakukan dengan makro dapat Anda lakukan dengan fungsi tingkat tinggi (dan saya menyertakan monad, panah, dll.), tetapi mungkin memerlukan lebih banyak pemikiran (tetapi hanya untuk pertama kali, dan itu menyenangkan dan Anda akan menjadi programmer yang lebih baik untuk itu), dan
  • sistem statis cukup umum sehingga tidak pernah menghalangi jalan Anda, dan agak mengejutkan itu sebenarnya "membantu mencapai ketangkasan" (seperti yang Anda katakan) karena ketika program Anda dikompilasi, Anda hampir dapat yakin bahwa itu benar, jadi kepastian ini memungkinkan Anda mencoba keluar hal-hal yang mungkin Anda takut untuk mencoba - ada nuansa "dinamis" untuk pemrograman meskipun tidak sama dengan Lisp.

[Catatan: Ada " Template Haskell " yang memungkinkan Anda menulis makro seperti di Lisp, tetapi sebenarnya Anda tidak akan membutuhkannya .]

ShreevatsaR
sumber
15
Dari Conor McBride, dikutip oleh Don Stewart: 'Saya suka menganggap tipe sebagai membelokkan gravitasi kita, sehingga arah yang perlu kita tempuh [untuk menulis program yang benar] menjadi "menurun".' Sistem tipe membuatnya sangat mudah untuk menulis program yang benar ... lihat posting ini dan pembagian ulangnya.
ShreevatsaR
3
Fungsi orde tinggi tidak dapat menggantikan makro, dan faktanya, CL memiliki keduanya karena beberapa alasan. Kekuatan sebenarnya dari makro di CL adalah memungkinkan pengembang untuk memperkenalkan fitur bahasa baru yang membantu mengungkapkan solusi masalah dengan lebih baik, tanpa harus menunggu versi baru dari bahasa seperti di Haskell atau Java. Misalnya, jika Haskell memiliki kekuatan ini, penulis Haskell tidak perlu menulis ekstensi GHC, apakah ekstensi tersebut dapat diterapkan oleh pengembang sendiri sebagai makro kapan saja.
mljrg
@mljrg Apakah Anda memiliki contoh konkret? Lihat komentar pada jawaban Hibou57 di bawah ini di mana contoh yang dituduhkan ternyata meragukan. Saya tertarik untuk mengetahui hal-hal yang Anda maksud (misalnya kode Haskell dengan dan tanpa makro).
ShreevatsaR
4
Keluarkan kari dari Haskell. Bisakah Anda menerapkannya dengan apa yang tersisa di Haskell? Contoh lain: misalkan Haskell tidak mendukung pencocokan pola, dapatkah Anda menambahkannya sendiri tanpa harus didukung oleh pengembang GHC? Di CL, Anda dapat menggunakan makro untuk memperluas bahasa sesuka Anda. Saya kira itu sebabnya CL bahasanya tidak berubah sejak standarnya di tahun 90-an, sedangkan Haskell tampaknya memiliki fluks ekstensi yang tidak pernah berakhir di GHC.
mljrg
64

Pertama-tama, jangan khawatir kehilangan fitur tertentu seperti pengetikan dinamis. Karena Anda sudah familiar dengan Common Lisp, bahasa yang dirancang dengan sangat baik, saya berasumsi Anda sadar bahwa bahasa tidak dapat direduksi menjadi kumpulan fiturnya. Ini semua tentang keseluruhan yang koheren, bukan?

Dalam hal ini, Haskell bersinar terang seperti halnya Common Lisp. Fitur-fiturnya digabungkan untuk memberi Anda cara pemrograman yang membuat kode menjadi sangat pendek dan elegan. Kurangnya makro diatasi dengan konsep yang lebih rumit (tetapi, juga, lebih sulit untuk dipahami dan digunakan) seperti monad dan panah. Sistem tipe statis menambah kekuatan Anda daripada menghalangi Anda seperti pada kebanyakan bahasa berorientasi objek.

Di sisi lain, pemrograman di Haskell jauh kurang interaktif daripada Lisp, dan jumlah refleksi yang luar biasa dalam bahasa seperti Lisp tidak sesuai dengan pandangan statis dunia yang diandaikan Haskell. Oleh karena itu, set alat yang tersedia untuk Anda sangat berbeda antara kedua bahasa tersebut, tetapi sulit untuk dibandingkan satu sama lain.

Saya pribadi lebih suka cara pemrograman Lisp secara umum, karena saya merasa itu cocok dengan cara saya bekerja lebih baik. Namun, ini tidak berarti Anda harus melakukannya juga.

Matthias Benkard
sumber
4
Bisakah Anda menjelaskan lebih lanjut tentang "pemrograman di Haskell jauh lebih tidak interaktif". Bukankah GHCi benar-benar menyediakan semua yang Anda butuhkan?
Johannes Gerer
3
@JohannesGerer: Saya belum mencobanya, tetapi sejauh yang saya baca, GHCi bukanlah shell ke dalam gambar yang sedang berjalan, di mana Anda dapat mendefinisikan ulang dan memperluas bagian sembarang dari seluruh program saat sedang berjalan. Selain itu, sintaks Haskell mempersulit penyalinan fragmen program antara repl dan editor secara terprogram.
Svante
13

Ada lebih sedikit kebutuhan untuk metaprogramming di Haskell daripada di Common Lisp karena banyak yang dapat disusun di sekitar monad dan sintaks yang ditambahkan membuat DSL yang disematkan terlihat kurang seperti pohon, tetapi selalu ada Template Haskell, seperti yang disebutkan oleh ShreevatsaR , dan bahkan Liskell (semantik Haskell + Lisp sintaks) jika Anda menyukai tanda kurung.

Herrmann
sumber
1
link Liskell sudah mati, tapi sekarang ada Hackett .
Will Ness
10

Mengenai makro, berikut adalah halaman yang membahasnya: Halo Haskell, Selamat tinggal Lisp . Ini menjelaskan sudut pandang di mana makro tidak diperlukan di Haskell. Itu datang dengan contoh singkat untuk perbandingan.

Contoh kasus di mana makro LISP diperlukan untuk menghindari evaluasi kedua argumen:

(defmacro doif (x y) `(if ,x ,y))

Contoh kasus di mana Haskell tidak mengevaluasi kedua argumen secara sistematis, tanpa memerlukan definisi makro:

doif x y = if x then (Just y) else Nothing

Dan voila

Hibou57
sumber
17
Itu kesalahpahaman yang umum. Ya, di Haskell kemalasan berarti Anda tidak memerlukan makro saat Anda ingin menghindari evaluasi beberapa bagian ekspresi, tetapi itu hanya subset paling sepele dari semua penggunaan makro. Google untuk "The Swine Before Perl" untuk ceramah yang mendemonstrasikan makro yang tidak dapat dilakukan dengan kemalasan. Juga, jika Anda tidak ingin beberapa bit menjadi ketat, maka Anda tidak bisa melakukan itu sebagai fungsi - mirroring fakta bahwa Skema ini delaytidak bisa menjadi fungsi.
Eli Barzilay
8
@Eli Barzilay: Saya tidak menemukan contoh ini sangat meyakinkan. Berikut adalah terjemahan slide 40 Haskell yang lengkap dan sederhana: pastebin.com/8rFYwTrE
Reid Barton
5
@Eli Barzilay: Saya sama sekali tidak mengerti tanggapan Anda. accept adalah (E) DSL. The acceptFungsi adalah analog dari makro diuraikan pada halaman sebelumnya, dan definisi vpersis sejajar dengan definisi vdalam Skema pada slide 40. Haskell dan Skema fungsi menghitung hal yang sama dengan strategi evaluasi yang sama. Makro memungkinkan Anda untuk mengekspos lebih banyak struktur program Anda ke pengoptimal. Anda hampir tidak dapat mengklaim ini sebagai contoh di mana makro meningkatkan kekuatan ekspresif bahasa dengan cara yang tidak direplikasi oleh evaluasi malas.
Reid Barton
5
@Eli Barzilay: Dalam Skema malas hipotetis, Anda dapat menulis ini: pastebin.com/TN3F8VVE Klaim umum saya adalah bahwa makro ini membeli Anda sangat sedikit: sintaks yang sedikit berbeda dan waktu yang lebih mudah untuk pengoptimal (tetapi tidak masalah untuk sebuah "kompiler yang cukup cerdas"). Sebagai gantinya, Anda telah menjebak diri Anda sendiri ke dalam bahasa yang tidak ekspresif; bagaimana Anda mendefinisikan robot yang cocok dengan huruf apa pun tanpa mencantumkan semuanya? Juga, saya tidak tahu apa yang Anda maksud dengan "menggunakannya di semua sub-daftar" atau "penggunaan tempat yang diperlukan dengan cakupannya sendiri".
Reid Barton
15
OK, saya menyerah. Rupanya definisi Anda tentang DSL adalah "argumen ke makro" dan jadi contoh Skema malas saya bukanlah DSL, meskipun secara sintaksis isomorfik ke aslinya ( automatonmenjadi letrec, :menjadi accept, ->menjadi tidak ada dalam versi ini). Masa bodo.
Reid Barton
9

Saya seorang programmer Common Lisp.

Setelah mencoba Haskell beberapa waktu lalu, inti pribadi saya adalah tetap menggunakan CL.

Alasan:

Haskell tentu saja memiliki kelebihannya sendiri dan melakukan beberapa hal dengan cara yang berbeda secara fundamental, tetapi bagi saya tidak berhasil dalam jangka panjang.

Leslie P. Polzer
sumber
Hei, apakah Anda kebetulan memiliki judul makalah Costanza yang Anda tautkan? Sepertinya file itu telah dipindahkan.
michiakig
2
Perhatikan bahwa haskell juga mendukung sintaks prefiks tetapi saya akan mengatakan bahwa monad >> = akan sangat jelek menggunakannya. Juga saya tidak setuju dengan ketidakmurnian menjadi berkah: P
alternatif
2
Saya suka catatan samping ini: Kami belum mengumpulkan data empiris apakah masalah ini menyebabkan masalah serius dalam program dunia nyata.
Jerome Baum
22
Tak satu pun dari contoh dalam makalah itu (Pascal Costanza, Dynamic vs. Static Typing - A Pattern-Based Analysis ) berlaku untuk Haskell. Semuanya khusus untuk Java (atau lebih tepatnya, khusus untuk "pemrograman berorientasi objek" ) dan saya tidak dapat melihat salah satu masalah tersebut muncul di Haskell. Demikian pula, semua argumen Anda yang lain masih dapat diperdebatkan: orang juga dapat mengatakan Haskell "murni dan karenanya lebih cocok untuk pembuatan prototipe cepat", sintaks awalan itu tidak wajib, bahwa ia tidak memiliki berbagai kompiler yang melakukan hal berbeda , dll.
ShreevatsaR
11
Makalah itu memang hampir sepenuhnya tidak relevan dengan Haskell. " dilbert = dogbert.hire(dilbert);" ?? Saya ragu banyak programmer Haskell bahkan dapat membaca ini tanpa bergerak sedikitpun.
kiri sekitar sekitar
6

Di Haskell Anda bisa mendefinisikan fungsi if, yang tidak mungkin dilakukan di LISP. Hal ini dimungkinkan karena kemalasan, yang memungkinkan adanya lebih banyak modularitas dalam program. Makalah klasik ini: Mengapa FP penting oleh John Hughes, menjelaskan bagaimana kemalasan meningkatkan komposabilitas.

luntain
sumber
5
Skema (salah satu dari dua dialek LISP utama) sebenarnya memiliki evaluasi malas, meskipun tidak default seperti di Haskell.
Randy Voet
7
(defmacro doif (xy) `(if, x, y))
Joshua Cheek
4
Sebuah makro adalah tidak sama dengan fungsi - macro tidak bekerja dengan baik dengan tingkat tinggi fungsi seperti fold, misalnya sedangkan fungsi non-ketat lakukan .
Tikhon Jelvis
5

Ada hal-hal keren yang bisa Anda capai di Lisp dengan makro yang rumit (jika mungkin) di Haskell. Ambil contoh makro `memoize '(lihat Bab 9 dari PAIP Peter Norvig). Dengan itu, Anda dapat mendefinisikan sebuah fungsi, katakanlah foo, dan kemudian cukup evaluasi (memoize 'foo), yang menggantikan definisi global foo dengan versi memo. Bisakah Anda mencapai efek yang sama di Haskell dengan fungsi tingkat tinggi?


sumber
3
Tidak cukup (AFAIK), tetapi Anda dapat melakukan sesuatu yang serupa dengan memodifikasi fungsi (dengan asumsi ini rekursif) untuk mengambil fungsi untuk memanggil secara rekursif sebagai parameter (!) Daripada hanya memanggil dirinya sendiri dengan nama: haskell.org/haskellwiki/Memoization
j_random_hacker
6
Anda bisa menambahkan foo ke lazy data-structure, di mana nilainya akan disimpan setelah dihitung. Ini akan secara efektif sama.
Theo Belaire
Semua yang ada di Haskell di-memoasi dan mungkin sebaris jika diperlukan secara default oleh compiler Haskell.
aoeu256
4

Saat saya melanjutkan perjalanan pembelajaran Haskell, tampaknya satu hal yang membantu "menggantikan" makro adalah kemampuan untuk menentukan operator infix Anda sendiri dan menyesuaikan prioritas dan asosiatifnya. Agak rumit, tapi sistemnya menarik!

J Cooper
sumber