Kami merancang bahasa pemrograman multi-paradigma. Bagian 6 - Meminjam dari SQL

Kami melanjutkan cerita tentang pembuatan bahasa pemrograman multi-paradigma yang menggabungkan gaya logika deklaratif dengan gaya berorientasi objek dan fungsional, yang akan memudahkan saat bekerja dengan data semi-terstruktur dan mengintegrasikan data dari sumber yang berbeda. Bahasa akan terdiri dari dua komponen yang terintegrasi erat satu sama lain: komponen deklaratif akan bertanggung jawab untuk mendeskripsikan model domain, dan komponen imperatif atau fungsional akan bertanggung jawab untuk mendeskripsikan algoritme untuk bekerja dengan model dan komputasi.



Komponen pemodelan bahasa hybrid adalah sekumpulan konsep-objek yang dihubungkan oleh hubungan logis. Saya berhasil berbicara tentang cara utama mendefinisikan konsep, termasuk pewarisan dan menentukan hubungan di antara mereka. Dan juga tentang beberapa nuansa pemrograman logis, termasuk semantik operator negasi dan logika tingkat tinggi. Daftar lengkap publikasi tentang topik ini dapat ditemukan di akhir artikel ini.



Di bidang bekerja dengan data, pemimpin yang tak terbantahkan adalah bahasa SQL. Beberapa fiturnya, yang ternyata sangat nyaman dalam praktiknya, seperti agregasi, kemudian bermigrasi ke pemrograman logika. Oleh karena itu, akan berguna untuk meminjam dari SQL sebanyak mungkin untuk komponen pemodelan. Dalam artikel ini, saya ingin menunjukkan kepada Anda bagaimana kueri bersarang, gabungan luar, dan agregasi dapat disematkan dalam definisi konsep. Saya juga akan berbicara tentang jenis konsep lain, yang dijelaskan menggunakan fungsi yang menghasilkan objek (entitas) dalam gaya algoritmik tanpa menggunakan pencarian logis. Dan saya akan menunjukkan kepada Anda bagaimana Anda dapat menggunakan array objek sebagai konsep induk dengan analogi dengan operasi SQL UNNESTyang mengonversi koleksi ke format tabel dan memungkinkan mereka untuk digabungkan ke tabel lain di klausa FROM .



Definisi konsep tanpa nama



Di dunia SQL, kueri bersarang adalah alat yang sering digunakan saat diperlukan untuk mendapatkan data perantara untuk diproses lebih lanjut di kueri utama. Komponen pemodelan tidak memiliki kebutuhan yang mendesak akan data perantara, karena cara untuk mendapatkannya dapat diformalkan sebagai konsep terpisah. Namun ada kasus di mana definisi konsep bersarang akan berguna.



Terkadang Anda perlu sedikit mengubah konsep, memilih atribut individualnya, memfilter nilainya. Jika modifikasi ini hanya dibutuhkan di satu tempat, maka tidak masuk akal untuk membuat konsep tersendiri dengan nama yang unik. Situasi ini sering dijumpai ketika konsep merupakan argumen untuk fungsi seperti ada , temukan, atau findOne , yang memeriksa dedusibilitas konsep, menemukan semua atau hanya objek (entitas) pertama dari konsep tersebut. Di sini Anda dapat menggambar analogi dengan fungsi anonim dalam bahasa pemrograman fungsional, yang sering digunakan sebagai argumen untuk fungsi seperti map , find , filter , dll.



Pertimbangkan sintaks untuk mendefinisikan konsep anonim. Secara umum, ini mengikuti sintaks dari definisi konsep umum, kecuali bahwa dalam beberapa kasus Anda dapat menghilangkan daftar atribut dan nama konsep anak. Jika konsep anonim digunakan sebagai argumen untuk ada, maka nama dan daftar atributnya tidak penting, cukup untuk memeriksa bahwa setidaknya ada beberapa hasil. Fungsi find dan findOne mungkin tidak memerlukan nama konsep jika keluaran tidak digunakan sebagai konsep keseluruhan, tetapi hanya sebagai sekumpulan atribut dalam array asosiatif. Jika tidak ada atribut yang ditentukan, maka mekanisme pewarisan digunakan secara default dan atribut akan diwarisi dari konsep induk. Jika tidak ada nama konsep yang ditentukan, itu akan dibuat secara otomatis.



