Mengapa ada dukungan terbatas untuk Desain berdasarkan Kontrak di sebagian besar bahasa pemrograman modern?

40

Saya baru-baru ini menemukan Desain oleh Kontrak (DbC) dan saya menemukan cara yang sangat menarik untuk menulis kode. Di antara hal-hal lain, tampaknya akan menawarkan:

  • Dokumentasi yang lebih baik. Karena kontrak adalah dokumentasi, tidak mungkin bagi seseorang untuk ketinggalan zaman. Selain itu, karena kontrak menentukan dengan tepat apa yang dilakukan rutin, itu membantu untuk mendukung penggunaan kembali.
  • Debugging sederhana. Karena eksekusi program berhenti ketika kontrak gagal, kesalahan tidak dapat menyebar, dan pernyataan spesifik yang dilanggar mungkin akan disorot. Ini menawarkan dukungan selama pengembangan dan selama pemeliharaan.
  • Analisis statis yang lebih baik. DbC pada dasarnya hanyalah implementasi dari logika Hoare, dan prinsip yang sama harus diterapkan.

Biaya, dalam perbandingan tampaknya agak kecil:

  • Mengetik jari ekstra. Karena kontrak harus dijabarkan.
  • Mengambil sejumlah pelatihan untuk merasa nyaman dengan kontrak penulisan.

Sekarang, karena terbiasa dengan Python terutama, saya menyadari bahwa sebenarnya mungkin untuk menulis prakondisi (hanya melemparkan pengecualian untuk input yang tidak pantas) dan bahkan mungkin untuk menggunakan pernyataan untuk menguji lagi postconditions tertentu. Tetapi tidak mungkin untuk mensimulasikan fitur tertentu seperti 'lama' atau 'hasil' tanpa sihir tambahan yang pada akhirnya akan dianggap tidak Pythonic. (Selain itu, ada beberapa perpustakaan yang menawarkan dukungan, tetapi pada akhirnya saya mendapatkan getaran itu akan salah untuk menggunakannya, karena kebanyakan pengembang tidak.) Saya berasumsi bahwa itu adalah masalah yang sama untuk semua bahasa lain (kecuali tentu saja , Eiffel).

Intuisi saya memberi tahu saya bahwa kurangnya dukungan harus merupakan hasil dari semacam penolakan terhadap praktik tersebut, tetapi pencarian online belum membuahkan hasil. Saya ingin tahu apakah seseorang dapat menjelaskan mengapa sebagian besar bahasa modern tampaknya menawarkan sedikit dukungan? Apakah DbC cacat atau terlalu mahal? Atau apakah itu hanya usang karena Pemrograman Ekstrim dan metodologi lainnya?

Ceasar Bautista
sumber
Kedengarannya seperti cara yang terlalu rumit untuk melakukan pemrograman berbasis tes, tanpa manfaat juga menguji program Anda.
Dan
3
@Dan, tidak juga, saya menganggapnya lebih sebagai perpanjangan dari sistem tipe. misalnya fungsi tidak hanya mengambil argumen bilangan bulat, dibutuhkan bilangan bulat yang secara kontrak diwajibkan lebih besar dari nol
Carson63000
4
@Dan kontrak kode dapat secara signifikan mengurangi jumlah tes yang perlu Anda lakukan.
Rei Miyasaka
24
@ Dan, saya lebih suka mengatakan bahwa TDD adalah kontrak orang miskin itu, bukan sebaliknya.
SK-logic
Dalam bahasa dinamis Anda dapat "menghias" objek Anda dengan kontrak berdasarkan pada bendera opsional. Saya punya contoh implementasi yang menggunakan flag lingkungan untuk secara opsional menambal objek yang ada dengan kontrak. Ya, dukungan itu bukan asli, tetapi mudah untuk ditambahkan. Hal yang sama berlaku untuk test harness, mereka bukan asli tetapi mereka mudah ditambahkan / ditulis.
Raynos

