Saya baru-baru ini belajar tentang pemrograman fungsional (khususnya Haskell, tapi saya sudah membaca tutorial tentang Lisp dan Erlang juga). Sementara saya menemukan konsep yang sangat mencerahkan, saya masih tidak melihat sisi praktis dari konsep "tanpa efek samping". Apa manfaat praktisnya? Saya mencoba berpikir dalam pola pikir fungsional, tetapi ada beberapa situasi yang sepertinya terlalu rumit tanpa kemampuan untuk menyelamatkan negara dengan cara yang mudah (saya tidak menganggap monad Haskell 'mudah').
Apakah perlu terus mempelajari Haskell (atau bahasa fungsional murni lainnya) secara mendalam? Apakah pemrograman fungsional atau stateless sebenarnya lebih produktif daripada prosedural? Apakah mungkin saya akan terus menggunakan Haskell atau bahasa fungsional lainnya nanti, atau haruskah saya mempelajarinya hanya untuk pengertian?
Saya kurang peduli dengan kinerja daripada produktivitas. Jadi saya terutama bertanya apakah saya akan lebih produktif dalam bahasa fungsional daripada prosedural / berorientasi objek / apa pun.
sumber
Semakin banyak bagian dari program Anda yang tidak memiliki kewarganegaraan, semakin banyak cara untuk menyatukan potongan-potongan tanpa mengalami kerusakan . Kekuatan paradigma kewarganegaraan tidak terletak pada kewarganegaraan (atau kemurnian) semata , tetapi kemampuan yang diberikannya kepada Anda untuk menulis fungsi-fungsi yang kuat dan dapat digunakan kembali dan menggabungkannya.
Anda dapat menemukan tutorial yang bagus dengan banyak contoh di makalah John Hughes, Why Functional Programming Matters (PDF).
Anda akan sekumpulan lebih produktif, terutama jika Anda memilih bahasa fungsional yang juga memiliki tipe data aljabar dan pola yang cocok (Caml, SML, Haskell).
sumber
Banyak jawaban lain berfokus pada sisi kinerja (paralelisme) pemrograman fungsional, yang saya yakini sangat penting. Namun, Anda memang secara spesifik bertanya tentang produktivitas, seperti dalam, bisakah Anda memprogram hal yang sama lebih cepat dalam paradigma fungsional daripada dalam paradigma imperatif.
Saya benar-benar menemukan (dari pengalaman pribadi) bahwa pemrograman dalam F # cocok dengan cara saya berpikir yang lebih baik, sehingga lebih mudah. Saya pikir itu perbedaan terbesar. Saya telah memprogram dalam F # dan C #, dan ada jauh lebih sedikit "melawan bahasa" dalam F #, yang saya sukai. Anda tidak perlu memikirkan detail di F #. Inilah beberapa contoh dari apa yang saya temukan yang sangat saya nikmati.
Sebagai contoh, meskipun F # diketik secara statis (semua tipe diselesaikan pada waktu kompilasi), inferensi tipe mencari tahu tipe apa yang Anda miliki, jadi Anda tidak perlu mengatakannya. Dan jika tidak bisa mengetahuinya, secara otomatis membuat fungsi Anda / kelas / apa pun generik. Jadi, Anda tidak perlu menulis generik apa pun, semuanya otomatis. Saya menemukan itu berarti saya menghabiskan lebih banyak waktu untuk memikirkan masalah dan lebih sedikit bagaimana menerapkannya. Bahkan, setiap kali saya kembali ke C #, saya merasa saya benar-benar kehilangan inferensi tipe ini, Anda tidak pernah menyadari betapa mengganggu itu sampai Anda tidak perlu melakukannya lagi.
Juga di F #, alih-alih menulis loop, Anda memanggil fungsi. Ini adalah perubahan yang halus, tetapi signifikan, karena Anda tidak perlu memikirkan tentang loop build lagi. Sebagai contoh, berikut adalah sepotong kode yang akan melewati dan mencocokkan sesuatu (Saya tidak ingat apa, itu dari puzzle Euler proyek):
Saya menyadari bahwa melakukan filter maka peta (itu adalah konversi dari setiap elemen) di C # akan sangat sederhana, tetapi Anda harus berpikir di tingkat yang lebih rendah. Khususnya, Anda harus menulis loop itu sendiri, dan memiliki pernyataan if if Anda sendiri, dan hal-hal semacam itu. Sejak mempelajari F #, saya menyadari saya merasa lebih mudah untuk membuat kode dengan cara fungsional, di mana jika Anda ingin memfilter, Anda menulis "filter", dan jika Anda ingin memetakan, Anda menulis "peta", daripada mengimplementasikan masing-masing detail.
Saya juga suka operator |>, yang saya pikir memisahkan F # dari ocaml, dan mungkin bahasa fungsional lainnya. Ini operator pipa, ini memungkinkan Anda "menyalurkan" output dari satu ekspresi ke input ekspresi lain. Itu membuat kode mengikuti bagaimana saya berpikir lebih. Seperti dalam cuplikan kode di atas, artinya, "ambil urutan faktor, saring, lalu petakan." Ini adalah tingkat pemikiran yang sangat tinggi, yang tidak Anda dapatkan dalam bahasa pemrograman imperatif karena Anda begitu sibuk menulis perulangan dan pernyataan if. Itu satu hal yang paling saya rindukan setiap kali saya masuk ke bahasa lain.
Jadi secara umum, meskipun saya dapat memprogram di C # dan F #, saya merasa lebih mudah untuk menggunakan F # karena Anda dapat berpikir pada tingkat yang lebih tinggi. Saya berpendapat bahwa karena detail yang lebih kecil dihapus dari pemrograman fungsional (setidaknya di F #), saya lebih produktif.
Sunting : Saya melihat di salah satu komentar yang Anda minta contoh "negara" dalam bahasa pemrograman fungsional. F # dapat ditulis secara imperatif, jadi inilah contoh langsung bagaimana Anda dapat memiliki status yang dapat berubah dalam F #:
sumber
a |> b (p1, p2)
itu hanya gula sintaksisb (a, p1, p2)
. Pasangkan ini dengan asosiasi kanan dan Anda sudah mendapatkannya.Pertimbangkan semua bug sulit yang telah Anda habiskan untuk debugging lama.
Sekarang, berapa banyak bug yang disebabkan oleh "interaksi yang tidak disengaja" antara dua komponen program yang terpisah? (Hampir semua bug threading memiliki formulir ini: perlombaan yang melibatkan penulisan data bersama, kebuntuan, ... Selain itu, umum untuk menemukan perpustakaan yang memiliki beberapa efek tak terduga pada keadaan global, atau membaca / menulis registri / lingkungan, dll.) I akan berpendapat bahwa setidaknya 1 dari 3 'hard bug' termasuk dalam kategori ini.
Sekarang jika Anda beralih ke pemrograman stateless / immutable / pure, semua bug itu hilang. Anda akan disajikan dengan beberapa tantangan baru, bukan (misalnya ketika Anda melakukan ingin modul yang berbeda untuk berinteraksi dengan lingkungan), tapi dalam bahasa seperti Haskell, mereka interaksi mendapatkan eksplisit tereifikasi ke dalam sistem jenis, yang berarti Anda hanya dapat melihat jenis fungsi dan alasan tentang jenis interaksi yang dapat dimilikinya dengan program lainnya.
Itulah kemenangan besar dari IMO 'kekekalan'. Di dunia yang ideal, kita semua akan merancang API yang hebat dan bahkan ketika semuanya bisa berubah, efeknya akan lokal dan didokumentasikan dengan baik dan interaksi 'tak terduga' akan dijaga seminimal mungkin. Di dunia nyata, ada banyak API yang berinteraksi dengan keadaan global dalam berbagai cara, dan ini adalah sumber bug yang paling merusak. Bercita-cita untuk kewarganegaraan adalah bercita-cita untuk menyingkirkan interaksi yang tidak disengaja / implisit / di belakang layar di antara komponen.
sumber
Salah satu keuntungan dari fungsi stateless adalah mereka mengizinkan precalculation atau caching dari nilai-nilai pengembalian fungsi. Bahkan beberapa kompiler C memungkinkan Anda untuk secara eksplisit menandai fungsi sebagai stateless untuk meningkatkan optimisabilitasnya. Seperti yang banyak dicatat, fungsi stateless jauh lebih mudah diparalelkan.
Tetapi efisiensi bukan satu-satunya perhatian. Fungsi murni lebih mudah untuk menguji dan men-debug karena apa pun yang mempengaruhinya dinyatakan secara eksplisit. Dan ketika memprogram dalam bahasa fungsional, orang terbiasa membuat sesedikit mungkin fungsi "kotor" (dengan I / O, dll.). Memisahkan hal-hal stateful dengan cara ini adalah cara yang baik untuk merancang program, bahkan dalam bahasa yang tidak begitu fungsional.
Bahasa fungsional dapat membutuhkan waktu untuk "mendapatkan", dan sulit untuk menjelaskan kepada seseorang yang belum melalui proses itu. Tetapi kebanyakan orang yang bertahan cukup lama akhirnya menyadari bahwa kerepotan itu sepadan, bahkan jika mereka tidak banyak menggunakan bahasa fungsional.
sumber
sin(PI/3)
dalam kode Anda, di mana PI adalah konstanta, kompilator dapat mengevaluasi fungsi ini pada waktu kompilasi dan menanamkan hasilnya dalam kode yang dihasilkan.Tanpa status, sangat mudah untuk memparalelkan kode Anda (karena CPU dibuat dengan semakin banyak inti, ini sangat penting).
sumber
Aplikasi web stateless sangat penting ketika Anda mulai memiliki lalu lintas yang lebih tinggi.
Mungkin ada banyak data pengguna yang tidak ingin Anda simpan di sisi klien karena alasan keamanan misalnya. Dalam hal ini Anda perlu menyimpannya di sisi server. Anda dapat menggunakan sesi default aplikasi web tetapi jika Anda memiliki lebih dari satu instance aplikasi Anda perlu memastikan bahwa setiap pengguna selalu diarahkan ke instance yang sama.
Load balancers sering memiliki kemampuan untuk memiliki 'sesi lengket' di mana load balancer tahu bagaimana server untuk mengirim permintaan pengguna. Namun ini tidak ideal, misalnya artinya setiap kali Anda me-restart aplikasi web Anda, semua pengguna yang terhubung akan kehilangan sesi mereka.
Pendekatan yang lebih baik adalah dengan menyimpan sesi di belakang server web dalam semacam penyimpanan data, hari ini ada banyak produk nosql besar yang tersedia untuk ini (redis, mongo, elasticsearch, memcached). Dengan cara ini server web tidak memiliki kewarganegaraan tetapi Anda masih memiliki sisi server negara dan ketersediaan status ini dapat dikelola dengan memilih pengaturan datastore yang tepat. Penyimpanan data ini biasanya memiliki redundansi yang besar sehingga hampir selalu memungkinkan untuk membuat perubahan pada aplikasi web Anda dan bahkan penyimpanan data tanpa berdampak pada pengguna.
sumber
Saya menulis posting hanya pada subjek ini beberapa waktu lalu: Pada Pentingnya Kemurnian .
sumber