Bahasa pemrograman fungsional murni? [Tutup]

20

Saya tertarik belajar pemrograman fungsional dengan lebih baik. Untuk melakukannya, tampak jelas bahwa saya harus memaksakan diri untuk menggunakan bahasa pemrograman fungsional semurni mungkin. Oleh karena itu, saya di sini meminta, kurang lebih, untuk pemesanan bahasa pemrograman fungsional sesuai dengan kemurniannya.

Sepertinya bagi saya bahwa akan lebih praktis untuk mempelajari Lisp atau Clojure (atau Skema, atau Scala, dll.), Tetapi untuk apa yang saya dengar baru-baru ini, Haskell akan sangat sulit dikalahkan dalam mengajarkan prinsip-prinsip pemrograman fungsional kepada seseorang. Saya belum yakin tentang ini, jadi saya bertanya kepada Anda: yang merupakan bahasa pemrograman fungsional paling murni? Suatu pemesanan akan bagus jika beberapa bersaing untuk mendapatkan gelar yang luar biasa dari bahasa pemrograman fungsional yang paling murni.

Joanis
sumber
2
Saya belajar Miranda di universitas, jadi saya dibiasakan, tetapi saya akan menyarankan Haskel kepada siapa pun yang ingin membenamkan diri dalam bahasa fungsional tanpa gangguan kenajisan . * 8 ')
Mark Booth
2
Setelah selesai mempelajari pemrograman fungsional, Anda juga harus mempelajari pemrograman yang diketik secara statis dengan sistem tipe ekspresif. Dalam kategori gabungan (fungsional dan ketik), saya sarankan: Coq> Haskell> OCaml> Scala> others. Ada beberapa alternatif yang kurang populer yang cocok antara Coq dan Haskell (seperti Epigram dan Agda). Haskell merindukan sistem modul ekspresif OCaml.
lukstafi

Jawaban:

28

Tidak ada skala untuk menilai tingkat kemurnian bahasa fungsional. Jika bahasa memungkinkan efek samping itu tidak murni, jika tidak murni. Dengan definisi ini, Haskell, Mercury, Clean dll adalah bahasa fungsional murni; sedangkan Scala, Clojure, F #, OCaml dll adalah yang tidak murni.

EDIT: Mungkin saya seharusnya mengatakan ini sebagai "jika bahasa tidak memungkinkan efek samping tanpa membiarkan tipe sistem tahu , itu murni. Kalau tidak, itu tidak murni.".

missingfaktor
sumber
6
Tidak cukup: Haskell memang memungkinkan efek samping ( IOmonad). Hanya saja kode penyebab efek samping ditandai dengan jelas seperti itu. Saya tidak berpikir itu berguna untuk berbicara dari / tidak murni murni bahasa sama sekali (Haskell memungkinkan Anda program imperatif! Terkesiap!) Tetapi masih dapat berguna untuk berbicara dari potongan kode (fungsi, modul, program apa pun) sebagai murni /najis.
Frank Shearar
8
@ Frank: Ya, Haskell memang memungkinkan efek samping, tetapi ia lebih dari sekadar menandai kode yang menyebabkan efek samping. Itu juga menjaga transparansi referensial bahkan ketika menggunakan kode yang menyebabkan efek samping dan itulah yang membuatnya murni - setidaknya dengan definisi kemurnian banyak orang. Tentu saja itu tidak sesuai dengan definisifaktor yang hilang tentang "tidak ada efek samping diizinkan".
sepp2k
4
@ Frank Shearar tetapi berguna untuk berbicara sedemikian rupa karena IOmonad itu murni. Ini adalah pustaka runtime yang bukan, bukan kode Anda, karena mainfungsi Anda pada dasarnya adalah transformator besar. (Sesuatu seperti di main :: World -> Worldbelakang layar)
alternatif
1
Bahasa saya juga memiliki transparansi referensial. Anda baru saja menulis program = "some C code", dan kemudian lingkungan runtime berhubungan dengan kode C. :-)
Daniel Lubarov
3
Seperti mencetak ke layar adalah efek samping program fungsional yang benar-benar murni sangat membosankan.
15

Karena belajar adalah tujuan Anda, dan bukan menulis program sendiri, Anda tidak bisa mendapatkan yang lebih murni dari Kalkulus Lambda .

Kalkulus Lambda ada sebelum komputer ditemukan. Butuh beberapa ahli logika yang terampil untuk mengatasinya untuk mengetahui bagaimana melakukan pengurangan (untuk sementara waktu diteorikan bahwa hanya penambahan dan perkalian yang mungkin dilakukan).