Jawaban:

9

Bisa dibilang mereka didukung di hampir setiap bahasa pemrograman.

Yang Anda butuhkan adalah "pernyataan".

Ini mudah dikodekan sebagai pernyataan "jika":

if (!assertion) then AssertionFailure();

Dengan ini, Anda dapat menulis kontrak dengan menempatkan pernyataan seperti itu di bagian atas kode Anda untuk kendala input; yang ada di titik balik adalah kendala keluaran. Anda bahkan dapat menambahkan invarian di seluruh kode Anda (walaupun sebenarnya bukan bagian dari "desain berdasarkan kontrak").

Jadi saya berpendapat mereka tidak tersebar luas karena programmer terlalu malas untuk membuat kode, bukan karena Anda tidak bisa melakukannya.

Anda dapat menjadikan ini sedikit lebih efisien di sebagian besar bahasa dengan mendefinisikan konstanta boolean "memeriksa" waktu kompilasi dan sedikit merevisi pernyataan:

if (checking & !Assertion) then AssertionFailure();

Jika Anda tidak menyukai sintaksis, Anda dapat menggunakan berbagai teknik abstraksi bahasa seperti makro.

Beberapa bahasa modern memberi Anda sintaks yang bagus untuk ini, dan itulah yang saya pikir Anda maksud dengan "dukungan bahasa modern". Itu dukungan, tetapi sangat tipis.

