Saya telah mendengar banyak kali ketika pengembang lain menggunakan frase itu untuk "beriklan" beberapa pola atau mengembangkan praktik terbaik. Sebagian besar waktu kalimat ini digunakan ketika Anda berbicara tentang manfaat pemrograman fungsional.
Ungkapan "Easy to reason about" telah digunakan apa adanya, tanpa penjelasan atau contoh kode. Jadi bagi saya itu menjadi seperti kata "buzz" berikutnya, yang lebih banyak digunakan pengembang "berpengalaman" dalam pembicaraan mereka.
Pertanyaan: Bisakah Anda memberikan beberapa contoh "Tidak mudah untuk beralasan", sehingga dapat dibandingkan dengan contoh "Mudah untuk beralasan tentang"?
Jawaban:
Dalam benak saya, frasa "mudah beralasan tentang", merujuk pada kode yang mudah "dieksekusi di kepala Anda".
Ketika melihat sepotong kode, jika pendek, ditulis dengan jelas, dengan nama baik dan mutasi nilai minimal, maka secara mental mengerjakan apa yang dilakukan kode itu adalah tugas yang (relatif) mudah.
Sepotong kode panjang dengan nama yang buruk, variabel yang terus-menerus mengubah nilai dan percabangan berbelit-belit biasanya akan membutuhkan misalnya pena dan selembar kertas untuk membantu melacak keadaan saat ini. Karenanya kode seperti itu tidak dapat dengan mudah dikerjakan hanya di kepala Anda, Jadi kode tersebut tidak mudah dipikirkan.
sumber
"Code easy to reason about" almost exclusively alludes to its mathematical properties and formal verification
- yang kira-kira terdengar seperti jawaban atas pertanyaan. Anda mungkin ingin memposting itu sebagai jawaban alih-alih tidak setuju tentang apa jawaban (subyektif) dalam komentar.Mekanisme atau sepotong kode mudah dipikirkan tentang kapan Anda perlu mempertimbangkan beberapa hal untuk memprediksi apa yang akan dilakukannya, dan hal-hal yang perlu Anda perhitungkan mudah tersedia.
Fungsi sebenarnya tanpa efek samping dan tidak ada keadaan mudah dipikirkan karena output sepenuhnya ditentukan oleh input, yang ada di sana dalam parameter.
Sebaliknya, objek dengan keadaan jauh lebih sulit untuk dipikirkan, karena Anda harus memperhitungkan keadaan apa objek tersebut ketika sebuah metode dipanggil, yang berarti Anda harus berpikir tentang situasi lain yang dapat mengarah pada objek lain yang berada dalam suatu objek. negara bagian tertentu.
Yang lebih buruk lagi adalah variabel global: untuk alasan tentang kode yang membaca variabel global, Anda perlu memahami di mana dalam kode Anda variabel itu dapat diatur dan mengapa - dan bahkan mungkin tidak mudah untuk menemukan semua tempat itu.
Salah satu hal tersulit untuk dipikirkan adalah pemrograman multithreaded dengan status bersama, karena Anda tidak hanya memiliki status, Anda memiliki beberapa utas mengubahnya sekaligus, jadi untuk alasan tentang apa yang dilakukan sepotong kode ketika dijalankan oleh satu utas Anda harus memungkinkan untuk kemungkinan bahwa pada setiap titik eksekusi, beberapa utas lainnya (atau beberapa di antaranya!) mungkin mengeksekusi hampir semua bagian lain dari kode dan mengubah data yang Anda operasikan tepat di bawah mata Anda. Secara teori, itu dapat dikelola dengan mutex / monitor / bagian kritis / apa pun yang Anda sebut itu, tetapi dalam praktiknya tidak ada manusia biasa yang benar-benar dapat melakukan hal itu secara andal kecuali mereka secara drastis membatasi keadaan bersama dan / atau paralelisme menjadi sangat kecil. bagian dari kode.
sumber
make
atau bahkan spesialisasi template C ++ dan kelebihan fungsi) dapat menempatkan Anda kembali pada posisi mempertimbangkan keseluruhan program. Bahkan sekali Anda merasa telah menemukan definisi sesuatu, bahasa tersebut memungkinkan deklarasi yang lebih spesifik di mana saja dalam program untuk menimpanya. IDE Anda mungkin bisa membantu.sealed
tidak menjadi default?Dalam kasus pemrograman fungsional, makna "Mudah untuk alasan" adalah sebagian besar bersifat deterministik. Maksud saya, input yang diberikan akan selalu menghasilkan output yang sama. Anda dapat melakukan apa pun yang Anda inginkan pada program, selama Anda tidak menyentuh potongan kode itu, itu tidak akan rusak.
Di sisi lain, OO biasanya lebih sulit untuk dipikirkan karena "output" yang dihasilkan tergantung pada keadaan internal setiap objek yang terlibat. Cara khas yang dimanifestasikannya adalah efek samping yang tidak terduga : ketika mengubah satu bagian kode, bagian yang tampaknya tidak terkait akan terputus.
... Kelemahan pemrograman fungsional tentu saja dalam prakteknya, banyak yang ingin Anda lakukan adalah IO dan mengelola negara.
Namun, ada banyak hal lain yang lebih sulit untuk dipikirkan, dan saya setuju dengan @Kilian bahwa concurrency adalah contoh utama. Sistem terdistribusi juga.
sumber
Menghindari diskusi yang lebih luas, dan menjawab pertanyaan spesifik:
Saya merujuk Anda ke "The Story of Mel, Programmer Nyata" , sepotong cerita rakyat programmer yang berasal dari tahun 1983 dan karena itu dianggap sebagai 'legenda', untuk profesi kami.
Ini menceritakan kisah seorang programmer menulis kode yang lebih suka teknik misterius sedapat mungkin, termasuk kode referensi diri dan modifikasi diri, dan eksploitasi bug mesin yang disengaja:
Ini adalah contoh kode yang 'sulit untuk dipikirkan'.
Tentu saja, Mel tidak akan setuju ...
sumber
Saya dapat memberikan contoh, dan yang sangat umum.
Pertimbangkan kode C # berikut.
Sekarang pertimbangkan alternatif ini.
Dalam contoh kedua, saya tahu persis apa yang dilakukan kode ini sekilas. Ketika saya melihat
Select
, saya tahu daftar item sedang dikonversi menjadi daftar yang lain. Ketika saya melihatWhere
, saya tahu bahwa beberapa item sedang disaring. Sekilas, saya bisa mengerti apanames
itu dan memanfaatkannya dengan efektif.Ketika saya melihat sebuah
for
loop, saya tidak tahu apa yang terjadi sampai saya benar-benar membaca kode. Dan kadang-kadang saya harus melacaknya untuk memastikan saya telah memperhitungkan semua efek samping. Saya harus melakukan sedikit pekerjaan untuk memahami nama apa itu (di luar definisi tipe) dan bagaimana menggunakannya secara efektif. Jadi, contoh pertama lebih sulit untuk dipertimbangkan daripada yang kedua.Pada akhirnya, menjadi mudah dipikirkan di sini juga tergantung pada pemahaman metode
Select
dan LINQWhere
. Jika Anda tidak mengenal mereka, maka kode kedua lebih sulit untuk dipikirkan pada awalnya. Tetapi Anda hanya membayar biaya untuk memahaminya sekali. Anda membayar biaya untuk memahamifor
loop setiap kali Anda menggunakan satu dan lagi setiap kali itu berubah. Kadang-kadang biayanya layak dibayar, tetapi biasanya "lebih mudah untuk alasan" jauh lebih penting.sumber
Ungkapan terkait adalah (I parafrase),
Contoh yang relatif "mudah untuk dipikirkan" mungkin adalah RAII .
Contoh lain mungkin menghindari pelukan maut : jika Anda bisa memegang kunci dan mendapatkan kunci lain, dan ada banyak kunci, sulit untuk memastikan tidak ada skenario di mana pelukan maut mungkin terjadi. Menambahkan aturan seperti "hanya ada satu kunci (global)", atau, "Anda tidak diizinkan memperoleh kunci kedua saat Anda memegang kunci pertama", membuat sistem ini relatif mudah untuk dipikirkan.
sumber
CComPtr<>
) dengan fungsi C-style (CoUninitialize()
). Saya menemukan itu contoh yang aneh, juga, sejauh saya ingat Anda memanggil CoInitialize / CoUninitialize pada lingkup modul dan untuk seluruh modul seumur hidup, misalnya dalammain
atau dalamDllMain
, dan bukan dalam beberapa ruang lingkup fungsi lokal yang berumur pendek seperti ditunjukkan dalam contoh. .main
) untuk aplikasi. Anda menginisialisasi COM saat startup, dan kemudian Anda unisialisasi itu tepat sebelum keluar. Kecuali Anda memiliki objek global, seperti COM smart pointer, menggunakan paradigma RAII. Mengenai gaya pencampuran: objek global yang menginisialisasi COM dalam ctornya dan tidak diinisialisasi dalam dtornya bisa diterapkan, dan apa yang disarankan Raymond, tetapi halus dan tidak mudah untuk dipikirkan.Inti dari pemrograman adalah analisis kasus. Alan Perlis mengomentari hal ini dalam Epigram # 32: Programmer tidak diukur dengan kecerdikan dan logika mereka tetapi dengan kelengkapan analisis kasus mereka.
Situasi mudah dipikirkan jika analisis kasusnya mudah. Ini berarti bahwa ada beberapa kasus untuk dipertimbangkan, atau, gagal, beberapa kasus khusus - mungkin ada beberapa ruang kasus yang besar, tetapi yang runtuh karena beberapa keteraturan, atau menyerah pada teknik penalaran seperti induksi.
Versi rekursif dari suatu algoritma, misalnya, biasanya lebih mudah untuk dipikirkan daripada versi imperatif, karena itu tidak berkontribusi pada kasus yang berlebihan yang muncul melalui mutasi variabel keadaan pendukung yang tidak muncul dalam verison rekursif. Selain itu, struktur rekursi sedemikian rupa sehingga cocok dengan pola pembuktian-per-induksi matematis. Kita tidak harus mempertimbangkan kompleksitas seperti varian loop dan prasyarat ketat terlemah dan yang lainnya.
Aspek lain dari ini adalah struktur ruang kasing. Lebih mudah untuk memikirkan tentang situasi yang memiliki pembagian flat, atau sebagian besar datar ke dalam kasus dibandingkan dengan situasi kasus hirarkis: kasus dengan sub-kasus dan sub-kasus dan sebagainya.
Properti sistem yang menyederhanakan penalaran adalah ortogonalitas : ini adalah properti bahwa kasus yang mengatur subsistem tetap independen ketika subsistem tersebut digabungkan. Tidak ada kombinasi yang menimbulkan "kasus khusus". Jika sesuatu empat kasus digabungkan dengan sesuatu tiga kasus secara ortogonal, ada dua belas kasus, tetapi idealnyasetiap kasus merupakan kombinasi dari dua kasus yang tetap independen. Dalam beberapa hal, sebenarnya tidak ada dua belas kasus; kombinasinya hanyalah "fenomena seperti kasus yang muncul" yang tidak perlu kita khawatirkan. Artinya, kita masih memiliki empat kasus yang dapat kita pikirkan tanpa mempertimbangkan tiga kasus lain di subsistem lainnya, dan sebaliknya. Jika beberapa kombinasi harus diidentifikasi secara khusus dan diberkahi dengan logika tambahan, maka alasannya lebih sulit. Dalam kasus terburuk, setiap kombinasi memiliki penanganan khusus, dan kemudian benar-benar ada dua belas kasus baru, yang merupakan tambahan dari empat dan tiga yang asli.
sumber
Tentu. Ambil konkurensi:
Bagian kritis ditegakkan oleh mutex: mudah dimengerti karena hanya ada satu prinsip (dua utas eksekusi tidak bisa masuk ke bagian kritis secara bersamaan), tetapi rentan terhadap inefisiensi dan kebuntuan.
Model-model alternatif, misalnya pemrograman atau aktor bebas-kunci: berpotensi jauh lebih elegan dan kuat, tetapi sangat sulit untuk dipahami, karena Anda tidak dapat lagi bergantung pada (tampaknya) konsep dasar seperti "sekarang tulis nilai ini ke tempat itu".
Mudah dipikirkan adalah salah satu aspek dari suatu metode. Tetapi memilih metode mana yang akan digunakan membutuhkan mempertimbangkan semua aspek dalam kombinasi.
sumber
Mari kita batasi tugas untuk alasan formal. Karena alasan humor atau inventif atau puitis memiliki hukum yang berbeda.
Meski begitu, ekspresinya didefinisikan secara samar-samar, dan tidak dapat diatur secara ketat. Tapi bukan berarti itu harus tetap redup bagi kita. Mari kita bayangkan bahwa suatu struktur sedang melewati beberapa pengujian dan mendapatkan nilai untuk titik yang berbeda. Tanda yang bagus untuk setiap poin berarti bahwa strukturnya nyaman di setiap aspek dan karenanya, "Mudah untuk dijelaskan".
Struktur "Mudah dipikirkan" harus mendapatkan nilai bagus untuk yang berikut:
Apakah tes ini subyektif? Ya tentu saja. Tetapi ungkapan itu sendiri juga subjektif. Apa yang mudah bagi satu orang, tidak mudah bagi orang lain. Jadi, tes harus berbeda untuk domain yang berbeda.
sumber
Gagasan bahasa fungsional yang memungkinkan untuk dipikirkan berasal dari sejarah mereka, khususnya ML yang dikembangkan sebagai bahasa pemrograman yang analog dengan konstruksi yang digunakan oleh Logic for Computable Functions untuk penalaran. Sebagian besar bahasa fungsional lebih dekat dengan pemrograman formal calculii daripada imperatif, sehingga terjemahan dari kode ke input dari sistem sistem penalaran kurang memberatkan.
Sebagai contoh dari sistem penalaran, dalam pi-kalkulus, setiap lokasi memori yang dapat berubah dalam bahasa imperatif perlu direpresentasikan sebagai proses paralel yang terpisah, sedangkan urutan operasi fungsional adalah proses tunggal. Empat puluh tahun sejak dari LFC theorem prover, kami bekerja dengan GB RAM sehingga memiliki ratusan proses kurang menjadi masalah - Saya telah menggunakan pi-kalkulus untuk menghilangkan potensi deadlock dari beberapa ratus baris C ++, meskipun representasi memiliki ratusan Memproses pemikir menghabiskan ruang negara di sekitar 3GB dan menyembuhkan bug yang terputus-putus. Ini tidak mungkin terjadi di tahun 70-an atau membutuhkan superkomputer di awal 1990-an, sedangkan ruang keadaan dari program bahasa fungsional dengan ukuran yang sama cukup kecil untuk dipikirkan saat itu.
Dari jawaban yang lain, frasa tersebut menjadi frasa-gebrakan meskipun banyak kesulitan yang membuatnya sulit untuk beralasan tentang bahasa-bahasa imperatif yang terkikis oleh hukum Moore.
sumber
Mudah untuk diartikan adalah istilah yang spesifik secara budaya, itulah sebabnya sangat sulit untuk memberikan contoh konkret. Ini adalah istilah yang dilabuhkan kepada orang-orang yang melakukan penalaran.
"Mudah untuk alasan" sebenarnya adalah frase yang sangat deskriptif diri. Jika seseorang melihat kode, dan ingin menjelaskan apa yang dilakukannya, mudah =)
Oke, hancurkan. Jika Anda melihat kode, biasanya Anda ingin melakukan sesuatu. Anda ingin memastikan bahwa itu melakukan apa yang menurut Anda harus dilakukan. Jadi Anda mengembangkan teori tentang apa yang harus dilakukan kode, dan kemudian Anda beralasan untuk mencoba berdebat mengapa kode itu benar-benar berfungsi. Anda mencoba untuk berpikir tentang kode seperti manusia (bukan seperti komputer) dan mencoba merasionalisasi argumen tentang apa yang dapat dilakukan kode.
Kasus terburuk untuk "alasan mudah" adalah ketika satu-satunya cara untuk memahami apa yang dilakukan oleh kode adalah dengan masuk baris demi baris melalui kode seperti mesin Turing untuk semua input. Dalam hal ini, satu-satunya cara untuk alasan apapun tentang kode ini adalah untuk mengubah diri menjadi komputer dan jalankan di kepala Anda. Contoh-contoh kasus terburuk ini mudah dilihat dalam kontes pemrograman usang, seperti 3 baris PERL ini yang mendekripsi RSA:
Untuk alasan yang mudah, sekali lagi, istilah ini sangat kultural. Anda harus mempertimbangkan:
Masing-masing mempengaruhi "mudah untuk alasan" berbeda. Ambil ketrampilan nalar sebagai contoh. Ketika saya mulai di perusahaan saya, disarankan agar saya mengembangkan skrip saya di MATLAB karena "mudah untuk dipikirkan." Mengapa? Nah, semua orang di perusahaan tahu MATLAB. Jika saya memilih bahasa yang berbeda, akan lebih sulit bagi siapa pun untuk mengerti saya. Nevermind bahwa keterbacaan MATLAB mengerikan untuk beberapa tugas, hanya karena itu tidak dirancang untuk mereka. Kemudian, ketika karier saya berkembang, Python menjadi semakin populer. Tiba-tiba kode MATLAB menjadi "sulit untuk dipikirkan" dan Python adalah bahasa preferensi untuk menulis kode yang mudah dipikirkan.
Pertimbangkan juga apa yang mungkin dimiliki pembaca. Jika Anda dapat mengandalkan pembaca Anda untuk mengenali FFT dalam sintaksis tertentu, itu "lebih mudah untuk alasan tentang" kode jika Anda tetap pada sintaks itu. Ini memungkinkan mereka melihat file teks sebagai kanvas yang Anda lukis FFT ke, daripada harus masuk ke detail seluk beluk. Jika Anda menggunakan C ++, cari tahu seberapa nyaman pembaca Anda dengan
std
perpustakaan. Seberapa mereka suka pemrograman fungsional? Beberapa idiom yang keluar dari wadah perpustakaan sangat bergantung pada gaya idomatik yang Anda inginkan.Penting juga untuk memahami jenis pertanyaan apa yang mungkin ingin dijawab oleh pembaca. Apakah pembaca Anda sebagian besar peduli dengan pemahaman dangkal terhadap kode, atau mereka mencari bug di dalam perut?
Betapa yakin pembaca harus benar-benar menarik. Dalam banyak kasus, alasan kabur sebenarnya cukup untuk mengeluarkan produk. Dalam kasus lain, seperti perangkat lunak penerbangan FAA, pembaca akan ingin memiliki alasan kuat. Saya menemukan sebuah kasus di mana saya berpendapat untuk menggunakan RAII untuk tugas tertentu, karena "Anda bisa mengaturnya dan melupakannya ... itu akan melakukan hal yang benar." Saya diberitahu bahwa saya salah tentang itu. Mereka yang akan membahas kode ini bukan tipe orang yang "hanya ingin melupakan detailnya." Bagi mereka, RAII lebih seperti tongkat gantung, memaksa mereka untuk memikirkan semua hal yang bisa terjadi ketika Anda meninggalkan ruang lingkup.
sumber