Belajar bagaimana booleans dan angka dan ifdapat ditemukan dari yang tampaknya tidak akan memasukkan lebih banyak gas ke tangki Anda, tetapi itu akan membuat tangki Anda jauh lebih besar.

Macneil
sumber
Memang, mungkin tidak ada yang lebih murni dari itu, tetapi kita terlalu banyak melewati penghalang matematika. Saya masih ingin berlatih pemrograman dan belajar bahasa baru sambil menyerap prinsip-prinsip fungsional. Padahal, saya setuju bahwa mungkin menarik untuk mempelajari lambda calculus hanya demi lebih memahami dasar-dasar paradigma fungsional (dan memiliki tangki yang lebih besar).
Joanis
2
Menulis bukti adalah menulis program dan menulis program menulis bukti. Ketika Anda mempelajari tentang isomorfisma Curry – Howard, Anda akan menyadari bahwa Anda melewati penghalang matematika itu sepuluh ribu baris yang lalu.
Macneil
1
Tidak ada representasi sederhana -1.
Daniel Lubarov
2
@Mason Wheeler: λ-kalkulus saja tidak memiliki angka, atau benar-benar segala jenis data selain abstraksi lambda. Kecuali ditentukan sebaliknya, siapa pun yang berbicara tentang angka dalam kalkulus λ mungkin berarti pengkodean Gereja untuk bilangan asli , di mana angka n diwakili oleh komposisi fungsi n-lipat. Yang mengatakan, saya meragukan itu bahwa banyak perjuangan untuk mencari tahu pengurangan setelah seseorang benar-benar mencoba; Saya mengerjakannya sendiri di sore hari hanya dengan memberikan definisi penambahan dan pengetahuan bahwa pengurangan adalah mungkin.
CA McCann
1
Pengurangan menggunakan angka-angka Gereja mudah setelah Anda mengalami dekomposisi berdasarkan kasus. Membangun kalkulus (turunan) keseluruhan untuk mendukung angka negatif, lebih banyak pekerjaan.
Donal Fellows
6

Pada dasarnya, bahasa yang tidak murni tidak benar-benar berbeda dari bahasa imperatif yang lebih akrab, terutama sekarang karena banyak trik fungsional telah disalin. Yang berbeda adalah gaya - bagaimana Anda memecahkan masalah.

Apakah Anda menganggap Haskell sebagai murni, atau menganggap monad IO sebagai ketidakmurnian, gaya Haskell adalah bentuk ekstrim dari gaya ini dan layak untuk dipelajari.

Haskell IO Monad berasal dari teori matematika (tentu saja) monad. Namun, untuk programer yang penting, saya pikir cara mundur untuk tiba di monad lebih masuk akal.

Fase satu - bahasa fungsional murni dapat dengan mudah mengembalikan nilai string besar sebagai hasilnya. String besar ini dapat menjadi kode sumber dari program imperatif, diturunkan dengan cara fungsional murni dari beberapa parameter yang menentukan persyaratan. Anda kemudian dapat membangun kompilator "tingkat lebih tinggi" yang menjalankan pembuat kode Anda, lalu secara otomatis memasukkan kode yang dihasilkan ke kompiler bahasa imperatif.

Fase dua - daripada menghasilkan kode sumber tekstual, Anda menghasilkan pohon sintaksis yang sangat diketik. Kompiler bahasa imperatif Anda diserap ke dalam kompiler "level lebih tinggi" Anda, dan menerima AST secara langsung sebagai kode sumber. Ini jauh lebih dekat dengan apa yang Haskell lakukan.

Ini masih canggung. Misalnya Anda memiliki dua jenis fungsi yang berbeda - yang dievaluasi selama fase pembuatan kode dan yang dijalankan ketika program yang dihasilkan dijalankan. Ini agak seperti perbedaan antara fungsi dan template di C ++.

Jadi, untuk fase 3, buat keduanya sama - fungsi yang sama dengan sintaksis yang sama dapat dievaluasi sebagian selama "pembuatan kode", atau dievaluasi sepenuhnya, atau tidak dievaluasi sama sekali. Lebih lanjut, buang semua simpul AST looping yang mendukung rekursi. Bahkan, buang ide AST node sebagai jenis data khusus sama sekali - tidak memiliki AST "nilai literal", hanya punya nilai dll.

Inilah yang dilakukan oleh IO monad - operator pengikat adalah cara menyusun "tindakan" untuk membentuk program. Tidak ada yang istimewa - hanya fungsi. Banyak ekspresi dan fungsi dapat dievaluasi selama "pembuatan kode", tetapi yang bergantung pada efek samping I / O harus ditunda evaluasi sampai run-time - bukan oleh aturan khusus, tetapi sebagai konsekuensi alami dari ketergantungan data pada ekspresi.