Apa yang sebagian besar dari bahkan bahasa modern tidak memberi Anda adalah pernyataan "temporal" (lebih dari keadaan sewenang-wenang sebelumnya atau berikut [operator temporal "akhirnya"], yang Anda butuhkan jika Anda ingin menulis kontrak yang sangat menarik. JIKA pernyataan tidak akan membantu kamu disini.

Ira Baxter
sumber
Masalah yang saya temukan dengan hanya memiliki akses ke menegaskan adalah bahwa tidak ada cara yang efektif untuk memeriksa postconditions pada perintah karena Anda sering perlu membandingkan kondisi postcondition terhadap kondisi prekondisi (Eiffel menyebut ini 'lama' dan secara otomatis meneruskannya ke rutinitas postcondition. .) Dengan Python, fungsi ini bisa dibuat dengan sepele menggunakan dekorator, tetapi muncul pendek ketika tiba saatnya untuk mematikan pernyataan.
Ceasar Bautista
Berapa banyak negara sebelumnya yang sebenarnya disimpan oleh Eiffel? Karena secara wajar tidak dapat mengetahui bagian mana yang dapat Anda akses / modifikasi tanpa menyelesaikan masalah penghentian (dengan menganalisis fungsi Anda), maka ia harus menyimpan status mesin lengkap, atau sebagai sebuah kesalahan, hanya bagian yang sangat dangkal saja. Saya curiga yang terakhir; dan ini dapat "disimulasikan" dengan tugas skalar sederhana sebelum prasyarat. Saya akan senang mengetahui Eiffel melakukan sebaliknya.
Ira Baxter
7
... baru saja memeriksa cara kerja Eiffel. "old <exp>" adalah nilai dari <exp> saat masuk ke fungsi, jadi ia melakukan salinan dangkal di pintu masuk fungsi seperti yang saya harapkan. Anda bisa melakukannya juga. Saya setuju bahwa kompiler mengimplementasikan sintaks untuk pre / post / old lebih nyaman daripada melakukan semua ini dengan tangan, tetapi intinya adalah seseorang dapat melakukannya dengan tangan dan itu sebenarnya tidak sulit. Kami kembali ke programmer yang malas.
Ira Baxter
@IraBaxter No. Code menjadi lebih sederhana jika Anda dapat memisahkan kontrak dari logika yang sebenarnya. Juga, jika compiler dapat memberitahu kontrak dan kode terpisah, dapat mengurangi duplikasi oleh banyak . Misalnya dalam D, Anda dapat mendeklarasikan kontrak pada antarmuka atau superclass dan pernyataan akan diterapkan ke semua kelas pelaksana / perluasan, terlepas dari kode dalam fungsinya. Misalnya dengan python atau Java, Anda harus menjalankan seluruh supermetode dan mungkin membuang hasilnya jika Anda hanya ingin kontrak diperiksa tanpa duplikasi. Ini sangat membantu menerapkan kode yang sesuai LSP bersih.
marstato
@marstato: Saya sudah setuju bahwa dukungan dalam bahasa adalah hal yang baik.
Ira Baxter
15

Seperti yang Anda katakan, Desain oleh Kontrak adalah fitur di Eiffel, yang telah lama menjadi salah satu bahasa pemrograman yang dihormati di masyarakat tetapi tidak pernah populer.

DbC tidak dalam bahasa yang paling populer karena itu relatif baru-baru ini bahwa komunitas pemrograman arus utama telah menerima bahwa menambahkan kendala / harapan pada kode mereka adalah hal yang "masuk akal" untuk diharapkan oleh para programmer. Sudah umum bagi para programmer untuk memahami betapa berharganya unit-testing, dan itu meresap hingga programmer lebih menerima memasukkan kode untuk memvalidasi argumen mereka dan melihat manfaatnya. Tetapi satu dekade yang lalu, mungkin sebagian besar programmer akan mengatakan "itu hanya pekerjaan ekstra untuk hal-hal yang Anda tahu akan selalu baik-baik saja."

Saya pikir jika Anda pergi ke pengembang rata-rata hari ini dan berbicara tentang kondisi setelahnya, mereka akan mengangguk dengan antusias dan berkata, "Oke, itu seperti pengujian unit." Dan jika Anda berbicara tentang pra-kondisi, mereka akan berkata "OK, itu seperti validasi parameter, yang tidak selalu kita lakukan, tetapi, Anda tahu, saya kira tidak apa-apa ..." Dan kemudian jika Anda berbicara tentang invarian , mereka akan mulai berkata, "Wah, berapa biaya overhead ini? Berapa banyak lagi bug yang akan kita tangkap?" dll.

Jadi saya pikir masih ada jalan panjang sebelum DbC diadopsi secara luas.

Larry OBrien
sumber
OTOH, para programmer utama telah terbiasa menulis pernyataan itu cukup lama. Kurangnya preprosesor yang dapat digunakan dalam bahasa arus utama paling modern membuat praktik yang bagus ini tidak efisien, tetapi masih umum untuk C dan C ++. Itu membuat comeback sekarang dengan Kontrak Kode Microsoft (berdasarkan, AFAIK, penulisan bytecode untuk build rilis).
SK-logic
8

Intuisi saya memberi tahu saya bahwa kurangnya dukungan harus merupakan hasil dari semacam penolakan terhadap latihan, ...

Salah.

Ini adalah praktik desain . Ini dapat diwujudkan secara eksplisit dalam kode (gaya Eiffel) atau secara implisit dalam kode (sebagian besar bahasa) atau dalam unit test. Praktik desain ada dan bekerja dengan baik. Dukungan bahasa ada di seluruh peta. Namun demikian, hadir dalam banyak bahasa dalam kerangka uji unit.

Saya ingin tahu apakah seseorang dapat menjelaskan mengapa sebagian besar bahasa modern tampaknya menawarkan sedikit dukungan? Apakah DbC cacat atau terlalu mahal?

Itu mahal. Dan. Lebih penting lagi, ada beberapa hal yang tidak dapat dibuktikan dalam bahasa yang diberikan. Pengakhiran loop, misalnya, tidak dapat dibuktikan dalam bahasa pemrograman, itu membutuhkan kemampuan bukti "tingkat tinggi". Jadi beberapa jenis kontrak secara teknis tidak bisa diungkapkan.

Atau apakah itu hanya usang karena Pemrograman Ekstrim dan metodologi lainnya?

Tidak.

Kami kebanyakan menggunakan unit test untuk menunjukkan bahwa DbC terpenuhi.

Untuk Python, seperti yang Anda perhatikan, DbC berjalan di beberapa tempat.

  1. Docstring dan hasil tes docstring.

  2. Pernyataan untuk memvalidasi input dan output.

  3. Tes unit.

Lebih lanjut.

Anda dapat mengadopsi alat gaya pemrograman terpelajar sehingga Anda menulis dokumen yang menyertakan informasi DbC Anda dan yang menghasilkan skrip pengujian unit Python plus unit. Pendekatan pemrograman melek memungkinkan Anda untuk menulis literatur yang bagus yang mencakup kontrak dan sumber lengkap.

S.Lott
sumber
Anda dapat membuktikan kasus sepele dari terminasi loop, seperti iterasi di atas urutan hingga yang tetap. Ini perulangan umum yang tidak dapat ditunjukkan untuk berhenti secara sepele (karena bisa mencari solusi untuk dugaan matematika "menarik"); itulah inti dari Masalah Pemutusan Hubungan.
Donal Fellows
+1. Saya pikir Anda adalah satu-satunya yang telah membahas titik paling kritis - there are some things which cannot be proven. Verifikasi formal mungkin bagus, tetapi tidak semuanya dapat diverifikasi! Sehingga fitur itu sebenarnya membatasi apa yang sebenarnya bisa dilakukan oleh bahasa pemrograman!
Dipan Mehta
@DonalFellows: Karena kasus umum tidak dapat dibuktikan, sulit untuk menggabungkan banyak fitur yang (a) mahal dan (b) diketahui tidak lengkap. Maksud saya dalam jawaban ini adalah bahwa lebih mudah untuk menghindari semua fitur tersebut dan menghindari menetapkan ekspektasi yang salah akan bukti kebenaran formal secara umum, ketika ada batasan. Sebagai latihan desain (di luar bahasa) banyak teknik bukti dapat (dan harus) digunakan.
S.Lott
Sama sekali tidak mahal dalam bahasa seperti C ++ di mana memeriksa kontrak dikompilasi dalam rilis rilis. Dan menggunakan DBC cenderung menyebabkan rilis kode bangunan yang lebih ringan, karena Anda memasukkan lebih sedikit pemeriksaan runtime untuk program yang berada dalam keadaan hukum. Saya telah kehilangan hitungan jumlah basis kode godawful yang pernah saya lihat di mana banyak fungsi memeriksa status ilegal dan mengembalikan false, padahal seharusnya TIDAK pernah berada dalam status itu dalam versi rilis yang teruji dengan baik.
Kaitain
6

Hanya menebak. Mungkin bagian dari alasan yang tidak begitu populer adalah karena "Desain oleh Kontrak" adalah merek dagang oleh Eiffel.

DPD
sumber
3

Satu hipotesis adalah bahwa untuk program kompleks yang cukup besar, terutama yang dengan target bergerak, massa kontrak itu sendiri bisa menjadi buggy dan sulit untuk di-debug, atau lebih-lebih, daripada kode program saja. Seperti halnya pola apa pun, mungkin ada penggunaan masa lalu yang semakin berkurang, serta keuntungan yang jelas saat digunakan dengan cara yang lebih bertarget.

Kesimpulan lain yang mungkin adalah bahwa popularitas "bahasa yang dikelola" adalah bukti terkini dari dukungan desain-kontrak untuk fitur-fitur terkelola yang dipilih (susunan batas berdasarkan kontrak, dll.)

hotpaw2
sumber
> Massa kontrak sendiri bisa menjadi seperti kereta dan sulit untuk di-debug, atau lebih-lebih, daripada kode program saja saya belum pernah melihat ini.
Kaitain
2

Alasan bahwa sebagian besar bahasa utama tidak memiliki fitur DbC dalam bahasa adalah rasio biaya-manfaat dari penerapannya adalah tinggi untuk pelaksana bahasa.

satu sisi dari ini telah dilihat pada jawaban lain, unit test dan mekanisme runtime lainnya (atau bahkan beberapa mekanisme waktu kompilasi dengan pemrograman meta templat) dapat memberi Anda banyak kebaikan DbC. Oleh karena itu, walaupun ada manfaatnya hal itu tampaknya dipandang cukup sederhana.

Sisi lain adalah biaya, pas retro DbC ke bahasa yang ada kemungkinan terlalu besar perubahan melanggar dan sangat kompleks untuk di-boot. Memperkenalkan sintaks baru dalam bahasa tanpa merusak kode lama itu sulit. Memperbarui perpustakaan standar yang ada untuk menggunakan perubahan yang jauh jangkauannya akan mahal. Oleh karena itu kita dapat menyimpulkan bahwa menerapkan fitur DbC dalam bahasa yang ada memiliki biaya tinggi.

Saya juga mencatat bahwa konsep yang cukup banyak kontrak untuk template, dan karena itu agak terkait dengan DbC, dikeluarkan dari standar C ++ terbaru bahkan setelah bertahun-tahun mengerjakannya, diperkirakan mereka masih membutuhkan tahun kerja. Perubahan besar, luas, dan luas ke bahasa-bahasa ini terlalu sulit untuk diimplementasikan.

jk.
sumber
2

DbC akan digunakan lebih luas jika kontrak dapat diperiksa pada waktu kompilasi sehingga tidak mungkin untuk menjalankan program yang melanggar kontrak apa pun.

Tanpa dukungan kompiler, "DbC" hanyalah nama anter untuk "periksa invarian / asumsi dan berikan pengecualian jika dilanggar".

Ingo
sumber
Bukankah itu mengalami masalah penghentian?
Ceasar Bautista
@ Ciasar Itu tergantung. Beberapa asumsi dapat diperiksa, yang lain tidak. Misalnya ada tipe sistem yang memungkinkan untuk menghindari melewati daftar kosong sebagai argumen, atau mengembalikannya.
Ingo
Poin bagus (+1) meskipun Bertrand Meyer di bagian "Masterminds of programming" juga menyebutkan sistem penciptaan kelas secara acak dan menyerukan pengecekan pelanggaran kontrak. Jadi ini adalah pendekatan waktu kompilasi / run-time campuran, tapi saya ragu teknik ini bekerja di setiap situasi
Maksee
Itu benar sampai batas tertentu, meskipun itu harus menjadi kegagalan bencana daripada pengecualian (lihat di bawah). Keuntungan utama DBC adalah metodologi, yang benar-benar mengarah ke program yang dirancang lebih baik, dan jaminan bahwa setiap metode yang diberikan HARUS berada dalam keadaan hukum saat masuk, yang menyederhanakan banyak logika internal. Umumnya Anda tidak boleh berkecimpung dalam bisnis melempar pengecualian ketika sebuah kontrak dilanggar. Pengecualian harus digunakan di mana program HUKUM dalam keadaan ~ X, dan kode klien harus menangani ini. Sebuah kontrak akan mengatakan bahwa ~ X itu ilegal.
Kaitain
1

Saya punya penjelasan sederhana, kebanyakan orang (termasuk programmer) tidak ingin bekerja ekstra kecuali mereka melihatnya perlu. Pemrograman Avionik di mana keselamatan dianggap sangat penting. Saya belum pernah melihat sebagian besar proyek tanpanya.

Tetapi jika Anda mempertimbangkan pemrograman situs web, desktop, atau seluler - crash dan perilaku tak terduga kadang-kadang tidak dianggap buruk dan programmer hanya akan menghindari pekerjaan ekstra ketika melaporkan bug dan kemudian memperbaikinya dianggap cukup memadai.

Ini mungkin alasan saya pikir Ada tidak pernah mengambil di luar industri pemrograman penerbangan karena dibutuhkan lebih banyak pekerjaan pengkodean meskipun Ada adalah bahasa yang luar biasa dan jika Anda ingin membangun sistem yang andal, itu adalah bahasa terbaik untuk pekerjaan itu (tidak termasuk SPARK yang merupakan hak milik) bahasa berdasarkan Ada).