Mari kita coba menjelaskan apa yang ditulis di atas dengan menggunakan beberapa contoh. Menggunakan ada fungsi, Anda dapat memeriksa deducibility atau non-deducibility dari konsep tertanam:



concept freeExecutor is executor e where not exists (
    task t where t.executor = e.id and t.status in ('assigned', 'in process')
)
      
      





Dalam contoh ini, konsep anonim:



(task t where t.executor = e.id and t.status in ('assigned', 'in process'))
      
      





sebenarnya adalah konsep yang mewarisi semua atribut konsep tugas :



(concept _unanimous_task_1 as task t where t.executor = e.id and t.status in ('assigned', 'in process'))
      
      





Fungsi find memungkinkan Anda untuk menampilkan semua nilai konsep, yang kemudian dapat dikaitkan dengan atribut:



concept customerOrdersThisYear is customer c with orders where c.orders = find(
    (id = o.id, status = o.status, createdDate = o.createdDate, total = o.total) 
    from order o where o.customerId = c.id and o.createdDate > '2021-01-01'
)
      
      





Dalam contoh ini, kami memperluas gagasan pelanggan dengan daftar pesanan, yang merupakan objek yang berisi atribut yang dipilih dari gagasan pesanan . Kami telah menentukan daftar atribut untuk konsep anonim, tetapi namanya dihilangkan.



Kondisi di bagian mana konsep anonim mungkin termasuk atribut lain dari orang tua atau anak perusahaan konsep, dalam hal ini c.id . Ciri dari konsep anonim adalah bahwa semua variabel dan atribut eksternal seperti itu harus dikaitkan dengan nilai pada saat memulai pencarian solusi. Dengan demikian, objek konsep anonim hanya dapat ditemukan setelah menemukan objek konsep pelanggan . ...



Koneksi eksternal



Definisi konsep anonim juga dapat digunakan di bagian from , di mana mereka mewakili konsep induk. Selain itu, dalam definisi konsep anonim, dimungkinkan untuk mentransfer beberapa kondisi yang menghubungkannya dengan konsep lain, yang akan memiliki efek khusus. Kondisi ini akan diperiksa pada tahap menemukan solusi untuk konsep anonim dan tidak akan mempengaruhi proses inferensi konsep anak. Di sini Anda dapat menggambar analogi antara kondisi di bagian where dari konsep anonim dan kondisi di bagian JOIN ON dari SQL.



Dengan demikian, konsep anonim dapat digunakan untuk mengimplementasikan analog SQL dari gabungan luar kiri. Ini membutuhkan tiga hal.



  1. Pertama, ganti konsep induk yang diinginkan dengan konsep anonim yang didasarkan padanya dan transfer semua koneksi dengan konsep induk lainnya ke dalamnya.
  2. Kedua, untuk menunjukkan bahwa kegagalan inferensi konsep ini seharusnya tidak menyebabkan kegagalan otomatis dari inferensi seluruh konsep anak. Untuk melakukan ini, Anda perlu menandai konsep induk ini dengan kata kunci opsional .
  3. Dan ketiga, di bagian mana pada konsep anak, Anda dapat memeriksa apakah ada solusi untuk konsep anonim ini.


Mari kita lihat contoh kecilnya:



concept taskAssignedTo (task = t, assignee = u, assigneeName) 
from task t, optional (user where id = t.assignedTo) u 
where assigneeName = if(defined(u), u.firstName + ' ' + u.lastName, 'Unassigned') 
      
      