Monads secara umum hanyalah generalisasi - mereka memiliki antarmuka yang sama, tetapi menerapkan operasi abstrak secara berbeda, jadi alih-alih mengevaluasi ke deskripsi kode imperatif mereka mengevaluasi ke sesuatu yang lain sebagai gantinya. Memiliki antarmuka yang sama berarti ada beberapa hal yang dapat Anda lakukan untuk monad tanpa peduli monad mana, yang ternyata bermanfaat.

Deskripsi ini tidak diragukan lagi akan membuat purists head meledak, tetapi bagi saya itu menjelaskan beberapa alasan sebenarnya mengapa Haskell menarik. Ini mengaburkan batas antara pemrograman dan metaprogramming, dan menggunakan alat pemrograman fungsional untuk menemukan kembali pemrograman imperatif tanpa memerlukan sintaksis khusus.

Kritik yang saya miliki tentang C ++ templates adalah bahwa mereka adalah semacam bahasa terjemahan fungsional yang rusak murni dalam bahasa imperatif - untuk mengevaluasi fungsi dasar yang sama pada waktu kompilasi daripada pada saat run-time Anda harus mengimplementasikannya kembali menggunakan gaya yang sama sekali berbeda pengkodean. Dalam Haskell, sementara kenajisan harus diberi label seperti itu dalam tipenya, fungsi yang sama persis dapat dievaluasi baik dalam arti meta-pemrograman dan dalam arti run-time non-meta-pemrograman dalam program yang sama - tidak ada garis keras antara pemrograman dan metaprogramming.

Yang mengatakan, ada beberapa hal metaprogramming yang Haskell standar tidak bisa lakukan, pada dasarnya karena tipe (dan mungkin beberapa hal lainnya) bukan nilai kelas Ada beberapa varian bahasa yang mencoba mengatasinya.

Banyak hal yang saya katakan tentang Haskell dapat diterapkan dalam bahasa fungsional yang tidak murni - dan kadang-kadang bahkan bahasa imperatif. Haskell berbeda karena Anda tidak punya pilihan selain mengambil pendekatan ini - pada dasarnya memaksa Anda untuk mempelajari gaya kerja ini. Anda dapat "menulis C dalam ML", tetapi Anda tidak dapat "menulis C di Haskell" - setidaknya tidak tanpa mengetahui apa yang terjadi di bawah tenda.

Steve314
sumber
Terima kasih! Saya bertanya-tanya apakah keumuman templat C ++ dapat disatukan ke dalam fungsinya sendiri. Sepertinya Haskell melakukan ini, tetapi bagian yang penting adalah apakah ini dapat diimplementasikan dalam bahasa yang dikompilasi , sehingga mesin yang sama membuat fungsi generik dari templat selama waktu kompilasi, juga dapat mengevaluasi mereka selama run-time ...
Milind R
5

Saya secara pribadi mengelompokkan bahasa dalam tiga tingkatan kemurnian fungsional:

  • Bahasa fungsional murni - yaitu bahasa yang memperlakukan seluruh program Anda sebagai fungsi murni dan menangani kemampuan berubah-ubah semata-mata melalui interaksi dengan runtime - Haskell mungkin merupakan contoh kanonik

  • Bahasa fungsional yang tidak murni - yaitu bahasa yang menekankan gaya fungsional tetapi memungkinkan efek samping. Clojure jelas dalam kategori ini (memungkinkan mutasi secara terkontrol sebagai bagian dari kerangka kerja STM-nya), juga OCaml atau F #

  • Bahasa multi-paradigma - ini bukan bahasa fungsional pertama dan terutama tetapi dapat mendukung gaya fungsional dengan menggunakan fungsi kelas satu dll. Scala adalah contoh yang baik di sini, saya juga meletakkan Common Lisp dalam kategori ini dan Anda bahkan bisa memasukkan bahasa seperti JavaScript .

Dalam situasi Anda, saya sarankan belajar Haskell dulu, lalu Clojure. Inilah yang saya lakukan dan itu bekerja dengan sangat baik untuk saya! Haskell cantik dan mengajarkan Anda prinsip-prinsip fungsional yang paling murni, Clojure jauh lebih pragmatis dan membantu Anda menyelesaikan banyak hal sementara masih sangat fungsional di hati.

Saya tidak benar-benar menghitung kategori ketiga sebagai bahasa fungsional (walaupun setelah mempelajari Haskell dan Clojure saya sering mendapati diri saya mengambil keuntungan dari teknik fungsional saat menggunakannya!)

