Apakah ada bahasa fungsional yang memungkinkan untuk menggunakan semantik stack - penghancuran deterministik otomatis di akhir ruang lingkup?
Apakah ada bahasa fungsional yang memungkinkan untuk menggunakan semantik stack - penghancuran deterministik otomatis di akhir ruang lingkup?
Jawaban:
Bukan yang saya tahu, meskipun saya bukan ahli pemrograman fungsional.
Tampaknya pada prinsipnya cukup sulit, karena nilai yang dikembalikan dari fungsi dapat berisi referensi ke nilai lain yang dibuat (di stack) dalam fungsi yang sama, atau mungkin dengan mudah diteruskan sebagai parameter, atau direferensikan oleh sesuatu yang diteruskan ke dalam sebagai parameter. Dalam C, masalah ini ditangani dengan memungkinkan bahwa pointer menggantung (atau lebih tepatnya, perilaku tidak terdefinisi) dapat terjadi jika programmer tidak melakukan sesuatu dengan benar. Itu bukan jenis solusi yang disetujui oleh desainer bahasa fungsional.
Ada beberapa solusi potensial. Satu ide adalah menjadikan nilai seumur hidup sebagai bagian dari jenis nilai, bersama dengan referensi untuknya, dan menetapkan aturan berbasis tipe yang mencegah nilai yang dialokasikan stack dikembalikan, atau direferensikan oleh sesuatu yang dikembalikan dari, suatu fungsi. Saya belum menyelesaikan implikasinya, tetapi saya curiga ini akan mengerikan.
Untuk kode monadik, ada solusi lain yang (sebenarnya atau hampir) monadik juga, dan dapat memberikan semacam IORef yang hancur secara deterministik secara otomatis. Prinsipnya adalah mendefinisikan tindakan "bersarang". Ketika digabungkan (menggunakan operator asosiatif), ini menentukan aliran kontrol bersarang - saya pikir "elemen XML", dengan nilai paling kiri memberikan pasangan tag awal dan akhir. "Tag XML" ini hanya mendefinisikan urutan tindakan monadik pada tingkat abstraksi lain.
Pada titik tertentu (di sisi kanan rantai komposisi asosiatif) Anda memerlukan semacam terminator untuk mengakhiri sarang - sesuatu untuk mengisi lubang di tengah. Kebutuhan akan terminator adalah apa yang mungkin berarti operator komposisi bersarang tidak monadik, sekali lagi, saya tidak sepenuhnya yakin karena saya belum mengerjakan detailnya. Seperti yang diterapkan semua terminator adalah mengubah tindakan bersarang menjadi efektif tindakan monadik normal, mungkin tidak - itu tidak selalu mempengaruhi operator komposisi bersarang.
Banyak dari tindakan khusus ini akan memiliki langkah "tag-akhir" nol, dan akan menyamakan langkah "tag awal" dengan beberapa tindakan monadik sederhana. Tetapi beberapa akan mewakili deklarasi variabel. Ini akan mewakili konstruktor dengan tag-awal, dan destruktor dengan tag-akhir. Jadi Anda mendapatkan sesuatu seperti ...
act = terminate ((def-var "hello" ) >>>= \h ->
(def-var " world") >>>= \w ->
(use-val ((get h) ++ (get w)))
)
Menerjemahkan ke komposisi monadik dengan urutan eksekusi berikut, setiap tag (bukan elemen) menjadi tindakan monadik yang normal ...
<def-var val="hello"> -- construction
<def-var val=" world> -- construction
<use-val ...>
<terminator/>
</use-val> -- do nothing
</def-val> -- destruction
</def-val> -- destruction
Aturan seperti ini dapat memungkinkan C ++ - style RAII diimplementasikan. Referensi seperti IORef tidak dapat lepas dari jangkauannya, karena alasan yang sama dengan alasan mengapa IORef normal tidak dapat lepas dari monad - aturan komposisi asosiatif tidak memberikan jalan bagi referensi untuk melarikan diri.
Suntingan - Saya hampir lupa mengatakan - ada area yang pasti saya tidak yakin di sini. Sangat penting untuk memastikan bahwa variabel luar tidak bisa mereferensikan variabel dalam, jadi harus ada batasan yang dapat Anda lakukan dengan referensi seperti IORef ini. Sekali lagi, saya belum mengerjakan semua detailnya.
Oleh karena itu, konstruksi dapat mis membuka file yang menutup kerusakan. Konstruksi dapat membuka soket yang menutup kerusakan. Pada dasarnya, seperti dalam C ++, variabel menjadi manajer sumber daya. Tetapi tidak seperti C ++, tidak ada objek yang dialokasikan heap yang tidak dapat secara otomatis dihancurkan.
Meskipun struktur ini mendukung RAII, Anda masih memerlukan pengumpul sampah. Meskipun tindakan bersarang dapat mengalokasikan dan membebaskan memori, memperlakukannya sebagai sumber daya, masih ada semua referensi nilai fungsional (berpotensi dibagi) di dalam memori tersebut dan di tempat lain. Mengingat bahwa memori dapat dengan mudah dialokasikan pada tumpukan, menghindari kebutuhan akan tumpukan bebas, arti sebenarnya (jika ada) adalah untuk jenis manajemen sumber daya lainnya.
Jadi yang dicapai adalah untuk memisahkan manajemen sumber daya gaya RAII dari manajemen memori, setidaknya dalam kasus di mana RAII didasarkan pada ruang lingkup bersarang yang sederhana. Anda masih memerlukan pengumpul sampah untuk manajemen memori, tetapi Anda mendapatkan pembersihan deterministik otomatis yang aman dan tepat waktu dari sumber daya lainnya.
shared_ptr<>
), Anda masih menyimpan kehancuran deterministik. Satu hal yang rumit untuk RAII adalah referensi siklik; RAII berfungsi dengan baik jika grafik kepemilikannya adalah Grafik Acyclic yang Diarahkan.
Jika Anda menganggap C ++ sebagai bahasa fungsional (memiliki lambdas), maka itu adalah contoh bahasa yang tidak menggunakan pengumpulan sampah.
Saya harus mengatakan bahwa pertanyaannya agak tidak jelas karena mengasumsikan bahwa ada kumpulan standar "bahasa fungsional". Hampir setiap bahasa pemrograman mendukung sejumlah pemrograman fungsional. Dan, hampir setiap bahasa pemrograman mendukung sejumlah pemrograman imperatif. Di mana orang menarik garis untuk mengatakan yang merupakan bahasa fungsional dan yang merupakan bahasa imperatif, selain dipandu oleh prasangka budaya dan dogma populer?
Cara yang lebih baik untuk mengutarakan pertanyaannya adalah, "apakah mungkin untuk mendukung pemrograman fungsional dalam memori yang dialokasikan dengan tumpukan". Jawabannya, seperti yang telah disebutkan, sangat sulit. Gaya pemrograman fungsional mempromosikan alokasi struktur data rekursif sesuai keinginan, yang membutuhkan memori tumpukan (apakah sampah dikumpulkan atau referensi dihitung). Namun, ada teknik analisis kompiler yang cukup canggih yang disebut analisis memori berbasis wilayah dimana kompiler dapat membagi tumpukan menjadi blok besar yang dapat dialokasikan dan didelokasi secara otomatis, dengan cara yang mirip dengan alokasi stack. Halaman Wikipedia mencantumkan berbagai implementasi teknik, untuk bahasa "fungsional" dan "imperatif".