Atribut konsep taskAssignedTo mencakup objek tugas, pelaksananya, dan nama pelaksana secara terpisah. Konsep induk adalah tugas dan pengguna , dan yang terakhir bisa kosong jika tugas belum memiliki eksekutor. Itu dibungkus dalam definisi konsep anonim, didahului dengan kata kunci opsional . Prosedur inferensi pertama akan menemukan benda dari tugas konsep , kemudian, didasarkan pada pengguna, akan menciptakan konsep anonim, mengaitkannya dengan nilai tertentu dari assignedTo atribut dari tugas konsep . Kata kunci opsional memberitahu rutin inferensi bahwa jika konsep gagal, objeknya akan dikaitkan dengan nilai khusus UNDEFINED . Dan memeriksa hasil keluarannya pada tingkat konsep anak memungkinkan atribut assigneeName untuk menyetel nilai default. Jika kata kunci opsional tidak ditentukan, maka kegagalan untuk menyimpulkan konsep anonim akan mengakibatkan cabang pencarian konsep anak saat ini gagal. Ini akan menjadi analogi dengan gabungan dalam SQL.



Karena kondisi di mana klausul dari konsep anonim termasuk assignedTo atribut dari konsep tua lainnya tugas , maka pencarian objek dari pengguna konsep hanya mungkin setelah mengikat objek tugas dengan nilai. Mereka tidak dapat ditukar:



from optional (user where id = t.assignedTo) u, task t 
      
      





Karena pada tahap awal nilai t.assignedTo tidak akan diketahui, ini tidak akan berfungsi untuk membuat definisi konsep anonim.



Jika di SQL urutan tabel di bagian daritidak masalah, maka dalam Prolog urutan predikat dalam aturan secara unik menentukan urutan melintasi pohon keputusan. Hal yang sama dapat dikatakan untuk komponen simulasi, yang aturan keluarannya didasarkan pada resolusi SLD yang digunakan dalam Prolog. Di dalamnya, hasil keluaran obyek konsep kiri menentukan batasan keluaran obyek konsep kanan. Karena itu, sayangnya, tidak akan berhasil menerapkan operasi gabungan luar kanan dan gabungan luar penuh dengan cara alami yang sama. Karena kardinalitas himpunan hasil konsep induk kanan bisa lebih besar daripada yang kiri, mereka perlu mengeluarkan ke arah yang berlawanan - dari konsep kanan ke kiri. Sayangnya, kekhasan prosedur inferensi yang dipilih memaksakan batasan mereka pada fungsionalitas bahasa. Tetapi operasi sambungan luar penuh dapat ditiru dengan menggabungkan bagian dalam,serikat kiri dan kanan:



concept outerJoinRelation( 
concept1Name, 
concept2Name, 
concept1Key,
concept2Key,
concept1 = c1,
concept2 = c2
) from <concept1Name> c1, <concept2Name> c2
where c1.<concept1Key> = c2.<concept2Key>;

concept outerJoinRelation(
concept1Name, 
concept2Name, 
concept1Key,
concept2Key,
concept1 = c1,
concept2 = null
) from <concept1Name> c1
where not exists( <concept2Name> c2 where c1.<concept1Key> = c2.<concept2Key>);

concept outerJoinRelation(
concept1Name, 
concept2Name, 
concept1Key,
concept2Key,
concept1 = null,
concept2 = c2
) from <concept2Name> c2
where not exists( <concept1Name> c1 where c1.<concept1Key> = c2.<concept2Key>);
      
      





Konsep umum ini dijelaskan menggunakan logika tingkat tinggi yang dijelaskan di artikel sebelumnya . Definisinya dibagi menjadi tiga bagian. Yang pertama akan menemukan perpotongan konsep, yang kedua - objek yang dimiliki konsep kiri dan yang kiri tidak, dan yang ketiga - sebaliknya. Karena nama setiap bagian sama, hasil inferensi mereka akan digabungkan.



Pengumpulan