mikera
sumber
Dalam batasan yang sangat ketat , bahkan C memiliki kemampuan fungsional. (Keterbatasan utama adalah pada sintesis runtime fungsi, yang benar-benar menghebohkan di C, begitu banyak sehingga kebanyakan orang mengklaim itu tidak dapat dilakukan.)
Donal Fellows
2
@Donal Fellows: Dalam batas ketika kekonyolan sampai tak terhingga, setiap bahasa Turing-lengkap fungsional: seseorang hanya perlu menerapkan interpreter Lisp dalam bahasa, dan kemudian menggunakannya. :]
CA McCann
Paradigma tidak ada artinya jika Anda mengambil sudut pandang bahwa semuanya Turing lengkap dan karena itu dapat meniru semua yang lain. Ketika Anda berpikir tentang paradigma, Anda harus fokus pada apa yang membuat bahasa idiomatik dan apa yang membuat / mencegah. Untuk menghitung sebagai bahasa fungsional dalam pandangan saya, mutasi dan efek samping harus tidak otomatis (untuk FP murni) atau dilarang (untuk FP murni). Itu berarti C tidak fungsional. Tidak ada bahasa multi-paradigma seperti Scala.
mikera
@mikera: Saya menemukan jawaban Anda sangat menarik: jika Scala tidak fungsional tetapi hanya multi-paradigma, bagaimana Anda menilai fitur pemrograman fungsional dalam C ++ 11, C #, atau bahkan Java (rencana pengenalan lambdas)?
Giorgio
@Iorgio: Saya pikir fitur fungsional dalam semua bahasa yang Anda sebutkan mungkin ide yang bagus, mereka tidak menjadikannya "bahasa fungsional". Atau untuk melihatnya dari arah yang berlawanan, fakta bahwa Anda dapat melakukan IO monadik gaya imperatif tidak menjadikan Haskell bahasa imperatif :-). Bahasa multi-paradigma pada dasarnya adalah hasil ketika Anda menyatukan fitur-fitur dari banyak paradigma yang berbeda dan tidak menempatkan gaya tertentu terlebih dahulu dan terutama. IMHO C ++, C # dan Java semuanya bergerak menuju multi-paradigma, tetapi belum ada di sana karena OOP berbasis kelas masih dominan.
mikera
3

Jika bahasa fungsional murni seperti itu, yang hanya memiliki fungsi murni (rutin yang tidak memiliki efek samping), maka itu sedikit tidak ada gunanya, karena tidak dapat membaca input atau menulis output;)

Karena ini benar-benar untuk belajar, saya pikir isolasi belum tentu yang cara untuk pergi. Pemrograman fungsional adalah sebuah paradigma. Penting untuk memahami paradigma mana yang cocok untuk masalah mana dan yang lebih penting, bagaimana mereka dapat dikombinasikan.

Saya akan mengatakannya sekarang: mode pemrograman itu bodoh dan kontraproduktif. Satu-satunya hal yang penting adalah program Anda pendek, mudah ditulis, mudah dipelihara, dan berfungsi dengan benar. Bagaimana Anda mencapai ini tidak ada hubungannya dengan mode pemrograman. - Richard Jones

Selain itu, jika Anda mencari "kemurnian", Anda mungkin ingin melihat Pure . Namun perlu dicatat, kemudahan memanggil rutinitas C membuatnya tidak jelas secara fungsional (tetapi juga sangat kuat).

back2dos
sumber
3

Bukan jawaban yang sepenuhnya serius, tetapi Unlambda harus menjadi penantang. Anda tidak bisa mendapatkan "fungsional murni" lebih dari kombinator SKI.

Stephen C
sumber
4
Kegilaan! Bidaah! Bahasa murni macam apa yang memiliki absurditas seperti kombinator dengan efek samping? Tidak tidak. Apa yang Anda benar-benar ingin di sini adalah Lazy K .
CA McCann
2

Erlang, Haskell, Skema, Scala, Clojure, dan F #

Pertanyaan ini mungkin akan membantu Anda dalam pencarian Anda juga .

programmer debu
sumber
Terima kasih, pertanyaan yang menarik memang. Saya sudah mulai dengan PLT-Skema / Raket, tapi saya belum melihat SICP ... Real World Haskell terlihat cukup menarik bagi saya juga.
Joanis
Skema mendorong gaya fungsional, tetapi memang memiliki set!(antara lain) ...
Daniel Lubarov
Saya menyarankan OCaml, karena ini adalah favorit saya. Dan ia memiliki sistem modul, yang Haskell dan F # lewatkan.
lukstafi
@ lukstafi mungkin haskell berubah dalam 3 tahun terakhir (saya tahu itu tumbuh seperti orang gila), tetapi pasti ada modul di haskell.
sara
@ Kai Dengan sistem modul, maksud saya modul diparameterisasi oleh modul lain ("lambda calculus" dari modul).
lukstafi