Desain oleh perpustakaan kontrak untuk C # adalah percobaan oleh Microsoft, dan mereka sangat berguna untuk membangun perangkat lunak yang andal, tetapi mereka tidak pernah mengambil momentum di pasar jika tidak Anda akan melihatnya sekarang sebagai bagian dari bahasa inti C #.

Pernyataan tidak sama dengan dukungan berfungsi penuh untuk kondisi pra / pasca dan invarian. Meskipun dapat mencoba untuk meniru mereka tetapi bahasa / kompiler dengan dukungan yang tepat melakukan analisis 'pohon sintaksis abstrak' dan memeriksa kesalahan logika yang hanya pernyataan tidak bisa.

Sunting: Saya melakukan pencarian dan berikut ini adalah diskusi terkait yang mungkin bisa membantu: https://stackoverflow.com/questions/4065001/are-there-any-provable-real-world-languages-scala

JERAMI
sumber
-2

Sebagian besar alasannya adalah sebagai berikut:

  1. Ini hanya tersedia dalam bahasa yang tidak populer
  2. Ini tidak perlu karena hal yang sama dapat dilakukan dengan cara yang berbeda dalam bahasa pemrograman yang ada oleh siapa saja yang benar-benar ingin melakukannya
  3. Sulit untuk dipahami dan digunakan - ini membutuhkan pengetahuan khusus untuk melakukannya dengan benar, jadi ada sejumlah kecil orang yang melakukannya
  4. itu memerlukan sejumlah besar kode untuk melakukannya - programmer lebih suka meminimalkan jumlah karakter yang mereka tulis - jika butuh sepotong kode panjang, pasti ada yang salah dengan itu
  5. keuntungannya tidak ada - itu tidak dapat menemukan bug yang cukup untuk membuatnya berharga