Agregasi adalah bagian integral dari aljabar relasional dan pemrograman logika. Dalam SQL, klausa GROUP BY memungkinkan Anda mengelompokkan baris yang memiliki nilai kunci yang sama ke dalam baris ringkasan. Ini memungkinkan Anda untuk menghapus nilai duplikat dan biasanya digunakan dengan fungsi agregat seperti sum , count , min , max , avg.... Untuk setiap grup baris, fungsi agregat mengembalikan nilai biasa berdasarkan semua baris dalam grup itu. Dalam pemrograman logika, agregasi memiliki semantik yang lebih kompleks. Hal ini disebabkan oleh fakta bahwa dalam beberapa kasus definisi rekursif aturan SLD, resolusi masuk ke loop tak terbatas dan tidak dapat diselesaikan. Seperti dalam kasus penolakan sebagai kegagalan, masalah rekursi dalam operasi agregasi diselesaikan menggunakan semantik model persisten atau semantik yang beralasan baik. Saya mencoba membahas secara singkat tentang pendekatan ini di artikel sebelumnya . Tetapi karena semantik komponen pemodelan harus sesederhana mungkin, resolusi SLD standar lebih disukai. Dan masalah menghindari rekursi tak terbatas lebih baik diselesaikan dengan membentuk kembali koneksi antar konsep.



Agregasi secara alami dapat diimplementasikan dalam gaya fungsional menggunakan komponen komputasi dari bahasa hybrid. Untuk melakukan ini, fungsi yang menciutkan hasil inferensi ke dalam grup unik dan menghitung fungsi agregat untuk masing-masing sudah cukup. Tetapi membagi definisi konsep menjadi bagian logis dan fungsional tidak akan menjadi solusi yang paling nyaman untuk alat penting seperti agregasi. Lebih baik memperluas sintaks definisi untuk menyertakan bagian pengelompokan dan fungsi agregasi:



concept < > < > (
    < > = <>,
    ... 
)
group by < >, ... 
from 
    <  > <  > (
        < > = <> ,
        ...
    ),
    ...
where < >
      
      





Kelompok demi bagian , seperti di SQL, berisi daftar atribut yang digunakan untuk melakukan pengelompokan. Ekspresi relasi juga bisa menyertakan fungsi agregasi. Ekspresi yang berisi fungsi seperti itu akan dianggap tidak terdefinisi hingga nilai dari semua konsep induk ditemukan dan pengelompokan dilakukan. Kemudian nilainya dapat dihitung untuk setiap grup, terkait dengan atribut dan / atau digunakan untuk memfilter grup. Dengan pendekatan malas untuk mengevaluasi dan memeriksa kondisi, tidak diperlukan bagian HAVING yang memisahkan kondisi filter sebelum dan sesudah pengelompokan. Runtime akan melakukan ini secara otomatis.



Fungsi agregasi utama adalah count , sum, rata-rata , min , maks . Maksud dari fungsinya bisa dipahami dari namanya. Karena komponen pemodelan secara alami dapat bekerja dengan tipe data komposit, Anda juga dapat menambahkan fungsi yang mengembalikan nilai yang dikelompokkan sebagai daftar. Sebut saja grup . Argumen masukannya adalah ekspresi. Fungsi mengembalikan daftar hasil evaluasi ekspresi ini untuk setiap elemen dalam grup. Dengan menggabungkannya dengan fungsi lain, Anda dapat menerapkan fungsi agregasi arbitrer apa pun. Fungsi grup akan lebih nyaman daripada fungsi SQL seperti group_concat atau json_arrayagyang sering digunakan sebagai langkah perantara untuk mendapatkan larik nilai bidang.



Contoh pengelompokan:



concept totalOrders (
    customer = c,
    orders = group(o),
    ordersTotal = sum(o.total)
) group by customer
from customer c, order o 
where c.id = o.customerId and ordersTotal > 100
      
      





Atribut pesanan akan berisi daftar semua pesanan pengguna, ordersTotal - total semua pesanan. Kondisi ordersTotal> 100 akan diperiksa setelah pengelompokan selesai dan fungsi penjumlahan dihitung .



