Pemrograman Asinkron dalam Bahasa Fungsional

31

Saya kebanyakan adalah seorang programmer C / C ++, yang berarti bahwa mayoritas pengalaman saya adalah dengan paradigma prosedural dan berorientasi objek. Namun, seperti yang diketahui oleh banyak programmer C ++, C ++ telah bergeser dalam penekanan selama bertahun-tahun ke gaya fungsional-esque, yang akhirnya memuncak dengan penambahan lambdas dan penutupan di C ++ 0x.

Bagaimanapun juga, walaupun saya memiliki banyak pengalaman pengkodean dalam gaya fungsional menggunakan C ++, saya memiliki sedikit pengalaman dengan bahasa fungsional aktual seperti Lisp, Haskell, dll.

Saya baru-baru ini mulai mempelajari bahasa-bahasa ini, karena gagasan "tidak ada efek samping" dalam bahasa yang murni fungsional selalu menggelitik saya, terutama yang berkaitan dengan penerapannya pada konkurensi dan komputasi terdistribusi.

Namun, berasal dari latar belakang C ++ saya bingung bagaimana ini "tidak ada efek samping" bekerja dengan pemrograman asinkron. Dengan pemrograman asinkron, yang saya maksudkan adalah kerangka / API / gaya pengkodean yang mengirim pengendali event yang disediakan pengguna untuk menangani peristiwa yang terjadi secara tidak sinkron (di luar alur program.) Ini termasuk pustaka asinkron seperti Boost.ASIO, atau bahkan C biasa saja penangan sinyal atau penangan event Java GUI.

Satu hal yang sama-sama dimiliki oleh semua adalah bahwa sifat pemrograman asinkron tampaknya membutuhkan penciptaan efek samping (keadaan), agar aliran utama program menjadi sadar bahwa event handler asinkron telah dijalankan. Biasanya, dalam kerangka kerja seperti Boost.ASIO, pengendali acara mengubah keadaan suatu objek, sehingga efek dari acara tersebut diperbanyak di luar masa pakai fungsi event handler. Sungguh, apa lagi yang bisa dilakukan oleh seorang pawang? Itu tidak dapat "mengembalikan" nilai ke titik panggilan, karena tidak ada titik panggilan. Event handler bukan bagian dari aliran utama program, jadi satu-satunya cara ia dapat memiliki efek pada program yang sebenarnya adalah mengubah beberapa status (atau yang lain longjmpke titik eksekusi lain).

Jadi tampaknya pemrograman asinkronik adalah tentang efek samping yang tidak sinkron. Ini tampaknya sangat bertentangan dengan tujuan pemrograman fungsional. Bagaimana kedua paradigma ini direkonsiliasi (dalam praktiknya) dalam bahasa fungsional?

Charles Salvia
sumber
3
Wow, saya baru saja akan menulis pertanyaan seperti ini dan tidak tahu bagaimana memasukkannya dan kemudian melihat ini di saran!
Amogh Talpallikar

Jawaban:

11

Semua logika Anda masuk akal, kecuali bahwa saya pikir pemahaman Anda tentang pemrograman fungsional agak terlalu ekstrem. Dalam pemrograman fungsional dunia nyata, seperti pemrograman berorientasi objek, atau imperatif adalah tentang pola pikir dan bagaimana Anda mendekati masalah. Anda masih dapat menulis program dalam semangat pemrograman fungsional sambil memodifikasi keadaan aplikasi.

Bahkan, Anda harus mengubah status aplikasi untuk benar - benar melakukan apa pun. Orang-orang Haskell akan memberitahu Anda program mereka 'murni' karena mereka membungkus semua perubahan negara mereka dalam monad. Namun, program mereka masih berinteraksi dengan dunia luar. (Kalau tidak apa intinya!)

Penekanan pemrograman fungsional "tidak ada efek samping" ketika masuk akal. Namun, untuk melakukan pemrograman dunia nyata, seperti yang Anda katakan, Anda perlu mengubah keadaan dunia. (Misalnya, merespons acara, menulis ke disk, dan sebagainya.)

Untuk informasi lebih lanjut tentang pemrograman asinkron dalam bahasa fungsional, saya sangat menyarankan Anda untuk melihat ke dalam model pemrograman Alynchronous Workflows F # . Hal ini memungkinkan Anda untuk menulis program fungsional sambil menyembunyikan semua detail berantakan dari transisi utas dalam perpustakaan. (Dengan cara yang sangat mirip dengan monad gaya Haskell.)

Jika 'badan' utas hanya menghitung nilai, maka memunculkan banyak utas dan meminta mereka menghitung nilai secara paralel masih dalam paradigma fungsional.

Chris Smith
sumber
5
Juga: melihat Erlang membantu. Bahasa ini sangat sederhana, murni (semua data tidak dapat diubah), dan semuanya tentang pemrosesan yang tidak sinkron.
9000
Pada dasarnya setelah memahami manfaat dari pendekatan fungsional dan mengubah keadaan hanya ketika itu penting secara otomatis akan memastikan bahwa bahkan jika Anda bekerja mengatakan sesuatu seperti Java, Anda tahu kapan harus memodifikasi keadaan dan bagaimana menjaga hal-hal seperti di kontrol.
Amogh Talpallikar
Tidak setuju - fakta bahwa program terdiri dari fungsi 'murni' tidak berarti bahwa ia tidak berinteraksi dengan dunia luar, itu berarti, bahwa setiap fungsi dalam program untuk satu set argumen akan selalu mengembalikan hasil yang sama, dan ini adalah (Kemurnian) masalah besar, karena, dari sudut pandang praktis - program seperti itu akan kurang buggy, lebih 'diuji', pelaksanaan fungsi yang sukses dapat dibuktikan secara matematis.
Gill Bates
8