tp1
sumber
1
Jawaban Anda tidak diperdebatkan dengan baik. Anda hanya menyatakan pendapat Anda bahwa tidak ada keuntungan, bahwa itu memerlukan sejumlah besar kode, dan itu tidak perlu karena dapat dilakukan dengan bahasa yang ada (OP secara khusus membahas masalah ini!).
Andres F.
Saya tidak memikirkan pernyataan dll sebagai pengganti. Itu bukan cara yang tepat untuk melakukannya dalam bahasa yang ada (yaitu belum ditangani)
tp1
@ tp1, jika programmer benar-benar ingin meminimalkan pengetikan, mereka tidak akan pernah jatuh ke dalam sesuatu yang verbose dan fasih seperti Java. Dan ya, pemrograman itu sendiri membutuhkan "pengetahuan khusus" "untuk melakukannya dengan benar". Mereka yang tidak memiliki pengetahuan semacam itu seharusnya tidak diizinkan untuk membuat kode.
SK-logic
@ Sk-logic: Ya, sepertinya separuh dunia melakukan kesalahan OO hanya karena mereka tidak ingin menulis fungsi penerusan dari fungsi anggota ke fungsi anggota anggota data. Ini masalah besar dalam pengalaman saya. Ini secara langsung disebabkan oleh meminimalkan jumlah karakter untuk ditulis.
tp1
@ tp1, jika orang benar-benar ingin meminimalkan pengetikan, mereka bahkan tidak akan menyentuh OO. OOP secara alami fasih, bahkan dalam implementasi terbaiknya, seperti Smalltalk. Saya tidak akan mengatakan itu adalah properti yang buruk, terkadang kefasihan membantu.
SK-logic