Konsep ditentukan oleh fungsi



Bentuk logis deklaratif untuk mendeskripsikan konsep tidak selalu nyaman. Kadang-kadang akan lebih mudah untuk mengatur urutan perhitungan, yang hasilnya akan menjadi inti dari konsep. Situasi ini sering muncul ketika diperlukan untuk memuat fakta dari sumber data eksternal, misalnya dari database, file, mengirim permintaan ke layanan eksternal, dll. Akan lebih mudah untuk merepresentasikan konsep dalam bentuk fungsi yang menerjemahkan kueri masukan menjadi kueri ke database dan mengembalikan hasil dari menjalankan kueri ini. Terkadang masuk akal untuk meninggalkan kesimpulan logis, menggantinya dengan implementasi khusus yang mempertimbangkan spesifikasi masalah tertentu dan menyelesaikannya dengan lebih efisien. Juga lebih mudah untuk mendeskripsikan dalam gaya fungsional urutan tak terbatas yang menghasilkan entitas konsep, misalnya, urutan bilangan bulat.



Prinsip kerja dengan konsep semacam itu harus sama dengan konsep lainnya yang dijelaskan di atas. Pencarian solusi harus diluncurkan dengan metode yang sama. Mereka sendiri dapat digunakan sebagai konsep induk dalam definisi konsep. Hanya implementasi internal dari pencarian solusi yang harus berbeda. Oleh karena itu, kami akan memperkenalkan cara lain untuk mendefinisikan konsep menggunakan fungsi:



concept < > (
< >, ... 
)
by <  >
      
      





Untuk mendefinisikan konsep yang didefinisikan menggunakan fungsi, Anda harus menentukan daftar atributnya dan fungsi untuk menghasilkan objek. Saya memutuskan untuk menjadikan daftar atribut sebagai elemen wajib dari definisi, karena ini akan menyederhanakan penggunaan konsep semacam itu - untuk memahami strukturnya, Anda tidak perlu mempelajari fungsi menghasilkan objek.



Sekarang mari kita bicara tentang fungsi untuk menghasilkan objek. Jelas, itu harus menerima permintaan sebagai masukan - nilai awal atribut. Karena nilai-nilai ini dapat ditentukan atau tidak, untuk kenyamanan mereka dapat ditempatkan dalam array asosiatif, yang akan menjadi argumen input dari fungsi tersebut. Ini juga akan berguna untuk mengetahui di mode mana pencarian makna konsep dimulai - temukan semua nilai yang mungkin, temukan hanya yang pertama, atau hanya periksa keberadaan solusi. Oleh karena itu, kami menambahkan mode pencarian sebagai argumen input kedua.



Hasil evaluasi fungsi harus berupa daftar objek konsep. Tapi, karena prosedur inferensi berdasarkan pencarian dengan backtracking akan menggunakan nilai-nilai ini satu per satu, dimungkinkan untuk membuat argumen output fungsi bukan daftar itu sendiri, tetapi iterator untuk itu. Ini akan membuat definisi konsep lebih fleksibel, misalnya, akan memungkinkan, jika perlu, untuk mengimplementasikan evaluasi malas atau urutan objek yang tak terbatas. Anda dapat menggunakan iterator dari setiap koleksi standar atau membuat implementasi kustom Anda sendiri. Elemen koleksi harus berupa array asosiatif dengan nilai-nilai atribut konsep. Esensi konsep akan dibuat atas dasar mereka secara otomatis.

Menggunakan iterator sebagai tipe kembalian memiliki kekurangannya. Ini lebih rumit dan kurang ramah pengguna daripada sekadar mengembalikan daftar hasil. Menemukan opsi terbaik yang menggabungkan keserbagunaan, kesederhanaan, dan kegunaan merupakan tantangan di masa depan.



Sebagai contoh, perhatikan konsep yang menjelaskan interval waktu. Katakanlah kita ingin membagi hari kerja menjadi interval 15 menit. Kita bisa melakukan ini dengan fungsi yang cukup sederhana:



