Saya akan menjelaskan secara singkat tugas utama
Ini terdiri dari pembuatan bahasa pemrograman yang akan nyaman baik untuk menjelaskan model domain dan untuk bekerja dengannya. Untuk membuat deskripsi model sealami mungkin, dapat dipahami oleh manusia dan mendekati spesifikasi perangkat lunak. Tetapi pada saat yang sama, itu harus menjadi bagian dari kode dalam bahasa pemrograman yang lengkap. Untuk itu, model akan berbentuk ontologi yang terdiri dari fakta-fakta konkret, konsep-konsep abstrak dan relasi-relasi diantara keduanya. Fakta akan menggambarkan pengetahuan langsung tentang bidang subjek, dan konsep serta hubungan logis di antara mereka - strukturnya.
Selain alat pemodelan, bahasa juga akan membutuhkan alat untuk menyiapkan data awal untuk model, membuat elemen secara dinamis, memproses hasil kueri ke dalamnya, membuat elemen model yang lebih nyaman untuk dijelaskan dalam bentuk algoritme. Semua ini jauh lebih mudah dilakukan dengan mendeskripsikan urutan kalkulasi secara eksplisit. Misalnya, menggunakan OOP atau pendekatan fungsional.
Dan, tentunya kedua bagian bahasa tersebut harus saling berinteraksi secara erat dan saling melengkapi. Sehingga mereka dapat dengan mudah digabungkan dalam satu aplikasi dan menyelesaikan setiap jenis masalah dengan alat yang paling nyaman.
Saya akan memulai cerita saya dengan pertanyaan mengapa bahkan membuat bahasa seperti itu, mengapa bahasa campuran dan di mana itu akan berguna. Dalam artikel berikutnya, saya berencana untuk membuat tinjauan singkat tentang teknologi dan kerangka kerja yang memungkinkan Anda untuk menggabungkan gaya deklaratif dengan imperatif atau fungsional. Lebih lanjut, dimungkinkan untuk meninjau bahasa untuk menjelaskan ontologi, merumuskan persyaratan dan prinsip dasar dari bahasa hibrid baru dan, pertama-tama, komponen deklaratifnya. Terakhir, jelaskan konsep dan elemen dasarnya. Setelah itu, kami akan mempertimbangkan masalah apa yang muncul saat menggunakan paradigma deklaratif dan imperatif bersama-sama dan bagaimana pemecahannya. Kami juga akan menganalisis beberapa masalah implementasi bahasa, misalnya, algoritma inferensi. Terakhir, mari kita lihat salah satu contoh penerapannya.
Memilih gaya bahasa pemrograman yang tepat merupakan syarat penting untuk kualitas kode
Banyak dari kita harus berurusan dengan mendukung proyek kompleks yang dibuat oleh orang lain. Ada baiknya jika tim memiliki orang-orang yang akrab dengan kode proyek dan dapat menjelaskan cara kerjanya, ada dokumentasi, kodenya bersih dan dapat dimengerti. Tetapi pada kenyataannya, ini sering terjadi dengan cara yang berbeda - penulis kode berhenti jauh sebelum Anda masuk ke proyek ini, tidak ada dokumentasi sama sekali, atau sangat terpisah-pisah dan ketinggalan jaman, dan tentang logika bisnis dari komponen yang diperlukan, seorang analis bisnis atau proyek - manajer hanya bisa mengatakan secara umum. Dalam hal ini, kebersihan dan kelengkapan kode sangat penting.
Kualitas kode memiliki banyak aspek, salah satunya adalah pemilihan bahasa pemrograman yang tepat yang harus sesuai dengan masalah yang sedang diselesaikan. Semakin mudah dan alami seorang pengembang menerapkan idenya dalam kode, semakin cepat dia dapat menyelesaikan masalah dan semakin sedikit kesalahan yang akan dia buat. Kami sekarang memiliki sejumlah besar paradigma pemrograman untuk dipilih, masing-masing dengan area aplikasinya sendiri. Misalnya, pemrograman fungsional lebih disukai untuk aplikasi yang berfokus pada komputasi karena memberikan lebih banyak fleksibilitas untuk menyusun, menggabungkan, dan menggunakan kembali fungsi yang melakukan operasi pada data. Pemrograman berorientasi objekmenyederhanakan pembuatan struktur dari data dan fungsi melalui enkapsulasi, pewarisan, polimorfisme. OOP cocok untuk aplikasi berorientasi data. Pemrograman logika cocok untuk masalah berbasis aturan yang memerlukan pengerjaan dengan tipe data yang kompleks dan didefinisikan secara rekursif seperti pohon dan grafik, dan cocok untuk memecahkan masalah kombinatorial. Selain itu, pemrograman multi-agen yang reaktif, digerakkan oleh peristiwa, memiliki cakupannya sendiri.
Bahasa pemrograman tujuan umum modern dapat mendukung banyak paradigma. Menggabungkan paradigma fungsional dan OOP telah menjadi arus utama sejak lama.
Pemrograman logika fungsional hybrid juga memiliki sejarah panjang, tetapi tidak pernah melampaui dunia akademis. Setidaknya semua perhatian diberikan pada kombinasi pemrograman OOP yang logis dan wajib (saya berencana untuk membicarakannya secara lebih rinci di salah satu publikasi berikutnya). Meskipun, menurut pendapat saya, pendekatan logis bisa sangat berguna dalam bidang tradisional OOP - aplikasi server sistem informasi perusahaan. Anda hanya perlu melihatnya dari sudut yang sedikit berbeda.
Mengapa saya menganggap gaya pemrograman deklaratif diremehkan
Saya akan mencoba memperkuat sudut pandang saya.
Untuk melakukan ini, pertimbangkan seperti apa solusi perangkat lunak. Komponen utamanya adalah: bagian klien (desktop, seluler, aplikasi web); sisi server (sekumpulan layanan terpisah, layanan mikro, atau aplikasi monolitik); sistem manajemen data (relasional, berorientasi dokumen, berorientasi objek, database grafik, layanan caching, indeks pencarian). Sebuah solusi perangkat lunak harus berinteraksi dengan lebih dari sekedar orang - pengguna. Integrasi dengan layanan eksternal yang memberikan informasi melalui API adalah tugas yang umum. Selain itu, sumber data dapat berupa dokumen audio dan video, teks bahasa alami, konten halaman web, log peristiwa, data medis, pembacaan sensor, dll.
Di satu sisi, aplikasi server menyimpan data dalam satu atau lebih database. Di sisi lain, ia menanggapi permintaan yang datang dari titik akhir API, memproses pesan masuk, dan menanggapi peristiwa. Struktur pesan dan kueri hampir tidak pernah cocok dengan struktur yang disimpan dalam database. Format data input / output dirancang untuk penggunaan eksternal, dioptimalkan untuk konsumen informasi ini dan menyembunyikan kompleksitas aplikasi. Format data yang disimpan dioptimalkan untuk sistem penyimpanannya, misalnya, untuk model data relasional. Oleh karena itu, diperlukan beberapa konsep lapisan perantara yang memungkinkan untuk menggabungkan input / output aplikasi dengan sistem penyimpanan data. Biasanya, lapisan middleware ini disebut lapisan logika bisnis dan mengimplementasikan aturan dan prinsip perilaku untuk objek di domain.
Tugas menghubungkan konten database ke objek aplikasi juga tidak mudah. Jika struktur tabel di penyimpanan sesuai dengan struktur konsep di tingkat aplikasi, maka Anda dapat menggunakan teknologi ORM. Tetapi untuk kasus yang lebih kompleks daripada akses ke catatan dengan kunci primer dan operasi CRUD, Anda harus mengalokasikan lapisan logika terpisah untuk bekerja dengan database. Biasanya, skema database dibuat seumum mungkin, sehingga berbagai layanan dapat bekerja dengannya. Masing-masing memetakan skema data ini ke model objeknya sendiri. Struktur aplikasi menjadi lebih membingungkan jika aplikasi tidak bekerja dengan satu penyimpanan data, tetapi dengan beberapa jenis penyimpanan yang berbeda, memuat data dari sumber pihak ketiga, misalnya, melalui API layanan lain.Dalam hal ini, perlu untuk membuat model domain terpadu dan memetakan data dari berbagai sumber ke sana.
Dalam beberapa kasus, model domain dapat memiliki struktur multi-level yang kompleks. Misalnya, ketika menyusun laporan analitis, beberapa indikator dapat dibangun atas dasar indikator lainnya, yang nantinya akan menjadi sumber untuk menyusun ketiga, dll. Selain itu, data masukan dapat berbentuk semi-terstruktur. Data ini tidak memiliki skema yang ketat seperti, misalnya, dalam model data relasional, tetapi berisi semacam markup yang memungkinkan Anda mengekstrak informasi yang berguna darinya. Contoh data tersebut dapat berupa sumber Web Semantik, hasil scraping web, dokumen, log peristiwa, pembacaan sensor, hasil praproses untuk data tidak terstruktur seperti teks, video dan gambar, dll. Skema data dari sumber-sumber ini akan dibangun secara eksklusif di tingkat aplikasi. Akan ada juga kode,mengonversi data sumber menjadi objek logika bisnis.
Jadi, aplikasi tidak hanya berisi algoritme dan perhitungan, tetapi juga sejumlah besar informasi tentang struktur model domain - struktur konsepnya, hubungannya, hierarki, aturan untuk membangun beberapa konsep berdasarkan yang lain, aturan untuk mengubah konsep antara berbagai lapisan aplikasi, dll. Saat kami menyusun dokumentasi atau proyek, kami mendeskripsikan informasi ini secara deklaratif - dalam bentuk struktur, diagram, pernyataan, definisi, aturan, deskripsi dalam bahasa alami. Akan lebih mudah bagi kita untuk berpikir seperti ini. Sayangnya, deskripsi ini tidak selalu dapat diekspresikan dalam kode dengan cara alami yang sama.
Mari kita pertimbangkan contoh kecil dan berspekulasi bagaimana implementasinya akan terlihat menggunakan paradigma pemrograman yang berbeda
Katakanlah kita memiliki 2 file CSV. Di file pertama:
Kolom pertama berisi ID klien.
Yang kedua berisi tanggal.
Yang ketiga - jumlah yang ditagih,
Yang keempat - jumlah pembayaran.
Di file kedua:
Kolom pertama menyimpan ID klien.
Yang kedua - nama.
Yang ketiga adalah alamat email.
Mari perkenalkan beberapa definisi:
Faktur menyertakan ID klien, tanggal, jumlah faktur, jumlah pembayaran, dan utang dari sel satu baris file 1.
Jumlah utang adalah perbedaan antara jumlah yang ditagih dan jumlah pembayaran.
Pelanggan dijelaskan menggunakan ID pelanggan, nama dan alamat email dari sel satu baris di file 2.
Faktur yang belum dibayar adalah faktur utang positif.
Akun ditautkan ke pelanggan berdasarkan nilai ID pelanggan.
Debitur adalah pelanggan yang memiliki setidaknya satu faktur yang belum dibayar, yang tanggal 1 bulan lebih lama dari tanggal saat ini.
Defaulter yang jahat adalah pelanggan yang memiliki lebih dari 3 faktur yang belum dibayar.
Lebih lanjut, dengan menggunakan definisi ini, Anda dapat menerapkan logika mengirim pengingat ke semua debitur, mengirimkan data tentang orang yang mangkir terus-menerus kepada kolektor, menghitung denda atas jumlah utang, menyusun berbagai laporan, dll.
Dalam bahasa pemrograman fungsionallogika bisnis seperti itu diimplementasikan dengan menggunakan sekumpulan struktur data dan fungsi untuk transformasinya. Selain itu, struktur data pada dasarnya dipisahkan dari fungsi. Akibatnya, model, dan terutama komponennya seperti hubungan antar entitas, disembunyikan di dalam sekumpulan fungsi, dioleskan di atas kode program. Hal ini menciptakan celah besar antara deskripsi deklaratif model dan implementasi perangkat lunaknya dan memperumit pemahamannya. Apalagi jika modelnya memiliki volume yang besar.
Penataan program dalam berorientasi objekgaya membantu mengurangi masalah ini. Setiap entitas domain diwakili oleh sebuah objek yang bidang datanya sesuai dengan atribut entitas. Dan relasi antar entitas diimplementasikan dalam bentuk relasi antar objek, sebagian berdasarkan prinsip OOP - pewarisan, abstraksi data dan polimorfisme, sebagian lagi menggunakan pola desain. Namun dalam banyak kasus, hubungan harus diimplementasikan dengan mengenkodenya dalam metode objek. Selain itu, selain membuat kelas yang mewakili entitas, Anda juga memerlukan struktur data untuk memesannya, algoritme untuk mengisi struktur ini, dan mencari informasi di dalamnya.
Dalam contoh dengan debitur, kita dapat mendeskripsikan kelas yang mendeskripsikan struktur dari konsep "Akun" dan "Klien". Tetapi logika membuat objek, menautkan akun dan objek pelanggan satu sama lain sering kali diterapkan secara terpisah di kelas atau metode pabrik. Untuk konsep debitur dan faktur yang belum dibayar, kelas terpisah tidak diperlukan sama sekali, objek mereka dapat diperoleh dengan menyaring klien dan faktur di mana mereka dibutuhkan. Akibatnya, beberapa konsep model akan diimplementasikan sebagai kelas secara eksplisit, beberapa secara implisit, pada level objek. Beberapa hubungan antar konsep berada dalam metode kelas yang sesuai, dan beberapa terpisah. Implementasi model akan diolesi di seluruh kelas dan metode, dicampur dengan logika tambahan penyimpanan, pencarian, pemrosesan, konversi format. Ini akan membutuhkan usaha untuk menemukan model ini dalam kode Anda dan memahaminya.
Yang paling dekat dengan deskripsi adalah implementasi model konseptual dalam bahasa representasi pengetahuan . Contoh bahasa tersebut adalah Prolog, Datalog, OWL, Flora dan lain-lain. Saya berencana untuk membicarakan bahasa-bahasa ini di terbitan ketiga. Mereka didasarkan pada logika orde pertama atau fragmennya, misalnya logika deskriptif. Bahasa-bahasa ini memungkinkan dalam bentuk deklaratif untuk menentukan spesifikasi solusi dari masalah, untuk mendeskripsikan struktur objek atau fenomena yang dimodelkan dan hasil yang diharapkan. Dan mesin pencari built-in secara otomatis akan menemukan solusi yang memenuhi kondisi yang ditentukan. Implementasi model domain dalam bahasa-bahasa tersebut akan sangat ringkas, mudah dimengerti dan mendekati deskripsi dalam bahasa natural.
Misalnya, implementasi masalah debitur dalam Prolog akan sangat mendekati definisi dari contoh. Untuk melakukan ini, sel tabel perlu direpresentasikan sebagai fakta, dan definisi dari contoh - sebagai aturan. Untuk membandingkan akun dan pelanggan, cukup menentukan hubungan di antara mereka dalam aturan, dan nilai spesifik mereka akan ditampilkan secara otomatis.
Pertama, kita mendeklarasikan fakta dengan isi tabel dalam format: ID tabel, baris, kolom, nilai:
cell(“Table1”,1,1,”John”).
Kemudian kami akan memberi nama untuk setiap kolom:
clientId(Row, Value) :- cell(“Table1”, Row, 1, Value).
Kemudian Anda dapat menggabungkan semua kolom menjadi satu konsep:
bill(Row, ClientId, Date, AmountToPay, AmountPaid) :- clientId(Row, ClientId), date(Row, Date), amountToPay(Row, AmountToPay), amountPaid(Row, AmountPaid).
unpaidBill(Row, ClientId, Date, AmountToPay, AmountPaid) :- bill(Row, ClientId, Date, AmountToPay, AmountPaid), AmountToPay > AmountPaid.
debtor(ClientId, Name, Email) :- client(ClientId, Name, Email), unpaidBill(_, ClientId, _, _, _).
Dll
Kesulitan akan dimulai saat bekerja dengan model: saat mengimplementasikan logika pengiriman pesan, mentransfer data ke layanan lain, kalkulasi algoritmik yang kompleks. Kelemahan Prolog adalah deskripsi urutan tindakannya. Penerapan deklaratifnya, bahkan dalam kasus sederhana, dapat terlihat sangat tidak wajar dan membutuhkan upaya dan keterampilan yang signifikan. Selain itu, sintaks Prolog tidak terlalu dekat dengan model berorientasi objek, dan deskripsi konsep gabungan yang kompleks dengan sejumlah besar atribut akan cukup sulit untuk dipahami.
Bagaimana kita mendamaikan bahasa pengembangan fungsional atau berorientasi objek arus utama dengan sifat deklaratif model domain?
Pendekatan paling terkenal adalah desain berorientasi objek (Domain-Driven Design). Metodologi ini memfasilitasi pembuatan dan implementasi model domain yang kompleks. Ini menentukan bahwa semua konsep model diekspresikan dalam kode secara eksplisit di lapisan logika bisnis. Konsep model dan elemen program yang mengimplementasikannya harus sedekat mungkin satu sama lain dan sesuai dengan satu bahasa, dapat dimengerti oleh programmer dan ahli materi pelajaran.
Model domain kaya untuk contoh dengan debitur tambahan akan berisi kelas untuk konsep "Faktur belum dibayar" dan "Debitur", kelas agregat untuk menggabungkan konsep akun dan pelanggan, pabrik untuk membuat objek. Implementasi dan dukungan model seperti itu lebih memakan waktu, dan kodenya rumit - apa yang sebelumnya dapat dilakukan dalam satu baris memerlukan beberapa kelas dalam model kaya. Akibatnya, dalam praktiknya, pendekatan ini hanya masuk akal jika tim besar mengerjakan model skala yang kompleks.
Dalam beberapa kasus, solusinya dapat berupa kombinasi dari bahasa pemrograman fungsional atau berorientasi objek dasar dan sistem representasi pengetahuan eksternal.... Model domain dapat ditransfer ke basis pengetahuan eksternal, misalnya, di Prolog atau OWL, dan hasil kueri diproses di tingkat aplikasi. Tetapi pendekatan ini memperumit solusi, entitas yang sama harus diterapkan dalam kedua bahasa, interaksi di antara keduanya harus disiapkan melalui API, didukung tambahan oleh sistem representasi pengetahuan, dll. Oleh karena itu, ini dibenarkan hanya jika modelnya besar dan kompleks, memerlukan inferensi logis. Untuk sebagian besar tugas, ini akan berlebihan. Selain itu, model ini tidak selalu dapat dilepaskan tanpa rasa sakit dari aplikasi.
Pilihan lain untuk menggabungkan basis pengetahuan dan aplikasi OOP adalah pemrograman berorientasi ontologi.... Pendekatan ini didasarkan pada persamaan antara alat deskripsi ontologi dan model pemrograman objek. Kelas, entitas dan atribut ontologi yang ditulis, misalnya, dalam bahasa OWL, dapat secara otomatis dipetakan ke kelas, objek, dan bidang model objek. Dan kemudian kelas yang dihasilkan dapat digunakan bersama dengan kelas aplikasi lainnya. Sayangnya, implementasi dasar dari ide ini akan terbatas cakupannya. Bahasa ontologi cukup ekspresif dan tidak semua komponen ontologi dapat diubah menjadi kelas OOP dengan cara yang sederhana dan alami. Selain itu, untuk mengimplementasikan inferensi lengkap, tidak cukup hanya dengan membuat sekumpulan kelas dan objek. Ia membutuhkan informasi tentang unsur-unsur ontologi dalam bentuk yang eksplisit, misalnya dalam bentuk meta-class.Saya berencana untuk membicarakan pendekatan ini secara lebih rinci di salah satu publikasi berikutnya.
Ada juga pendekatan ekstrim untuk pengembangan perangkat lunak seperti pengembangan yang didorong oleh model . Menurutnya, tugas utama pengembangan menjadi pembuatan model domain, dari mana kode program kemudian dibuat secara otomatis. Namun dalam praktiknya, solusi radikal tersebut tidak selalu cukup fleksibel, terutama dalam hal kinerja program. Pencipta model seperti itu harus menggabungkan peran programmer dan analis bisnis. Oleh karena itu, pendekatan ini tidak bisa menyingkirkan pendekatan tradisional untuk mengimplementasikan model dalam bahasa pemrograman tujuan umum.
Semua pendekatan ini agak rumit dan masuk akal untuk model yang sangat kompleks, sering kali dijelaskan secara terpisah dari logika penggunaannya. Saya mau sesuatu yang lebih ringan, lebih nyaman dan alami. Sehingga dengan bantuan satu bahasa dimungkinkan untuk mendeskripsikan baik model dalam bentuk deklaratif maupun algoritma untuk penggunaannya. Oleh karena itu, saya berpikir tentang bagaimana menggabungkan paradigma berorientasi objek atau fungsional (sebut saja komponen komputasi ) dan paradigma deklaratif (sebut saja komponen pemodelan ) dalam satu bahasa pemrograman hybrid . Sekilas, paradigma-paradigma ini terlihat saling bertentangan, namun yang lebih menarik untuk dicoba.
Jadi, tujuannya adalah untuk membuat bahasa yang sesuai untuk pemodelan konseptual berdasarkan data semi-terstruktur dan terfragmentasi. Bentuk model harus mendekati ontologi dan terdiri dari deskripsi entitas domain dan hubungan antar keduanya. Kedua komponen bahasa tersebut harus terintegrasi erat, termasuk di tingkat semantik.
Elemen ontologi harus merupakan entitas dari bahasa tingkat pertama - mereka dapat diteruskan ke fungsi sebagai argumen, ditetapkan ke variabel, dll. Karena model - ontologi akan menjadi salah satu elemen utama dari program, maka pendekatan pemrograman ini dapat disebut berorientasi ontologis. Menggabungkan deskripsi model dengan algoritme untuk penggunaannya akan membuat kode program lebih mudah dipahami dan alami bagi manusia, akan membawanya lebih dekat ke model konseptual domain, dan akan menyederhanakan pengembangan dan pemeliharaan perangkat lunak.
Cukup untuk pertama kali. Di posting berikutnya, saya ingin berbicara tentang beberapa teknologi modern yang menggabungkan gaya imperatif dan deklaratif - PL / SQL, Microsoft LINQ dan GraphQL. Bagi mereka yang tidak ingin menunggu rilis semua publikasi di Habré, ada teks lengkap dalam gaya ilmiah dalam bahasa Inggris, tersedia di tautan:
Pemrograman Berorientasi Ontologi Hibrid untuk Pemrosesan Data Semi-Terstruktur .