Saya sudah trending ke arah pemrograman fungsional selama 4 tahun sekarang, sejak saya mulai bekerja dengan LINQ. Baru-baru ini, saya menulis beberapa kode fungsional C # murni dan saya perhatikan, secara langsung, apa yang telah saya baca tentang program fungsional - yang setelah mereka kompilasi, mereka cenderung benar.
Saya mencoba menekankan mengapa ini yang terjadi tetapi saya belum berhasil.
Satu tebakan adalah bahwa dalam menerapkan prinsip-prinsip OO, Anda memiliki "lapisan abstraksi" yang tidak ada dalam program-program fungsional dan lapisan abstraksi ini memungkinkan kontrak antara objek-objek menjadi benar sementara implementasi salah.
Adakah yang memikirkan hal ini dan mengemukakan alasan abstrak yang mendasari korelasi antara keberhasilan kompilasi dan kebenaran program dalam pemrograman fungsional?
sumber
final
segalanya).Jawaban:
Saya dapat menulis jawaban ini sebagai seseorang yang banyak membuktikan hal, jadi bagi saya kebenaran bukan hanya yang berhasil, tetapi yang berhasil dan mudah untuk dibuktikan.
Dalam banyak hal, pemrograman fungsional lebih membatasi daripada pemrograman imperatif. Lagi pula, tidak ada yang menghentikan Anda untuk tidak pernah mengubah variabel dalam C! Memang sebagian besar fitur dalam bahasa FP langsung untuk dibicarakan dalam hal hanya beberapa fitur inti. Itu semua cukup banyak bermuara pada lambdas, aplikasi fungsi, dan pencocokan pola!
Namun, karena kami telah membayar piper di muka, kami memiliki jauh lebih sedikit untuk ditangani dan kami memiliki jauh lebih sedikit opsi untuk bagaimana kesalahan bisa terjadi. Jika Anda penggemar tahun 1984, kebebasan memang perbudakan! Dengan menggunakan 101 trik yang berbeda untuk suatu program, kita harus mempertimbangkan berbagai hal seolah-olah 101 hal ini dapat terjadi! Itu benar-benar sulit dilakukan ternyata :)
Jika Anda memulai dengan gunting keselamatan alih-alih pedang, berlari tidak terlalu berbahaya.
Sekarang kami melihat pertanyaan Anda: bagaimana semua ini cocok dengan "itu mengkompilasi dan bekerja!" fenomena. Saya pikir sebagian besar dari ini adalah alasan yang sama seperti mengapa mudah untuk membuktikan kode! Lagi pula, ketika Anda menulis perangkat lunak, Anda membuat bukti informal bahwa itu benar. Karena hal ini, apa yang dicakup oleh bukti-bukti handwavy alami Anda dan para penyusun memiliki gagasan tentang kebenaran (pengetikan) cukup banyak.
Saat Anda menambahkan fitur dan interaksi yang rumit di antara mereka, apa yang tidak diperiksa oleh sistem tipe meningkat. Namun, kemampuan Anda untuk membangun bukti informal tampaknya tidak membaik! Ini berarti bahwa masih ada lagi yang bisa lolos dari pemeriksaan awal Anda dan harus ditangkap kemudian.
sumber
Negara yang bisa berubah.
Compiler memeriksa hal-hal secara statis. Mereka memastikan program Anda terbentuk dengan baik, dan sistem tipe menyediakan mekanisme untuk mencoba memastikan bahwa jenis nilai yang tepat diperbolehkan di tempat yang tepat. Sistem tipe juga mencoba memastikan bahwa semantik yang tepat diizinkan di tempat yang tepat.
Segera setelah program Anda memperkenalkan keadaan, kendala yang terakhir menjadi kurang berguna. Anda tidak hanya perlu khawatir tentang nilai-nilai yang tepat di tempat yang tepat, tetapi Anda juga perlu memperhitungkan nilai yang berubah pada titik-titik acak program Anda. Anda harus memperhitungkan semantik kode Anda yang berubah di samping kondisi itu.
Jika Anda melakukan pemrograman fungsional dengan baik, tidak ada (atau sangat sedikit) keadaan bisa berubah.
Ada beberapa perdebatan tentang penyebabnya di sini - jika program tanpa kerja negara setelah kompilasi lebih sering karena kompiler dapat menangkap lebih banyak bug atau jika program tanpa kerja negara setelah kompilasi lebih sering karena gaya pemrograman menghasilkan lebih sedikit bug.
Ini mungkin campuran dari keduanya dalam pengalaman saya.
sumber
Sederhananya, pembatasan berarti ada lebih sedikit cara yang benar untuk menyatukan segala sesuatunya, dan fungsi kelas satu memudahkan untuk memfaktorkan hal-hal seperti struktur loop. Ambil loop dari jawaban ini , misalnya:
Ini merupakan cara imperatif yang aman di Jawa untuk menghapus elemen dari koleksi saat Anda mengulanginya. Ada banyak cara yang terlihat sangat dekat, tetapi salah. Orang-orang yang tidak mengetahui metode ini kadang-kadang pergi melalui cara berbelit-belit untuk menghindari masalah, seperti iterasi melalui salinan.
Ini tidak terlalu sulit untuk membuat generik ini, jadi ini akan berfungsi pada lebih dari sekedar koleksi
Strings
, tetapi tanpa fungsi kelas satu, Anda tidak dapat mengganti predikat (kondisi di dalamif
), sehingga kode ini cenderung disalin dan ditempelkan dan sedikit dimodifikasi.Gabungkan fungsi-fungsi kelas satu yang memberi Anda kemampuan untuk lulus predikat sebagai parameter, dengan pembatasan kekekalan yang membuatnya sangat menjengkelkan jika Anda tidak melakukannya, dan Anda menghasilkan blok bangunan sederhana seperti
filter
, seperti dalam kode Scala ini itu melakukan hal yang sama:Sekarang pikirkan tentang apa tipe sistem memeriksa untuk Anda, pada waktu kompilasi dalam kasus Scala, tetapi pemeriksaan ini juga dilakukan oleh sistem tipe dinamis saat pertama kali Anda menjalankannya:
list
harus semacam jenis yang mendukungfilter
metode, yaitu koleksi.list
harus memilikiisEmpty
metode yang mengembalikan boolean.Setelah hal-hal itu diperiksa, cara-cara lain apa yang tersisa bagi programmer untuk gagal? Saya tidak sengaja lupa
!
, yang menyebabkan kegagalan test case yang sangat jelas. Itu satu-satunya kesalahan yang tersedia untuk dibuat, dan saya hanya membuatnya karena saya langsung menerjemahkan dari kode yang diuji untuk kondisi terbalik.Pola ini terus berulang. Fungsi kelas satu memungkinkan Anda merombak berbagai hal menjadi utilitas kecil yang dapat digunakan kembali dengan semantik yang tepat, pembatasan seperti immutability memberi Anda dorongan untuk melakukannya, dan mengetik memeriksa parameter dari utilitas tersebut menyisakan sedikit ruang untuk mengacaukannya.
Tentu saja, ini semua tergantung pada programmer yang mengetahui bahwa fungsi penyederhanaan seperti
filter
sudah ada, dan dapat menemukannya, atau mengenali manfaat dari membuat sendiri. Cobalah untuk menerapkan ini sendiri di mana-mana hanya menggunakan rekursi ekor, dan Anda segera kembali ke kapal kompleksitas yang sama dengan versi imperatif, hanya lebih buruk. Hanya karena Anda dapat menulisnya dengan sangat sederhana, bukan berarti versi sederhananya jelas.sumber
Saya tidak berpikir ada korelasi yang signifikan antara kompilasi pemrograman fungsional dan kebenaran runtime. Mungkin ada beberapa korelasi antara kompilasi yang diketik secara statis dan kebenaran runtime, karena setidaknya Anda mungkin memiliki tipe yang tepat, jika Anda tidak melakukan casting.
Aspek bahasa pemrograman yang entah bagaimana dapat mengkorelasikan kompilasi yang berhasil dengan kebenaran jenis runtime, seperti yang Anda jelaskan, adalah pengetikan statis, dan bahkan kemudian, hanya jika Anda tidak melemahkan pemeriksa tipe dengan gips yang hanya dapat ditegaskan saat runtime (dalam lingkungan dengan nilai atau tempat yang diketik dengan kuat, mis. Java atau .Net) atau tidak sama sekali (di lingkungan di mana informasi jenis hilang atau dengan pengetikan yang lemah, mis. C dan C ++).
Namun, pemrograman fungsional sendiri dapat membantu dengan cara lain, seperti menghindari data bersama dan keadaan yang bisa berubah.
Kedua aspek bersama-sama mungkin memiliki korelasi yang signifikan dalam kebenaran, tetapi Anda harus menyadari bahwa tidak memiliki kesalahan kompilasi dan runtime mengatakan apa-apa, secara tegas, tentang kebenaran dalam arti yang lebih luas, seperti dalam program melakukan apa yang seharusnya dilakukan dan gagal dengan cepat lebih dari input tidak valid atau kegagalan runtime yang tidak terkendali. Untuk itu, Anda memerlukan aturan bisnis, persyaratan, kasus penggunaan, pernyataan, tes unit, tes integrasi, dll. Pada akhirnya, setidaknya menurut saya, mereka memberikan kepercayaan diri yang jauh lebih baik daripada pemrograman fungsional, pengetikan statis atau keduanya.
sumber
Penjelasan untuk manajer:
Program fungsional seperti sebuah mesin besar di mana semuanya terhubung, tabung, kabel. [Mobil]
Program prosedural seperti bangunan dengan ruangan yang berisi mesin kecil, menyimpan produk parsial dalam nampan, mendapatkan produk parsial dari tempat lain. [Sebuah pabrik]
Jadi ketika mesin fungsional sudah cocok: ia terikat untuk menghasilkan sesuatu. Jika kompleks prosedural berjalan, Anda mungkin mengawasi efek tertentu, menimbulkan kekacauan, tidak menjamin fungsinya. Bahkan jika Anda memiliki daftar periksa dari segala sesuatu yang terintegrasi dengan benar, ada begitu banyak keadaan, situasi yang mungkin terjadi (produk parsial tergeletak, ember meluap, hilang), sehingga jaminan sulit untuk diberikan.
Tetapi serius, kode prosedural tidak menentukan semantik dari hasil yang diinginkan sebanyak kode fungsional. Pemrogram prosedural mungkin lebih mudah lolos dengan kode dan data tidak langsung, dan memperkenalkan beberapa cara untuk melakukan satu hal (beberapa di antaranya tidak sempurna). Biasanya data asing dibuat. Pemrogram fungsional mungkin membutuhkan waktu lebih lama ketika masalahnya menjadi lebih kompleks?
Bahasa fungsional yang diketik kuat masih dapat melakukan data dan analisis aliran yang lebih baik. Dengan bahasa prosedural, tujuan suatu program sering harus didefinisikan di luar program, sebagai analisis kebenaran formal.
sumber