concept timeSlot15min (id, hour, minute) by function(query, mode) {
    var timeSlots = [];
    var curId = 1;
    for(var curHour = 8; curHour < 19; curHour += 1) {
        for(var curMinute = 0; curMinute < 60; curMinute += 15) {
            timeSlots.push({
                id: curId,
                hour: curHour,
                minute: curMinute;
            });
            curId++;
        }
    }
    return timeSlots.iterator();
}
      
      





Fungsi mengembalikan iterator untuk semua kemungkinan nilai dari interval waktu 15 menit untuk hari kerja. Ini dapat digunakan, misalnya, untuk mencari slot gratis yang belum dipesan:



concept freeTimeSlot is timeSlot15min s where not exists (bookedSlot b where b.id = s.id)
      
      





Fungsi ini tidak memeriksa hasil perhitungan untuk kesesuaian dengan kueri kueri , ini akan dilakukan secara otomatis saat mengubah larik atribut menjadi entitas. Tetapi jika perlu, bidang kueri dapat digunakan untuk mengoptimalkan fungsi. Misalnya, formulir kueri database berdasarkan bidang kueri untuk sebuah konsep.



Konsep melalui fungsi menggabungkan semantik logis dan fungsional. Jika dalam paradigma fungsional fungsi menghitung hasil untuk nilai yang diberikan dari argumen masukan, maka dalam paradigma logika tidak ada pembagian menjadi argumen keluaran dan masukan. Hanya sebagian dari argumen yang dapat diberikan, dan dalam kombinasi apa pun, dan fungsi perlu menemukan nilai dari argumen yang tersisa. Dalam praktiknya, tidak selalu mungkin untuk menerapkan fungsi yang mampu melakukan penghitungan ke segala arah, jadi masuk akal untuk membatasi kemungkinan kombinasi argumen bebas. Misalnya, nyatakan bahwa beberapa argumen harus terikat ke nilai sebelum mengevaluasi fungsi. Untuk melakukan ini, tandai atribut tersebut dalam definisi konsep dengan kata kunci yang diperlukan .



Sebagai contoh, pertimbangkan konsep yang mendefinisikan nilai-nilai dari skala eksponensial tertentu.



concept expScale (value, position, required limit) by function(query, mode) {
return {
    _curPos = 0,
    _curValue = 1,
    next: function() {
        if(!this.hasNext()) {
            return null;
        }
        var curItem = {value: this._curValue, position: this._curPosition, limit:  query.limit};
        this._curPos += 1;
        this._curValue = this._curValue * Math.E;
        return curItem;
    },
    hasNext: function() {
        return query.limit == 0 || this._curPos < query.limit; 
    }
}}
      
      





Fungsi ini mengembalikan iterator yang menghasilkan entitas konsep menggunakan evaluasi malas. Ukuran urutan dibatasi oleh nilai atribut limit , tetapi jika nol, itu menjadi tak terbatas. Konsep yang didasarkan pada urutan tak terbatas harus digunakan dengan sangat hati-hati, karena tidak menjamin bahwa rutinitas inferensi akan selesai. Atribut batas bersifat tambahan dan digunakan untuk mengatur perhitungan. Kita tidak bisa menyimpulkannya dari nilai atribut lain, harus diketahui sebelum penghitungan dimulai, jadi sudah ditandai sebagai wajib



Kami telah mempertimbangkan salah satu opsi untuk tampilan konsep sebagai fungsi. Namun masalah keamanan dan kegunaan konsep semacam itu membutuhkan penelitian yang lebih detail di masa depan.



Meratakan koleksi bersarang



Beberapa dialek SQL yang bisa bekerja dengan data dalam bentuk objek mendukung operasi seperti UNNEST , yang mengonversi konten kumpulan ke format tabel ( baris set ) dan menambahkan tabel yang dihasilkan ke klausa FROM . Biasanya digunakan untuk meratakan objek dengan struktur bersarang, dengan kata lain, untuk meratakan atau meratakannya. Contoh bahasa tersebut adalah BigQuery dan SQL ++.



