it-swarm-id.com

Mengapa efek samping dianggap jahat dalam pemrograman fungsional?

Saya merasa bahwa efek samping adalah fenomena alam. Tapi itu seperti tabu dalam bahasa fungsional. Apa alasannya?

Pertanyaan saya khusus untuk gaya pemrograman fungsional. Tidak semua bahasa/paradigma pemrograman.

70
Gulshan

Menulis fungsi/metode Anda tanpa efek samping - jadi mereka fungsi murni - memudahkan Anda beralasan tentang kebenaran program Anda.

Itu juga membuatnya mudah untuk menyusun fungsi-fungsi itu untuk menciptakan perilaku baru.

Ini juga memungkinkan optimisasi tertentu, di mana kompiler dapat misalnya memoise hasil fungsi, atau menggunakan Penghapusan Subekspresi Umum.

Sunting: atas permintaan Benjol: Karena banyak negara Anda disimpan dalam tumpukan (aliran data, bukan aliran kontrol, seperti yang disebut Jonas di sini ), Anda dapat memparalelkan atau mengatur ulang pelaksanaan bagian-bagian itu perhitungan Anda yang independen satu sama lain. Anda dapat dengan mudah menemukan bagian-bagian independen itu karena satu bagian tidak memberikan masukan kepada yang lain.

Dalam lingkungan dengan debugger yang memungkinkan Anda memutar kembali tumpukan dan melanjutkan komputasi (seperti Smalltalk), memiliki fungsi murni berarti Anda dapat dengan mudah melihat bagaimana suatu perubahan nilai, karena status sebelumnya tersedia untuk diperiksa. Dalam perhitungan yang berat-mutasi, kecuali jika Anda secara eksplisit menambahkan tindakan lakukan/batalkan ke struktur atau algoritma Anda, Anda tidak dapat melihat sejarah perhitungan. (Ini mengikat kembali ke paragraf pertama: menulis fungsi murni membuatnya lebih mudah untuk memeriksa kebenaran program Anda.)

73
Frank Shearar

Anda salah, pemrograman fungsional mempromosikan efek samping yang terbatas untuk membuat program mudah dipahami dan dioptimalkan. Bahkan Haskell memungkinkan Anda menulis ke file.

Pada dasarnya apa yang saya katakan adalah bahwa programmer fungsional tidak menganggap efek samping itu jahat, mereka hanya berpikir membatasi penggunaan efek samping itu baik. Saya tahu itu mungkin tampak seperti perbedaan yang sederhana tetapi membuat semua perbedaan.

24
ChaosPandion

Dari artikel tentang Pemrograman fungsional :

