Pemrograman fungsional mencakup banyak teknik yang berbeda. Beberapa teknik baik-baik saja dengan efek samping. Tetapi satu aspek penting adalah penalaran yang sama : Jika saya memanggil suatu fungsi dengan nilai yang sama, saya selalu mendapatkan hasil yang sama. Jadi saya bisa mengganti panggilan fungsi dengan nilai balik, dan mendapatkan perilaku yang setara. Ini membuatnya lebih mudah untuk beralasan tentang program, terutama ketika debugging.
Jika fungsi tersebut memiliki efek samping, ini tidak cukup berlaku. Nilai kembali tidak setara dengan panggilan fungsi, karena nilai kembali tidak mengandung efek samping.
Solusinya adalah berhenti menggunakan efek samping dan menyandikan efek ini dalam nilai kembali . Bahasa yang berbeda memiliki sistem efek yang berbeda. Misalnya Haskell menggunakan monad untuk menyandikan efek tertentu seperti IO atau mutasi Negara. Bahasa C / C ++ / Rust memiliki sistem tipe yang dapat melarang mutasi beberapa nilai.
Dalam bahasa imperatif, suatu print("foo")
fungsi akan mencetak sesuatu dan tidak mengembalikan apa pun. Dalam bahasa fungsional murni seperti Haskell, print
fungsi juga mengambil objek yang mewakili keadaan dunia luar, dan mengembalikan objek baru yang mewakili negara setelah melakukan output ini. Sesuatu yang mirip dengan newState = print "foo" oldState
. Saya dapat membuat banyak negara baru dari negara lama yang saya inginkan. Namun, hanya satu yang akan digunakan oleh fungsi utama. Jadi saya perlu mengurutkan status dari beberapa tindakan dengan merantai fungsi. Untuk mencetak foo bar
, saya mungkin mengatakan sesuatu seperti print "bar" (print "foo" originalState)
.
Jika keadaan keluaran tidak digunakan, Haskell tidak melakukan tindakan yang mengarah ke keadaan itu, karena itu adalah bahasa malas. Sebaliknya, kemalasan ini hanya dimungkinkan karena semua efek dikodekan secara eksplisit sebagai nilai pengembalian.
Perhatikan bahwa Haskell adalah satu - satunya bahasa fungsional yang umum digunakan yang menggunakan rute ini. Bahasa fungsional lainnya termasuk keluarga Lisp, keluarga ML, dan bahasa fungsional yang lebih baru seperti Scala mencegah tetapi memungkinkan efek samping - mereka bisa disebut bahasa fungsional-imperatif.
Menggunakan efek samping untuk I / O mungkin baik-baik saja. Seringkali, I / O (selain logging) hanya dilakukan di batas luar sistem Anda. Tidak ada komunikasi eksternal yang terjadi dalam logika bisnis Anda. Maka dimungkinkan untuk menulis inti dari perangkat lunak Anda dalam gaya murni, sambil tetap melakukan I / O yang tidak murni di kulit terluar. Ini juga berarti bahwa inti dapat menjadi tanpa kewarganegaraan.
Statelessness memiliki sejumlah keunggulan praktis, seperti peningkatan kewajaran dan skalabilitas. Ini sangat populer untuk aplikasi web backend. Status apa pun disimpan di luar, dalam database bersama. Ini membuat load balancing mudah: Saya tidak perlu menempel sesi ke server tertentu. Bagaimana jika saya membutuhkan lebih banyak server? Tambahkan saja, karena menggunakan basis data yang sama. Bagaimana jika satu server lumpuh? Saya dapat mengulang permintaan yang tertunda di server lain. Tentu saja, masih ada keadaan - dalam database. Tetapi saya telah membuatnya secara eksplisit dan mengekstraksinya, dan dapat menggunakan pendekatan fungsional murni secara internal jika saya mau.