Saya dapat melihat manfaat dari objek yang tidak dapat diubah dan tidak dapat diubah seperti objek yang tidak dapat diubah menghilangkan banyak masalah yang sulit dalam pemrograman multi-threaded karena keadaan bersama dan dapat ditulis. Sebaliknya, objek yang bisa berubah membantu menangani identitas objek daripada membuat salinan baru setiap waktu dan dengan demikian juga meningkatkan kinerja dan penggunaan memori terutama untuk objek yang lebih besar.
Satu hal yang saya coba pahami adalah apa yang salah dengan memiliki objek yang bisa berubah dalam konteks pemrograman fungsional. Seperti salah satu poin yang diceritakan kepada saya adalah bahwa hasil fungsi panggilan dalam urutan yang berbeda tidak deterministik.
Saya mencari contoh nyata nyata di mana sangat jelas apa yang bisa salah menggunakan objek yang bisa berubah dalam pemrograman fungsi. Pada dasarnya jika itu buruk, itu buruk terlepas dari OO atau paradigma pemrograman fungsional, kan?
Saya percaya di bawah pernyataan saya sendiri menjawab pertanyaan ini. Tetapi saya masih membutuhkan beberapa contoh agar saya bisa merasakannya lebih alami.
OO membantu mengelola ketergantungan dan menulis program yang lebih mudah dan dapat dipelihara dengan bantuan alat-alat seperti enkapsulasi, polimorfisme, dll.
Pemrograman fungsional juga memiliki motif yang sama dalam mempromosikan kode yang dapat dipelihara tetapi dengan menggunakan gaya yang menghilangkan kebutuhan untuk menggunakan alat dan teknik OO - salah satunya saya percaya adalah dengan meminimalkan efek samping, fungsi murni dll.
Jawaban:
Saya pikir pentingnya ditunjukkan dengan membandingkan dengan pendekatan OO
misalnya, katakanlah kita memiliki objek
Dalam paradigma OO metode ini dilampirkan pada data, dan masuk akal jika data tersebut dimutasi oleh metode tersebut.
Dalam Paradigma Fungsional kami mendefinisikan hasil dalam hal fungsi. pesanan yang dibeli ADALAH hasil dari fungsi pembelian yang diterapkan pada suatu pesanan. Ini menyiratkan beberapa hal yang perlu kita pastikan
Apakah Anda mengharapkan order.Status == "Dibeli"?
Ini juga menyiratkan bahwa fungsi kita idempoten. yaitu. menjalankannya dua kali akan menghasilkan hasil yang sama setiap kali.
Jika pesanan diubah oleh fungsi pembelian, PurchasingOrder2 akan gagal.
Dengan mendefinisikan hal-hal sebagai hasil dari fungsi itu memungkinkan kita untuk menggunakan hasil-hasil itu tanpa benar-benar menghitungnya. Yang dalam istilah pemrograman eksekusi ditangguhkan.
Ini bisa berguna dalam dirinya sendiri, tetapi begitu kita tidak yakin kapan suatu fungsi benar-benar akan terjadi DAN kita baik-baik saja tentang itu, kita dapat memanfaatkan pemrosesan paralel lebih banyak daripada yang bisa kita lakukan dalam paradigma OO.
Kita tahu bahwa menjalankan suatu fungsi tidak akan mempengaruhi hasil dari fungsi lain; jadi kita dapat meninggalkan komputer untuk menjalankannya dalam urutan apa pun yang dipilihnya, menggunakan sebanyak utas yang diinginkan.
Jika suatu fungsi mengubah inputnya, kita harus lebih berhati-hati tentang hal-hal seperti itu.
sumber
Order Purchase() { return new Order(Status = "Purchased") }
sehingga status hanya baca bidang. ? Sekali lagi mengapa praktik ini lebih relevan dalam konteks paradigma pemrograman fungsi? Manfaat yang Anda sebutkan dapat dilihat dalam pemrograman OO juga, kan?Kunci untuk memahami mengapa benda tidak bergerak bermanfaat tidak benar-benar terletak pada mencoba menemukan contoh nyata dalam kode fungsional. Karena sebagian besar kode fungsional ditulis menggunakan bahasa fungsional, dan sebagian besar bahasa fungsional tidak dapat diubah secara default, sifat dasar paradigma ini dirancang untuk menghindari apa yang Anda cari, agar tidak terjadi.
Hal utama yang harus ditanyakan adalah, apa manfaat dari kekekalan? Jawabannya adalah, ia menghindari kompleksitas. Katakanlah kita memiliki dua variabel,
x
dany
. Keduanya dimulai dengan nilai1
.y
meskipun berlipat ganda setiap 13 detik. Berapa nilai masing-masing dari mereka dalam waktu 20 hari?x
akan1
. Itu mudah. Namun perlu upaya untuk berolahragay
karena jauh lebih kompleks. Apa waktu dalam 20 hari? Apakah saya harus memperhitungkan daylight saving? Kompleksitasy
versusx
jauh lebih banyak.Dan ini terjadi dalam kode nyata juga. Setiap kali Anda menambahkan nilai mutasi ke dalam campuran, itu menjadi nilai kompleks lain untuk Anda pegang dan hitung di kepala Anda, atau di atas kertas, ketika mencoba menulis, membaca, atau men-debug kode. Semakin banyak kompleksitas, semakin besar kemungkinan Anda membuat kesalahan dan memperkenalkan bug. Kode sulit untuk ditulis; sulit dibaca; susah di-debug: kodenya susah diperbaiki
Mutabilitas tidak buruk . Sebuah program dengan nol mutabilitas tidak dapat memberikan hasil, yang sangat tidak berguna. Bahkan jika ketidakstabilan adalah menulis hasil ke layar, disk atau apa pun, itu harus ada di sana. Yang buruk adalah kerumitan yang tidak perlu. Salah satu cara paling sederhana untuk mengurangi kompleksitas adalah membuat hal-hal yang tidak dapat diubah secara default dan hanya membuatnya dapat berubah ketika dibutuhkan, karena kinerja atau alasan fungsional.
sumber
y
harus bermutasi; itu syarat. Terkadang kita harus memiliki kode kompleks untuk memenuhi persyaratan yang kompleks. Poin yang saya coba sampaikan adalah bahwa kompleksitas yang tidak perlu harus dihindari. Nilai bermutasi secara inheren lebih kompleks daripada yang tetap, jadi - untuk menghindari kompleksitas yang tidak perlu - hanya nilai bermutasi saat Anda harus.Hal-hal yang sama yang dapat salah dalam pemrograman non-fungsional: Anda bisa mendapatkan efek samping yang tidak diinginkan dan tidak diinginkan , yang merupakan penyebab kesalahan yang diketahui sejak ditemukannya bahasa pemrograman yang tercakup.
IMHO satu-satunya perbedaan nyata antara ini antara pemrograman fungsional dan non-fungsional adalah, dalam kode non-fungsional Anda biasanya akan mengharapkan efek samping, dalam pemrograman fungsional, Anda tidak akan.
Tentu - efek samping yang tidak diinginkan adalah kategori bug, terlepas dari paradigma. Sebaliknya juga benar - efek samping yang sengaja digunakan dapat membantu untuk menangani masalah kinerja dan biasanya diperlukan untuk sebagian besar program dunia nyata ketika datang ke I / O dan berurusan dengan sistem eksternal - juga terlepas dari paradigma.
sumber
Saya baru saja menjawab pertanyaan StackOverflow yang menggambarkan pertanyaan Anda dengan cukup baik. Masalah utama dengan struktur data yang bisa berubah adalah identitas mereka hanya valid pada satu waktu yang tepat, sehingga orang cenderung menjejalkan sebanyak mungkin ke titik kecil dalam kode di mana mereka tahu identitasnya konstan. Dalam contoh khusus ini, ia melakukan banyak logging di dalam for for loop:
Ketika Anda terbiasa dengan ketidakberdayaan, tidak ada rasa takut perubahan struktur data jika Anda menunggu terlalu lama, sehingga Anda dapat melakukan tugas yang secara logis terpisah pada waktu luang Anda, dengan cara yang jauh lebih terpisah:
sumber
Keuntungan menggunakan objek yang tidak dapat diubah adalah bahwa jika seseorang menerima referensi ke objek dengan yang akan memiliki properti tertentu ketika penerima memeriksanya, dan perlu memberikan beberapa kode lain referensi ke objek dengan properti yang sama, seseorang dapat dengan mudah lulus sepanjang referensi ke objek tanpa memperhatikan siapa lagi yang mungkin telah menerima referensi atau apa yang mungkin mereka lakukan terhadap objek [karena tidak ada yang bisa dilakukan orang lain terhadap objek], atau ketika penerima dapat memeriksa objek [karena semua properti akan sama terlepas dari kapan mereka diperiksa].
Sebaliknya, kode yang perlu memberi seseorang referensi ke objek yang bisa berubah yang akan memiliki properti tertentu ketika penerima memeriksanya (dengan asumsi penerima itu sendiri tidak mengubahnya) juga perlu tahu bahwa tidak ada hal lain selain penerima yang akan pernah berubah properti itu, atau tahu kapan penerima akan mengakses properti itu, dan tahu bahwa tidak ada yang akan mengubah properti itu sampai terakhir kali penerima akan memeriksanya.
Saya pikir ini sangat membantu, untuk pemrograman secara umum (bukan hanya pemrograman fungsional) untuk memikirkan objek yang tidak dapat berubah jatuh ke dalam tiga kategori:
Objek yang tidak dapat tidak akan membiarkan apa pun mengubahnya, bahkan dengan referensi. Objek seperti itu, dan referensi padanya, berperilaku sebagai nilai , dan dapat dibagikan secara bebas.
Objek yang akan membiarkan diri mereka diubah oleh kode yang memiliki referensi padanya, tetapi referensi yang tidak akan pernah terpapar ke kode apa pun yang benar - benar akan mengubahnya. Objek-objek ini merangkum nilai-nilai, tetapi mereka hanya dapat dibagikan dengan kode yang dapat dipercaya untuk tidak mengubahnya atau mengeksposnya ke kode yang mungkin dilakukan.
Objek yang akan diubah. Objek-objek ini paling baik dilihat sebagai wadah , dan merujuk padanya sebagai pengidentifikasi .
Pola yang berguna adalah sering memiliki objek membuat wadah, mengisi dengan menggunakan kode yang dapat dipercaya untuk tidak menyimpan referensi sesudahnya, dan kemudian memiliki satu-satunya referensi yang pernah ada di mana saja di alam semesta dalam kode yang tidak akan pernah memodifikasi objek setelah itu dihuni. Sementara wadah mungkin dari jenis yang bisa berubah, itu mungkin beralasan tentang (*) seolah-olah itu tidak berubah, karena tidak ada yang akan benar-benar bermutasi. Jika semua referensi ke wadah disimpan dalam jenis pembungkus yang tidak dapat diubah yang tidak akan pernah mengubah isinya, pembungkus tersebut dapat diedarkan dengan aman seolah-olah data di dalamnya disimpan dalam benda yang tidak dapat diubah, karena referensi ke pembungkus dapat dibagikan dan diperiksa secara bebas di kapan saja.
(*) Dalam kode multi-ulir, mungkin perlu menggunakan "hambatan memori" untuk memastikan bahwa sebelum utas apa pun dapat melihat referensi ke pembungkus, efek dari semua tindakan pada wadah akan terlihat oleh utas itu, tetapi itu adalah kasus khusus yang disebutkan di sini hanya untuk kelengkapan.
sumber
Seperti yang telah disebutkan, masalah dengan keadaan dapat berubah pada dasarnya adalah subkelas dari masalah efek samping yang lebih besar , di mana tipe pengembalian fungsi tidak secara akurat menggambarkan fungsi yang sebenarnya dilakukan, karena dalam kasus ini, ia juga melakukan mutasi keadaan. Masalah ini telah diatasi oleh beberapa bahasa penelitian baru, seperti F * ( http://www.fstar-lang.org/tutorial/ ). Bahasa ini menciptakan Sistem Efek yang mirip dengan sistem tipe, di mana fungsi tidak hanya secara statis mendeklarasikan tipenya, tetapi juga efeknya. Dengan cara ini, penelepon fungsi menyadari bahwa mutasi negara dapat terjadi saat memanggil fungsi, dan efek yang disebarkan ke peneleponnya.
sumber