Dalam praktiknya, aplikasi perlu memiliki beberapa efek samping. Simon Peyton-Jones, kontributor utama bahasa pemrograman fungsional Haskell, mengatakan hal berikut: "Pada akhirnya, program apa pun harus memanipulasi keadaan. Program yang tidak memiliki efek samping apa pun adalah semacam kotak hitam. Yang bisa Anda katakan hanyalah bahwa kotak semakin panas. " ( http://oscon.blip.tv/file/324976 ) Kuncinya adalah untuk membatasi efek samping, mengidentifikasi dengan jelas, dan menghindari menyebarkannya di seluruh kode.

23
Peter Stuifzand

Beberapa catatan:

  • Fungsi tanpa efek samping dapat dengan mudah dieksekusi secara paralel, sedangkan fungsi dengan efek samping biasanya memerlukan semacam sinkronisasi.

  • Fungsi tanpa efek samping memungkinkan pengoptimalan yang lebih agresif (mis. Dengan menggunakan cache hasil secara transparan), karena selama kita mendapatkan hasil yang benar, bahkan tidak masalah apakah fungsinya sungguh dieksekusi

13
user281377

Saya terutama bekerja dalam kode fungsional sekarang, dan dari perspektif itu tampak sangat jelas. Efek samping membuat besar beban mental pada programmer mencoba membaca dan memahami kode. Anda tidak menyadari beban itu sampai Anda bebas darinya untuk sementara waktu, lalu tiba-tiba harus membaca kode dengan efek samping lagi.

Pertimbangkan contoh sederhana ini:

val foo = 42
// Several lines of code you don't really care about, but that contain a
// lot of function calls that use foo and may or may not change its value
// by side effect.

// Code you are troubleshooting
// What's the expected value of foo here?

Dalam bahasa fungsional, saya tahu bahwa foo masih 42. Saya bahkan tidak perlu lihat pada kode di antaranya, lebih sedikit memahaminya, atau lihat implementasi fungsi yang dipanggil.

Semua hal tentang konkurensi dan paralelisasi dan optimalisasi itu bagus, tetapi itulah yang dimasukkan oleh para ilmuwan komputer pada brosur. Tidak perlu bertanya-tanya siapa yang mengubah variabel Anda dan kapan hal yang benar-benar saya nikmati dalam latihan sehari-hari.

11
Karl Bielefeldt

Sedikit bahasa yang tidak memungkinkan menyebabkan efek samping. Bahasa yang sepenuhnya bebas efek samping akan sangat sulit (hampir tidak mungkin) untuk digunakan, kecuali dalam kapasitas yang sangat terbatas.

Mengapa efek samping dianggap jahat?

Karena mereka membuatnya jauh lebih sulit untuk berpikir tentang apa yang dilakukan suatu program, dan untuk membuktikan bahwa itu melakukan apa yang Anda harapkan.

Pada tingkat yang sangat tinggi, bayangkan menguji seluruh situs web 3-tier dengan hanya pengujian kotak hitam. Tentu, itu bisa dilakukan, tergantung pada skalanya. Tetapi tentu saja ada banyak duplikasi yang terjadi. Dan jika ada adalah bug (yang terkait dengan efek samping), maka Anda dapat berpotensi merusak seluruh sistem untuk pengujian lebih lanjut, sampai bug didiagnosis dan diperbaiki, dan perbaikannya adalah dikerahkan ke lingkungan pengujian.

Manfaat

Sekarang, turunkan itu. Jika Anda cukup pandai menulis kode bebas efek samping, seberapa cepat Anda akan mempertimbangkan apa yang dilakukan oleh beberapa kode yang ada? Seberapa cepat Anda bisa menulis unit test? Seberapa yakin Anda akan merasa bahwa kode tanpa efek samping dijamin bebas bug, dan bahwa pengguna dapat membatasi paparan terhadap bug apa pun yang did miliki?

Jika kode tidak memiliki efek samping, kompiler juga dapat memiliki optimasi tambahan yang dapat dilakukan. Mungkin lebih mudah untuk mengimplementasikan optimasi tersebut. Mungkin jauh lebih mudah untuk bahkan membuat konsep optimasi untuk kode bebas efek samping, yang berarti bahwa vendor kompiler Anda dapat menerapkan optimasi yang sulit untuk mustahil dalam kode dengan efek samping.

Concurrency juga secara drastis lebih mudah untuk diterapkan, untuk secara otomatis menghasilkan, dan untuk mengoptimalkan ketika kode tidak memiliki efek samping. Ini karena semua bagian dapat dievaluasi dengan aman dalam urutan apa pun. Mengizinkan pemrogram untuk menulis kode yang sangat konkuren secara luas dianggap sebagai tantangan besar berikutnya yang perlu ditangani oleh Ilmu Komputer, dan salah satu dari beberapa lindung nilai yang tersisa terhadap Hukum Moore .

6

Efek samping seperti "kebocoran" dalam kode Anda yang perlu ditangani nanti, baik oleh Anda atau rekan kerja yang tidak curiga.

Bahasa fungsional menghindari variabel keadaan dan data yang dapat berubah sebagai cara membuat kode lebih tidak tergantung konteks dan lebih modular. Modularitas memastikan bahwa pekerjaan satu pengembang tidak akan memengaruhi/merusak pekerjaan pengembang lain.

Penskalaan laju pengembangan dengan ukuran tim, adalah "cawan suci" pengembangan perangkat lunak saat ini. Ketika bekerja dengan programmer lain, beberapa hal sama pentingnya dengan modularitas. Bahkan efek samping logis yang paling sederhana pun membuat kolaborasi menjadi sangat sulit.

4
Ami

Nah, IMHO, ini sangat munafik. Tidak ada yang suka efek samping, tetapi semua orang membutuhkannya.

Apa yang sangat berbahaya tentang efek samping adalah bahwa jika Anda memanggil suatu fungsi, maka ini mungkin memiliki efek tidak hanya pada cara fungsi berperilaku ketika dipanggil lain kali, tetapi mungkin memiliki efek ini pada fungsi lain. Dengan demikian efek samping menimbulkan perilaku yang tidak terduga dan dependensi nontrivial.

Pemrograman paradigma seperti OO dan fungsional keduanya mengatasi masalah ini. OO mengurangi masalah dengan memaksakan pemisahan masalah. Ini berarti keadaan aplikasi, yang terdiri dari banyak data yang bisa berubah, dienkapsulasi menjadi objek, yang masing-masing bertanggung jawab untuk mempertahankan keadaannya sendiri saja. Dengan cara ini risiko ketergantungan berkurang dan masalah jauh lebih terisolasi dan lebih mudah dilacak.

Pemrograman fungsional mengambil pendekatan yang jauh lebih radikal, di mana status aplikasi tidak dapat diubah dari perspektif pemrogram. Ini adalah ide yang bagus, tetapi menjadikan bahasa itu sendiri tidak berguna. Mengapa? Karena SETIAP I/O-operasi memiliki efek samping. Segera setelah Anda membaca dari aliran input apa pun, status aplikasi Anda kemungkinan akan berubah, karena kali berikutnya Anda menjalankan fungsi yang sama, hasilnya kemungkinan akan berbeda. Anda mungkin membaca data yang berbeda, atau - juga kemungkinan - operasi mungkin gagal. Hal yang sama berlaku untuk output. Bahkan keluaran adalah operasi dengan efek samping. Ini bukan apa-apa yang sering Anda sadari saat ini, tetapi bayangkan Anda hanya memiliki 20K untuk output Anda dan jika Anda output lagi, aplikasi Anda macet karena Anda kehabisan ruang disk atau apa pun.

Jadi ya, efek sampingnya buruk dan berbahaya dari sudut pandang seorang programmer. Sebagian besar bug berasal dari cara bagian-bagian tertentu dari status aplikasi yang saling terkait dalam cara yang hampir tidak jelas, melalui efek samping yang tidak dipertimbangkan dan seringkali tidak perlu. Dari perspektif pengguna, efek samping adalah titik menggunakan komputer. Mereka tidak peduli dengan apa yang terjadi di dalam atau bagaimana hal itu diatur. Mereka melakukan sesuatu dan berharap komputer untuk MENGUBAH sesuai.

4
back2dos

Setiap efek samping memperkenalkan parameter input/output tambahan yang harus diperhitungkan saat pengujian.

Ini membuat validasi kode jauh lebih kompleks karena lingkungan tidak dapat dibatasi hanya pada kode yang divalidasi, tetapi harus membawa sebagian atau semua lingkungan sekitarnya (global yang diperbarui tinggal dalam kode itu di sana, yang pada gilirannya tergantung pada kode, yang pada gilirannya tergantung pada tinggal di dalam server penuh Java EE ....)

Dengan mencoba menghindari efek samping, Anda membatasi jumlah eksternalisme yang diperlukan untuk menjalankan kode.

2
user1249

Dalam pengalaman saya desain yang baik dalam pemrograman Object Orientated mengamanatkan penggunaan fungsi yang memiliki efek samping.

Misalnya, ambil aplikasi desktop UI dasar. Saya mungkin memiliki program yang sedang berjalan yang memiliki tumpukan objek grafik yang mewakili keadaan saat ini dari model domain program saya. Pesan tiba ke objek dalam grafik itu (misalnya, melalui metode panggilan dipanggil dari pengontrol lapisan UI). Grafik objek (model domain) pada heap dimodifikasi sebagai respons terhadap pesan. Pengamat model diberitahu tentang perubahan, UI dan mungkin sumber daya lain dimodifikasi.

Jauh dari kejahatan, pengaturan yang benar dari efek samping heap-modifying dan modifying screen ini adalah inti dari desain OO (dalam hal ini pola MVC).

Tentu saja, itu tidak berarti bahwa metode Anda harus memiliki efek samping arbiter. Dan fungsi bebas efek samping memang memiliki tempat dalam meningkatkan keterbacaan dan kadang-kadang kinerja, kode Anda.

1
flamingpenguin

Seperti yang telah ditunjukkan oleh pertanyaan di atas, bahasa fungsional tidak terlalu banyak mencegah kode dari memiliki efek samping seperti menyediakan alat untuk mengelola efek samping apa yang dapat kami lakukan terjadi dalam potongan kode tertentu dan kapan.

Ini ternyata memiliki konsekuensi yang sangat menarik. Pertama, dan yang paling jelas, ada banyak hal yang dapat Anda lakukan dengan kode bebas efek samping, yang telah dijelaskan. Tetapi ada hal-hal lain yang dapat kita lakukan juga, bahkan ketika bekerja dengan kode yang memiliki efek samping:

  • Dalam kode dengan status yang bisa berubah, kita dapat mengelola ruang lingkup negara sedemikian rupa untuk memastikan secara statis bahwa itu tidak dapat bocor di luar fungsi yang diberikan, ini memungkinkan kita untuk mengumpulkan sampah tanpa penghitungan referensi atau skema gaya mark-and-sweep , namun tetap yakin bahwa tidak ada referensi yang bertahan. Jaminan yang sama juga berguna untuk menjaga informasi sensitif privasi, dll. (Ini dapat dicapai menggunakan ST monad di haskell)
  • Saat memodifikasi keadaan bersama di beberapa utas, kita dapat menghindari perlunya kunci dengan melacak perubahan dan melakukan pembaruan atom pada akhir transaksi, atau memutar kembali transaksi dan mengulanginya jika utas lain membuat modifikasi yang bertentangan. Ini hanya dapat dicapai karena kami dapat memastikan bahwa kode tidak memiliki efek selain dari modifikasi negara (yang dapat kami tinggalkan dengan senang hati). Ini dilakukan oleh monad STM (Software Transactional Memory) di Haskell.
  • kita dapat melacak efek kode dan mengirimnya ke kotak pasir sepele, memfilter setiap efek yang mungkin perlu dilakukan untuk memastikan itu aman, sehingga memungkinkan (misalnya) kode yang dimasukkan pengguna dijalankan dengan aman di situs web
0
Jules

Kejahatan sedikit di atas .. itu semua tergantung pada konteks penggunaan bahasa.

Pertimbangan lain untuk yang telah disebutkan adalah bahwa hal itu membuat pembuktian kebenaran suatu program menjadi lebih sederhana jika tidak ada efek samping fungsional.

0
Ilan

Dalam basis kode yang kompleks, interaksi kompleks dari efek samping adalah hal paling sulit yang saya temukan untuk dipikirkan. Saya hanya bisa berbicara secara pribadi dengan cara otak saya bekerja. Efek samping dan keadaan persisten dan input yang bermutasi dan seterusnya membuat saya harus berpikir tentang "kapan" dan "di mana" hal-hal terjadi dengan alasan tentang kebenaran, bukan hanya "apa" yang terjadi di setiap fungsi individu.

Saya tidak bisa hanya fokus pada "apa". Saya tidak dapat menyimpulkan setelah benar-benar menguji suatu fungsi yang menyebabkan efek samping yang akan menyebarkan udara keandalan seluruh kode yang menggunakannya, karena penelepon mungkin masih menyalahgunakannya dengan memanggilnya pada waktu yang salah, dari utas yang salah, di salah memesan. Sementara itu fungsi yang tidak menyebabkan efek samping dan hanya mengembalikan output baru yang diberikan input (tanpa menyentuh input) sangat tidak mungkin untuk disalahgunakan dengan cara ini.

Tetapi saya adalah tipe pragmatis, saya pikir, atau setidaknya mencoba untuk menjadi, dan saya tidak berpikir kita harus menghapus semua efek samping seminimal mungkin untuk alasan tentang kebenaran kode kita (paling tidak paling tidak Saya akan menemukan ini sangat sulit dilakukan dalam bahasa seperti C). Di mana saya merasa sangat sulit untuk beralasan tentang kebenaran adalah ketika kita memiliki kombinasi aliran kontrol yang kompleks dan efek samping.

Alur kontrol kompleks kepada saya adalah yang bersifat seperti grafik, sering bersifat rekursif atau rekursif (kejadian antrian, misalnya, yang tidak secara langsung menyebut peristiwa secara rekursif tetapi bersifat "rekursif-suka"), mungkin melakukan hal-hal dalam proses melintasi struktur grafik tertaut yang sebenarnya, atau memproses antrian peristiwa non-homogen yang berisi campuran eklektik peristiwa untuk diproses membawa kita ke semua jenis bagian kode basis yang berbeda dan semua memicu efek samping yang berbeda. Jika Anda mencoba untuk menggambar semua tempat yang pada akhirnya Anda akan berakhir dalam kode, itu akan menyerupai grafik yang kompleks dan berpotensi dengan node dalam grafik yang Anda tidak pernah harapkan akan ada di sana pada saat itu, dan mengingat bahwa mereka semua menyebabkan efek samping, itu berarti Anda mungkin tidak hanya terkejut tentang fungsi apa yang disebut tetapi juga efek samping apa yang terjadi selama waktu itu dan urutan di mana mereka terjadi.

Bahasa fungsional dapat memiliki aliran kontrol yang sangat kompleks dan rekursif, tetapi hasilnya sangat mudah dipahami dalam hal kebenaran karena tidak ada segala macam efek samping eklektik yang terjadi dalam proses. Hanya ketika aliran kontrol yang kompleks bertemu dengan efek samping eklektik, saya merasa sakit kepala untuk mencoba memahami keseluruhan dari apa yang terjadi dan apakah itu akan selalu melakukan hal yang benar.

Jadi ketika saya memiliki kasus-kasus itu, saya sering merasa sangat sulit, jika bukan tidak mungkin, untuk merasa sangat yakin tentang kebenaran kode tersebut, apalagi sangat yakin bahwa saya dapat membuat perubahan pada kode tersebut tanpa tersandung pada sesuatu yang tidak terduga. Jadi solusinya bagi saya ada yang menyederhanakan aliran kontrol atau meminimalkan/menyatukan efek samping (dengan menyatukan, maksud saya seperti hanya menyebabkan satu jenis efek samping ke banyak hal selama fase tertentu dalam sistem, bukan dua atau tiga atau lusin). Saya perlu salah satu dari dua hal itu terjadi untuk memungkinkan otak bodoh saya merasa yakin tentang kebenaran kode yang ada dan kebenaran perubahan yang saya perkenalkan. Sangat mudah untuk merasa yakin tentang kebenaran kode yang memperkenalkan efek samping jika efek sampingnya seragam dan sederhana bersama dengan aliran kontrol, seperti:

for each pixel in an image:
    make it red

Sangat mudah untuk alasan tentang kebenaran kode tersebut, tetapi terutama karena efek sampingnya sangat seragam dan aliran kontrolnya sangat sederhana. Tetapi katakanlah kita memiliki kode seperti ini:

for each vertex to remove in a mesh:
     start removing vertex from connected edges():
         start removing connected edges from connected faces():
             rebuild connected faces excluding edges to remove():
                  if face has less than 3 edges:
                       remove face
             remove Edge
         remove vertex

Maka ini adalah pseudocode yang sangat disederhanakan yang biasanya melibatkan lebih banyak fungsi dan loop bersarang dan banyak lagi hal yang harus dilakukan (memperbarui beberapa peta tekstur, bobot tulang, keadaan pemilihan, dll), tetapi bahkan kodesemu membuatnya sangat sulit untuk alasan tentang kebenaran karena interaksi aliran kontrol seperti grafik yang kompleks dan efek samping yang terjadi. Jadi satu strategi untuk menyederhanakannya adalah menunda pemrosesan dan hanya fokus pada satu jenis efek samping pada satu waktu:

for each vertex to remove:
     mark connected edges
for each marked Edge:
     mark connected faces
for each marked face:
     remove marked edges from face
     if num_edges < 3:
          remove face

for each marked Edge:
     remove Edge
for each vertex to remove:
     remove vertex

... sesuatu untuk efek ini sebagai satu iterasi penyederhanaan. Itu berarti kita melewati data beberapa kali yang pasti menimbulkan biaya komputasi, tetapi kita sering menemukan kita dapat melakukan multithread kode yang dihasilkan lebih mudah, sekarang efek samping dan aliran kontrol telah mengambil sifat yang seragam dan lebih sederhana ini. Lebih jauh, setiap loop dapat dibuat lebih ramah-cache daripada melintasi grafik yang terhubung dan menyebabkan efek samping saat berjalan (mis: gunakan bit paralel yang disetel untuk menandai apa yang perlu dilalui sehingga kita dapat melakukan pass yang ditangguhkan dalam urutan berurutan yang diurutkan) menggunakan bitmasks dan FFS). Tetapi yang paling penting, saya menemukan versi kedua jauh lebih mudah untuk dipikirkan dalam hal kebenaran serta perubahan tanpa menyebabkan bug. Jadi begitulah cara saya mendekatinya dan saya menerapkan jenis pola pikir yang sama untuk menyederhanakan pemrosesan jala di atas sebagai penyederhanaan penanganan acara dan sebagainya - loop yang lebih homogen dengan aliran kontrol sederhana yang mati yang menyebabkan efek samping yang seragam.

Lagi pula, kita perlu efek samping terjadi di beberapa titik, atau kita hanya akan memiliki fungsi yang menghasilkan data tanpa tempat. Seringkali kita perlu merekam sesuatu ke file, menampilkan sesuatu ke layar, mengirim data melalui soket, sesuatu seperti ini, dan semua ini adalah efek samping. Tetapi kita pasti dapat mengurangi jumlah efek samping berlebihan yang terjadi, dan juga mengurangi jumlah efek samping yang terjadi ketika kontrol mengalir sangat rumit, dan saya pikir akan jauh lebih mudah untuk menghindari bug jika kita melakukannya.

0
user204677