Ini pertanyaan yang menarik. Menurut saya, yang paling menarik adalah pendekatan yang diadopsi di Clojure dan dijelaskan dalam video ini:

http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey

Pada dasarnya "solusi" yang diajukan adalah sebagai berikut:

  • Anda menulis sebagian besar kode Anda sebagai fungsi "murni" klasik dengan struktur data tidak berubah dan tanpa efek samping
  • Efek samping diisolasi melalui penggunaan referensi terkelola yang mengontrol subjek perubahan dengan aturan memori transaksional perangkat lunak (yaitu semua pembaruan Anda ke keadaan dapat berubah terjadi dalam transaksi terisolasi yang tepat)
  • Jika Anda mengambil pandangan dunia ini, Anda dapat melihat "peristiwa" yang tidak sinkron sebagai pemicu untuk pembaruan transaksional dari keadaan yang bisa berubah di mana pembaruan itu sendiri merupakan fungsi murni.

Saya mungkin tidak mengungkapkan ide sejelas yang telah dilakukan orang lain, tapi saya harap ini memberikan ide umum - pada dasarnya ini menggunakan sistem STM bersamaan untuk menyediakan "jembatan" antara pemrograman fungsional murni dan penanganan acara yang tidak sinkron.

mikera
sumber
6

Satu catatan: bahasa fungsional murni, tetapi runtime tidak.

Sebagai contoh, runtime Haskell melibatkan antrian, multiplexing utas, pengumpulan sampah, dll ... yang semuanya tidak murni.

Contoh yang baik adalah kemalasan. Haskell mendukung evaluasi malas (itu adalah standarnya, sebenarnya). Anda membuat nilai malas dengan menyiapkan operasi, Anda kemudian dapat membuat banyak salinan dari nilai ini, dan itu masih "malas" selama itu tidak diperlukan. Ketika hasilnya diperlukan, atau jika runtime menemukan waktu, nilai sebenarnya dihitung, dan keadaan objek malas berubah untuk mencerminkan bahwa itu tidak lagi diperlukan untuk melakukan perhitungan (sekali lagi) untuk mendapatkan hasilnya. Sekarang tersedia melalui semua referensi, sehingga keadaan objek telah berubah, meskipun itu adalah bahasa murni.

Matthieu M.
sumber
2

Saya bingung bagaimana ini "tidak ada efek samping" philsophy bekerja dengan pemrograman asinkron. Maksud saya dengan pemrograman asinkron ...

Itu intinya, kalau begitu.

Gaya suara, tanpa efek samping tidak kompatibel dengan kerangka kerja yang bergantung pada kondisi. Temukan kerangka kerja baru.

Standar WSGI Python, misalnya, memungkinkan kita membuat aplikasi tanpa efek samping.

Idenya adalah bahwa berbagai "perubahan negara" tercermin oleh lingkungan nilai-nilai yang dapat dibangun secara bertahap. Setiap permintaan adalah pipa transformasi.

S.Lott
sumber
"memungkinkan membangun aplikasi efek samping" Saya pikir ada kata yang hilang di suatu tempat.
Christopher Mahan
1

Setelah mempelajari enkapsulasi dari Borland C ++ setelah mempelajari C, ketika Borland C ++ tidak memiliki templat yang memungkinkan generik, paradigma orientasi objek membuat saya tidak nyaman. Agak cara yang lebih alami untuk menghitung tampak menyaring data melalui pipa. Aliran keluar memiliki identitas yang terpisah dan independen dari aliran input ke dalam yang tidak dapat diubah, daripada dianggap sebagai efek samping, yaitu setiap sumber data (atau filter) terpisah dari yang lain. Keypress (contoh acara) membatasi kombinasi input pengguna asinkron ke kode kunci yang tersedia. Fungsi beroperasi pada argumen parameter input, dan negara yang dienkapsulasi oleh kelas hanyalah jalan pintas untuk menghindari secara eksplisit melewati argumen berulang antara subset fungsi yang kecil, selain bersikap hati-hati pada konteks terikat mencegah penyalahgunaan argumen-argumen tersebut dari fungsi sewenang-wenang.

Berpegang teguh pada paradigma tertentu menyebabkan ketidaknyamanan untuk berurusan dengan abstraksi yang bocor, misalnya. runtime komersial seperti JRE, DirectX, .net target berorientasi objek pendukung terutama. Untuk membatasi ketidaknyamanan ini, bahasa dapat memilih untuk monad yang canggih secara ilmiah seperti yang dilakukan Haskell, atau dukungan multi-paradigma yang fleksibel seperti yang akhirnya didapat oleh F #. Kecuali jika enkapsulasi berguna dari beberapa kasus penggunaan pewarisan berganda, pendekatan multi-paradigma mungkin merupakan alternatif yang lebih baik dari beberapa pola pemrograman spesifik-paradigma yang terkadang rumit.

Chawathe Vipul
sumber