Katakanlah kita menyimpan informasi pengguna sebagai objek JSON:



[ {
"id":1,
"alias":"Margarita",
"name":"MargaritaStoddard",
"nickname":"Mags",
"userSince":"2012-08-20T10:10:00",
"friendIds":[2,3,6,10],
"employment":[{
    "organizationName":"Codetechno",
    "start-date":"2006-08-06"
},
{
    "organizationName":"geomedia",
    "start-date":"2010-06-17",
    "end-date":"2010-01-26"
}],
"gender":"F"
},
{
"id":2,
"alias":"Isbel",
"name":"IsbelDull",
"nickname":"Izzy",
"userSince":"2011-01-22T10:10:00",
"friendIds":[1,4],
"employment":[{
    "organizationName":"Hexviafind",
    "startDate":"2010-04-27"
}]
}, 
…]
      
      





Objek pengguna menyimpan koleksi bersarang dengan daftar teman dan tempat kerja. Anda dapat mengekstrak informasi terlampir tentang tempat kerja pengguna dengan merekatkannya bersama dengan data tentang pengguna yang diambil dari tingkat atas objek menggunakan kueri SQL ++:



SELECT u.id AS userId, u.name AS userName, e.organizationName AS orgName
FROM Users u UNNEST u.employment e
WHERE u.id = 1;
      
      





Hasilnya adalah:



[ {
"userId": 1,
"userName": "MargaritaStoddard",
"orgName": "Codetechno"
}, {
"userId": 1,
"userName": "MargaritaStoddard",
"orgName": "geomedia"
} ]
      
      





Operasi ini dibahas lebih detail di sini .



Tidak seperti SQL, dalam komponen pemodelan, data yang disematkan harus diubah tidak menjadi tabel, tetapi ke format objek. Konsep yang dibahas di atas akan membantu kita dalam hal ini - konsep yang didefinisikan melalui fungsi dan konsep anonim. Konsep melalui fungsi akan memungkinkan Anda untuk mengubah koleksi bersarang menjadi format objek, dan konsep anonim akan memungkinkan Anda untuk menyematkan definisinya dalam daftar konsep induk dan mengakses nilai atributnya yang berisi koleksi bersarang yang diinginkan.



Karena definisi lengkap suatu konsep melalui suatu fungsi terlalu rumit untuk digunakan sebagai konsep anonim:



concept conceptName(attribute1, attribute2, ...) by function(query, mode) {...}
      
      





kita perlu menemukan cara untuk mempersingkatnya. Pertama, Anda dapat menyingkirkan judul definisi fungsi dengan parameter kueri dan mode . Dalam posisi konsep induk, argumen mode akan selalu menjadi "temukan semua nilai konsep". Argumen kueri akan selalu kosong, karena dependensi pada atribut konsep lain dapat disematkan di badan fungsi. Kata kunci konsep juga dapat dibuang. Jadi, kami mendapatkan:



conceptName(attribute1, attribute2, ...) {…}
      
      





Jika nama konsep tidak penting, maka konsep tersebut dapat dihilangkan, dan akan dibuat secara otomatis:



(attribute1, attribute2, ...) {…}
      
      





Jika di masa mendatang dimungkinkan untuk membuat kompiler yang dapat menyimpulkan daftar atribut dari tipe objek yang dikembalikan oleh suatu fungsi, maka daftar atribut tersebut dapat dibuang:



{…}
      
      







Jadi, contoh dengan pengguna dan tempat kerja mereka sebagai konsep akan terlihat seperti ini:



concept userEmployments (
    userId = u.id,
    userName = u.name,
    orgName = e.orgName
) from users u, {u.employment.map((item) => {orgName: item.organizationName}).iterator()} e
      
      





