Mengapa mimpi pemrograman deklaratif tidak terwujud? Apa saja hambatan nyata yang menghadang? Sebagai contoh sederhana mengapa saya tidak bisa mengatakannya
sort(A) is defined by sort(A) in perm(A) && asc(sort(A))
dan secara otomatis mendapatkan algoritma pengurutan dari itu. perm
berarti permutasi dan asc
berarti naik.
declarative-programming
davidk01
sumber
sumber
n!
permutasi dari urutan dan dalam kasus terburuk algoritma Anda harus mencoba semuanya untuk menemukan yang diurutkan. Waktu faktorial sama buruknya dengan algoritma untuk memproses suatu urutan dapat dilakukan.Jawaban:
Ada beberapa jawaban yang sangat bagus. Saya akan mencoba berkontribusi dalam diskusi.
Pada topik deklaratif, pemrograman logika dalam Prolog, ada buku bagus "The Craft of Prolog" oleh Richard O'Keefe . Ini adalah tentang menulis program yang efisien menggunakan bahasa pemrograman yang memungkinkan Anda menulis program yang sangat tidak efisien. Dalam buku ini, ketika membahas implementasi efisien dari beberapa algoritma (dalam bab "Metode Pemrograman"), penulis mengambil pendekatan berikut:
Pengamatan yang paling mencerahkan (untuk saya) yang saya dapat lakukan sambil bekerja melalui ini:
Ya, versi terakhir dari implementasi jauh lebih efisien daripada spesifikasi "deklaratif" yang dimulai oleh penulis. Itu masih sangat deklaratif, ringkas, dan mudah dimengerti. Apa yang terjadi di antaranya adalah bahwa solusi akhir menangkap sifat-sifat masalah yang tidak diketahui oleh solusi awal.
Dengan kata lain, saat mengimplementasikan solusi, kami telah menggunakan sebanyak mungkin pengetahuan kami tentang masalah yang kami bisa. Membandingkan:
untuk:
Selain kecil: definisi seperti yang Anda berikan menarik karena sangat umum. Namun, saya tidak bisa lepas dari perasaan bahwa itu sengaja mengabaikan fakta bahwa permutasi adalah masalah kombinatorial. Ini adalah sesuatu yang sudah kita ketahui ! Ini bukan kritik, hanya pengamatan.
Adapun pertanyaan sebenarnya: bagaimana cara bergerak maju? Nah, salah satu caranya adalah memberikan sebanyak mungkin pengetahuan tentang masalah yang kita nyatakan ke komputer.
Upaya terbaik yang saya tahu untuk benar-benar menyelesaikan masalah disajikan dalam buku-buku yang ditulis bersama oleh Alexander Stepanov, "Elemen Pemrograman" dan "Dari Matematika ke Pemrograman Generik" . Saya sedih tidak sampai pada tugas meringkas (atau bahkan sepenuhnya memahami) segala sesuatu dalam buku-buku ini. Namun, pendekatan yang ada untuk mendefinisikan algoritma perpustakaan dan struktur data yang efisien (atau bahkan optimal), di bawah ketentuan bahwa semua properti input yang relevan diketahui sebelumnya. Hasil akhirnya adalah:
Mengenai mengapa hal itu belum terjadi, yah, ilmu komputer adalah bidang yang benar-benar muda, dan kami masih berusaha untuk benar-benar menghargai kebaruan sebagian besar darinya.
PS
Untuk memberi Anda gambaran tentang apa yang saya maksud dengan "memperbaiki implementasi": ambil contoh masalah mudah untuk mendapatkan elemen terakhir dari daftar, di Prolog. Solusi deklaratif kanonik adalah dengan mengatakan:
Di sini, makna deklaratif dari
append/3
adalah:Karena dalam argumen kedua untuk
append/3
kita memiliki daftar dengan hanya satu elemen, dan argumen pertama diabaikan (garis bawah), kita mendapatkan pemisahan dari daftar asli yang membuang bagian depan daftar (List1
dalam konteksappend/3
) dan menuntut agar bagian belakang (List2
dalam konteksappend/3
) memang daftar dengan hanya satu elemen: jadi, itu adalah elemen terakhir.The aktual implementasi disediakan oleh SWI-Prolog , bagaimanapun, mengatakan:
Ini masih deklaratif dengan baik. Baca dari atas ke bawah, katanya:
Alasan mengapa implementasi ini disediakan adalah untuk mengatasi masalah-masalah praktis seputar model pelaksanaan Prolog. Idealnya, itu seharusnya tidak membuat perbedaan implementasi yang digunakan. Demikian pula, kita dapat mengatakan:
Jika Anda ingin mengisi diskusi tidak meyakinkan tentang apa yang baik, Prolog deklaratif, cukup tanyakan beberapa pertanyaan dan jawaban di tag Prolog pada Stack Overflow .
sumber
Bahasa logis sudah melakukan ini. Anda dapat mendefinisikan jenis dengan cara yang sama seperti yang Anda lakukan.
Masalah utama adalah kinerja. Komputer mungkin hebat dalam menghitung banyak hal, tetapi pada dasarnya mereka bodoh. Setiap keputusan "pintar" yang dibuat komputer diprogram ke dalamnya oleh seorang programmer. Dan keputusan ini biasanya digambarkan bukan dengan bagaimana hasil akhirnya terlihat, tetapi bagaimana cara mencapai, langkah demi langkah, hasil akhir ini.
Bayangkan kisah seorang Golem . Jika Anda mencoba memberinya perintah abstrak, maka yang terbaik, dia akan melakukannya secara tidak efisien dan paling buruk, akan melukai dirinya sendiri, Anda atau orang lain. Tetapi jika Anda mendeskripsikan apa yang Anda inginkan dalam perincian sebesar mungkin, Anda dijamin akan menyelesaikan tugas dengan efektif dan efisien.
Merupakan tugas programmer untuk memutuskan level abstraksi apa yang akan digunakan. Untuk aplikasi yang Anda buat, apakah Anda akan naik level tinggi dan mendeskripsikannya secara abstrak dan membuat kinerja menjadi rendah atau kotor, menghabiskan waktu 10x lebih banyak untuk itu, tetapi mendapatkan algoritma yang 1000x lebih berkinerja tinggi?
sumber
Selain poin Euphoric yang sangat baik , saya ingin menambahkan bahwa kita sudah menggunakan bahasa deklaratif di banyak tempat di mana mereka bekerja dengan baik, yaitu menggambarkan keadaan yang tidak mungkin berubah atau meminta sesuatu yang mana komputer sebenarnya dapat menghasilkan kode efisien dengan dirinya sendiri:
HTML menyatakan apa isi dari suatu halaman web.
CSS menyatakan seperti apa bentuk berbagai elemen dalam halaman web.
Setiap basis data relasional memiliki bahasa definisi data yang menyatakan apa struktur basis data itu.
SQL jauh lebih dekat dengan deklaratif daripada keharusan, karena Anda memberi tahu apa yang ingin Anda lihat dan perencana kueri database mencari tahu bagaimana sebenarnya mewujudkannya.
Orang bisa berpendapat bahwa sebagian besar file konfigurasi (.vimrc, .profile, .bashrc, .gitconfig) menggunakan bahasa khusus domain yang sebagian besar bersifat deklaratif
sumber
Abstraksi bocor
Anda dapat menerapkan sistem deklaratif di mana Anda menyatakan apa yang Anda inginkan, dan kompilator atau penerjemah menemukan urutan eksekusi. Manfaat teoretisnya adalah membebaskan Anda dari keharusan memikirkan 'bagaimana', dan Anda tidak perlu merinci implementasi ini. Namun, dalam praktiknya untuk komputasi tujuan umum Anda masih harus berpikir tentang 'bagaimana' dan menulis semua jenis trik sambil tetap mengingat bagaimana ini akan diterapkan, karena jika tidak kompiler dapat (dan sering akan) memilih implementasi yang akan sangat, sangat, sangat lambat (mis. operasi n! di mana n sudah cukup).
Dalam contoh khusus Anda, Anda akan mendapatkan A algoritma sorting - itu tidak berarti bahwa Anda akan mendapatkan yang baik atau bahkan satu agak digunakan. Definisi yang Anda berikan, jika diterapkan secara harfiah (sebagai kompiler kemungkinan akan) menghasilkan http://en.wikipedia.org/wiki/Bogosort yang tidak dapat digunakan untuk kumpulan data yang lebih besar - secara teknis benar, tetapi membutuhkan keabadian untuk mengurutkan seribu angka .
Untuk beberapa domain terbatas, Anda dapat menulis sistem yang hampir selalu berhasil dalam menentukan implementasi yang baik, misalnya, SQL. Untuk komputasi tujuan umum yang tidak bekerja dengan baik - Anda dapat menulis sistem di, katakanlah, Prolog tetapi Anda harus memvisualisasikan bagaimana sebenarnya deklarasi Anda akan dikonversi menjadi perintah eksekusi yang penting pada akhirnya, dan yang kehilangan banyak deklaratif yang diharapkan manfaat pemrograman.
sumber
Desidabilitas komputasi adalah alasan terpenting mengapa pemrograman deklaratif belum terbukti semudah kelihatannya.
Banyak masalah yang relatif mudah dinyatakan telah terbukti tidak dapat diputuskan atau memiliki kompleksitas NP-complete untuk dipecahkan. Ini sering terjadi ketika kita memperhitungkan kelas dan klasifikasi negatif, kemampuan menghitung dan rekursi.
Saya ingin mencontohkan ini dengan beberapa domain yang terkenal.
Keputusan tentang kelas CSS mana yang akan digunakan membutuhkan pengetahuan dan pertimbangan semua aturan CSS. Menambahkan aturan baru mungkin membatalkan semua keputusan lain. Kelas CSS negatif sengaja tidak ditambahkan ke bahasa, karena masalah NP-complete, tetapi kurangnya kelas negatif mempersulit keputusan desain CSS.
Di dalam pengoptimal kueri (SQL), ada masalah tidak jelas dalam memutuskan untuk bergabung, indeks mana yang digunakan dan memori mana yang dialokasikan untuk hasil sementara mana. Ini adalah masalah NP-complete yang diketahui dan menyulitkan desain-database dan formulasi kueri. Untuk memformulasikannya secara berbeda: saat mendesain basis data atau kueri, perancang perlu mengetahui tindakan dan urutan tindakan yang cenderung dilakukan oleh pengoptimal kueri. Seorang insinyur berpengalaman membutuhkan pengetahuan tentang heuristik yang digunakan oleh vendor database utama.
File konfigurasi bersifat deklaratif, tetapi konfigurasi tertentu sulit untuk dideklarasikan. Misalnya, untuk mengkonfigurasi fitur dengan benar, seseorang perlu mempertimbangkan versi akun, penyebaran (dan riwayat penerapan), kemungkinan penggantian manual dan kemungkinan konflik dengan pengaturan lain. Untuk memvalidasi konfigurasi dengan benar mungkin menjadi masalah NP-complete.
Hasilnya adalah bahwa komplikasi ini mengejutkan pemula, mereka merusak 'keindahan' pemrograman deklaratif dan menyebabkan beberapa insinyur mencari solusi lain. Migrasi insinyur yang tidak berpengalaman dari SQL ke NoSQL mungkin telah dipicu oleh kompleksitas yang mendasari database relasional.
sumber
Kami memiliki perbedaan dalam declarativeness bahasa pemrograman yang digunakan dengan baik dalam verifikasi logika digital.
Biasanya logika digital dijelaskan pada level transfer register (RTL) di mana level logika sinyal antara register didefinisikan. Untuk memeriksa bahwa kami semakin menambahkan properti yang didefinisikan dengan cara yang lebih abstrak dan deklaratif.
Salah satu bahasa yang lebih deklaratif / himpunan bagian bahasa disebut PSL untuk Bahasa Spesifikasi Properti. Saat menguji model RTL dari pengali di mana, misalnya semua operasi logika shift dan penambahan beberapa clock cycle perlu ditentukan; Anda dapat menulis properti dengan cara
assert that when enable is high, this output will equal the multiplication of these two inputs after no more than 8 clock cycles
. Deskripsi PSL kemudian dapat diperiksa bersama-sama dengan RTL dalam simulasi, atau PSL dapat secara resmi terbukti tahan untuk deskripsi RTL.Model PSL yang lebih deklaratif memaksa seseorang untuk menggambarkan perilaku yang sama dengan deskripsi RTL tetapi dengan cara yang cukup berbeda yang dapat secara otomatis diperiksa terhadap RTL untuk melihat apakah mereka setuju.
sumber
Sebagian besar masalahnya adalah bagaimana Anda memodelkan data; dan pemrograman deklaratif tidak membantu di sini. Dalam bahasa imperatif Anda sudah memiliki banyak perpustakaan yang melakukan banyak hal untuk Anda, jadi Anda hanya perlu tahu harus menelepon apa. Dengan cara tertentu orang mungkin mempertimbangkan pemrograman deklaratif ini (mungkin contoh terbaik untuk ini adalah Stream API di Java 8 ). Setelah ini, abstraksi sudah diselesaikan dan pemrograman deklaratif tidak diperlukan.
Juga, seperti yang telah dikatakan, bahasa pemrograman logika sudah melakukan apa yang Anda inginkan. Orang mungkin mengatakan masalahnya adalah kinerja, tetapi dengan perangkat keras dan penelitian saat ini di bidang ini, hal-hal dapat ditingkatkan untuk siap digunakan untuk produksi; sebenarnya Prolog digunakan untuk hal-hal AI, tapi saya percaya hanya oleh akademisi.
Perlu dicatat bahwa ini berlaku untuk bahasa pemrograman untuk tujuan umum. Untuk bahasa spesifik domain, bahasa deklaratif jauh lebih baik; SQL mungkin adalah contoh terbaik.
sumber
Akan terlihat seperti ini .. {(apa pun => membaca file & panggil url) | panggil url & baca file} Namun, ini adalah tindakan yang harus dilakukan, dan keadaan sistem berubah sebagai hasilnya, tetapi itu tidak jelas dari sumbernya.
Deklaratif dapat menggambarkan mesin negara yang terbatas dan transisinya. FSM seperti kebalikan dari deklaratif tanpa tindakan, bahkan jika satu-satunya tindakan adalah mengubah negara ke negara berikutnya.
Keuntungan menggunakan metode ini adalah bahwa transisi dan tindakan dapat ditentukan oleh predikat yang berlaku untuk banyak transisi, bukan hanya satu.
Saya tahu ini terdengar agak aneh, tetapi pada tahun 2008 saya menulis program generator yang menggunakan metode ini, dan C ++ yang dihasilkan adalah 2 hingga 15 kali lebih banyak dari sumbernya. Saya sekarang memiliki lebih dari 75.000 baris C ++ dari 20.000 baris input. Dua hal sesuai dengan ini: konsistensi dan kelengkapan.
Konsistensi: Tidak ada dua predikat yang dapat menjadi benar pada saat yang sama dapat menyiratkan tindakan yang tidak konsisten, karena keduanya x = 8 dan x = 9, atau keadaan berikutnya yang berbeda.
Kelengkapan: Logika untuk setiap transisi negara ditentukan. Ini mungkin sulit untuk memeriksa sistem dengan substrat N, dengan status> 2 ** N, tetapi ada metode kombinatorial yang menarik yang dapat memverifikasi semuanya. Pada tahun 1962 saya menulis fase 1 semacam sistem untuk mesin 7070, menggunakan semacam ini pembuatan kode kondisional dan debug kombinatorial. Dari 8.000 baris dalam semacam itu, jumlah bug dari hari rilis pertama selamanya adalah nol!
Fase dua semacam itu, 12.000 baris, memiliki lebih dari 60 kesalahan dalam dua bulan pertama. Ada banyak lagi yang bisa dikatakan tentang ini, tetapi berhasil. Jika pabrikan mobil menggunakan metode ini untuk memeriksa firmware, kami tidak akan melihat kegagalan yang kami lihat sekarang.
sumber
Tidak semuanya bisa direpresentasikan secara deklaratif.
Seringkali, Anda secara eksplisit ingin mengontrol aliran eksekusi
Sebagai contoh mengikuti pseudo-code:
if whatever read a file call an URL else call an URL write a file
Bagaimana Anda mewakilinya secara deklaratif?Tentu, mungkin ada beberapa cara eksotis untuk melakukannya. Seperti monad . Tetapi ini biasanya lebih rumit, rumit dan jauh lebih tidak intuitif daripada bagian prosedural mereka.
Semuanya bermuara pada kenyataan bahwa "berinteraksi" dengan lingkungan / sistem Anda tidak bersifat deklaratif. Segala sesuatu yang berkaitan dengan I / O pada dasarnya prosedural. Anda harus tahu persis kapan dan apa yang harus terjadi, dan dalam urutan apa.
Deklaratif bagus untuk segala sesuatu yang murni terkait dengan perhitungan. Seperti fungsi raksasa, Anda memasukkan X dan Anda mendapatkan Y. Itu bagus. Contoh untuk ini adalah HTML, input adalah teks, outputnya adalah apa yang Anda lihat di browser Anda.
sumber
if
/else
, dalam hal apa alternatif deklaratif akan terlihat? Ini jelas bukan bagianread
/write
/call
, karena mereka adalah daftar nilai deklaratif yang bagus (jika Anda menyiratkan mereka dibungkus{...; ...}
, mengapa tidak menyiratkan mereka dibungkus[..., ...]
?) Tentu saja, daftar hanyalah monoids gratis; banyak orang lain juga akan melakukannya. Saya tidak mengerti mengapa monad relevan di sini; mereka hanya API. Haskell beralih dari stream -> monads untuk membantu komposisi manual, tetapi bahasa logika dapat menyusun daftar secara otomatis.