Solusinya ternyata sedikit bertele-tele, tetapi universal. Pada saat yang sama, jika tidak ada transformasi objek dari koleksi bersarang yang diperlukan, maka itu dapat disederhanakan secara signifikan:



concept userEmployments (
    userId = u.id,
    userName = u.name,
    orgName = e. organizationName
) from users u, {u.employment.iterator()} e
      
      





temuan



Artikel ini berfokus pada dua masalah. Pertama, transfer beberapa fitur bahasa SQL ke komponen pemodelan: kueri bersarang, gabungan luar, agregasi, dan gabungan dengan koleksi bersarang. Kedua, pengenalan dua konstruksi baru ke dalam komponen pemodelan: definisi konsep dan konsep tanpa nama yang ditentukan melalui suatu fungsi.



Sebuah konsep anonim adalah bentuk singkatan dari definisi konsep, dimaksudkan untuk digunakan sebagai argumen untuk fungsi ( menemukan , findOne, dan ada ) atau sebagai definisi konsep bersarang di mana klausa... Ini dapat dianggap sebagai analogi dengan definisi fungsi anonim dalam bahasa pemrograman fungsional.



Konsep yang didefinisikan melalui suatu fungsi adalah konsep, cara menghasilkan objek yang diekspresikan menggunakan algoritma dalam bentuk eksplisit. Ini adalah semacam "antarmuka" antara dunia pemrograman fungsional atau berorientasi objek dan pemrograman logika. Ini akan berguna dalam banyak kasus ketika cara logis untuk mendefinisikan konsep tidak nyaman atau tidak mungkin: misalnya, untuk memuat fakta awal dari database mereka, file atau dari permintaan ke layanan jarak jauh, untuk mengganti pencarian logis universal dengan spesifikasinya implementasi yang dioptimalkan, atau untuk mengimplementasikan aturan arbitrer yang membuat objek.



Menariknya, peminjaman dari SQL, seperti kueri bersarang, gabungan luar, dan gabungan kumpulan bersarang, tidak memerlukan perubahan besar dalam logika komponen pemodelan dan diimplementasikan menggunakan konsep seperti konsep dan konsep anonim melalui suatu fungsi. Ini menunjukkan bahwa jenis konsep ini adalah alat yang fleksibel dan serbaguna dengan kekuatan ekspresif yang besar. Saya pikir ada banyak cara yang lebih menarik untuk menggunakannya.



Jadi, dalam artikel ini dan dua artikel sebelumnya, saya menjelaskan konsep dasar dan elemen komponen pemodelan bahasa pemrograman hybrid. Namun, sebelum beralih ke masalah pengintegrasian komponen pemodelan dengan komponen komputasi yang menerapkan gaya pemrograman fungsional atau berorientasi objek, saya memutuskan untuk mencurahkan artikel berikutnya ke opsi yang memungkinkan untuk aplikasinya. Dari sudut pandang saya, komponen pemodelan memiliki keunggulan dibandingkan bahasa kueri tradisional, terutama SQL, dan dapat digunakan sendiri tanpa integrasi yang mendalam dengan komponen komputasi, yang ingin saya tunjukkan dengan beberapa contoh di artikel berikutnya.



Teks ilmiah lengkap dalam bahasa Inggris tersedia di: paper.ssrn.com/sol3/papers.cfm?abstract_id=3555711



Tautan ke publikasi sebelumnya:



Merancang bahasa pemrograman multi-paradigma. Bagian 1 - Untuk apa ini?

Kami merancang bahasa pemrograman multi-paradigma. Bagian 2 - Perbandingan model bangunan di PL / SQL, LINQ dan GraphQL Kami

merancang bahasa pemrograman multi-paradigma. Bagian 3 - Tinjauan bahasa representasi pengetahuan Kami

merancang bahasa pemrograman multi-paradigma. Bagian 4 - Konstruksi dasar bahasa pemodelan Kami

merancang bahasa pemrograman multi-paradigma. Bagian 5 - Fitur Pemrograman Logika



All Articles