Nous concevons un langage de programmation multi-paradigme. Partie 6 - Emprunter depuis SQL

Nous continuons l'histoire de la création d'un langage de programmation multi-paradigme qui combine un style logique déclaratif avec un style orienté objet et fonctionnel, ce qui serait pratique lorsque vous travaillez avec des données semi-structurées et intégrez des données provenant de sources disparates. Le langage sera constitué de deux composants étroitement intégrés l'un à l'autre: le composant déclaratif sera chargé de décrire le modÚle de domaine, et le composant impératif ou fonctionnel sera chargé de décrire les algorithmes pour travailler avec le modÚle et le calcul.



Le composant de modélisation de langage hybride est un ensemble de concepts-objets reliés par des relations logiques. J'ai réussi à parler des principaux moyens de définir les concepts, y compris l'héritage et la définition des relations entre eux. Et aussi sur certaines des nuances de la programmation logique, y compris la sémantique de l'opérateur de négation et la logique d'ordre supérieur. Une liste complÚte des publications sur ce sujet se trouve à la fin de cet article.



Dans le domaine du travail avec des donnĂ©es, le leader incontestable est le langage SQL. Certaines de ses fonctionnalitĂ©s, qui se sont avĂ©rĂ©es trĂšs pratiques en pratique, comme l'agrĂ©gation, ont ensuite migrĂ© vers la programmation logique. Par consĂ©quent, il sera utile d'emprunter autant que possible Ă  SQL pour le composant de modĂ©lisation. Dans cet article, je souhaite vous montrer comment les requĂȘtes imbriquĂ©es, les jointures externes et l'agrĂ©gation peuvent ĂȘtre intĂ©grĂ©es dans les dĂ©finitions de concept. Je parlerai Ă©galement d'un autre type de concepts, qui est dĂ©crit Ă  l'aide d'une fonction qui gĂ©nĂšre des objets (entitĂ©s) dans un style algorithmique sans recourir Ă  la recherche logique. Et je vais vous montrer comment vous pouvez utiliser des tableaux d'objets comme concepts parents par analogie avec l'opĂ©ration SQL UNNESTqui convertit les collections au format table et leur permet d'ĂȘtre jointes Ă  d'autres tables dans la clause FROM .



Définitions anonymes des concepts



Dans le monde SQL, les requĂȘtes imbriquĂ©es sont un outil souvent utilisĂ© lorsqu'il est nĂ©cessaire d'obtenir des donnĂ©es intermĂ©diaires pour un traitement ultĂ©rieur dans la requĂȘte principale. Le composant de modĂ©lisation n'a pas un besoin aussi urgent de donnĂ©es intermĂ©diaires, car la maniĂšre de les obtenir peut ĂȘtre formalisĂ©e en tant que concept sĂ©parĂ©. Mais il y a des cas oĂč des dĂ©finitions de concept imbriquĂ©es seraient utiles.



Parfois, vous devez modifier légÚrement le concept, sélectionner ses attributs individuels, filtrer les valeurs. Si cette modification n'est nécessaire qu'à un seul endroit, cela n'a aucun sens de créer un concept séparé avec un nom unique. Cette situation est souvent rencontrée lorsque les concepts sont des arguments pour des fonctions telles que exist , find ou findOne , qui vérifie la déductibilité du concept, en trouvant tout ou seulement le premier objet (entité) du concept. Ici, vous pouvez faire une analogie avec les fonctions anonymes dans les langages de programmation fonctionnelle, qui sont souvent utilisées comme arguments pour des fonctions telles que mapper , rechercher , filtrer , etc.



Considérez la syntaxe pour définir un concept anonyme. En général, il suit la syntaxe d'une définition de concept commune, sauf que dans certains cas, vous pouvez omettre les listes d'attributs et le nom du concept enfant. Si un concept anonyme est utilisé comme argument pour existe, alors son nom et la liste des attributs ne sont pas importants, il suffit de vérifier qu'il y a au moins un résultat. Les fonctions find et findOne peuvent ne pas avoir besoin du nom du concept si la sortie n'est pas utilisée comme un concept entier, mais uniquement comme un ensemble d'attributs dans un tableau associatif. Si aucun attribut n'est spécifié, le mécanisme d'héritage est utilisé par défaut et les attributs seront hérités des concepts parents. Si aucun nom de concept n'est spécifié, il sera généré automatiquement.



Essayons d'expliquer ce qui a Ă©tĂ© Ă©crit ci-dessus Ă  l'aide de quelques exemples. À l'aide de la fonction Existe , vous pouvez vĂ©rifier la dĂ©ductibilitĂ© ou la non-dĂ©ductibilitĂ© d'un concept embarquĂ©:



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





Dans cet exemple, le concept anonyme:



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





est en fait un concept qui hérite de tous les attributs du concept de tùche :



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





La fonction find permet de renvoyer sous forme de liste toutes les valeurs d'un concept, qui peuvent ensuite ĂȘtre associĂ©es Ă  un attribut:



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'
)
      
      





Dans cet exemple, nous étendons la notion de client avec une liste de commandes, qui sont des objets contenant les attributs sélectionnés de la notion de commande . Nous avons spécifié une liste d'attributs pour un concept anonyme, mais son nom est omis.



Les conditions dans la section oĂč les concepts anonymes peuvent inclure d' autres attributs des concepts de mĂšre ou filiale, dans ce cas c.id . Une caractĂ©ristique des concepts anonymes est que toutes ces variables et attributs externes doivent nĂ©cessairement ĂȘtre associĂ©s Ă  des valeurs au moment du dĂ©marrage de la recherche de solutions. Ainsi, les objets du concept anonyme ne peuvent ĂȘtre trouvĂ©s qu'aprĂšs avoir trouvĂ© les objets du concept client . ...



Connexions externes



Les dĂ©finitions de concept anonymes peuvent Ă©galement ĂȘtre utilisĂ©es dans la section from , oĂč elles reprĂ©sentent des concepts parents. De plus, dans la dĂ©finition d'un concept anonyme, il est possible de transfĂ©rer certaines des conditions le reliant Ă  d'autres concepts, ce qui aura un effet particulier. Ces conditions seront vĂ©rifiĂ©es au stade de la recherche d'une solution au concept anonyme et n'affecteront pas le processus d'infĂ©rence du concept enfant. Ici, vous pouvez dessiner une analogie entre les conditions de la section where d' un concept anonyme et les conditions de la section JOIN ON de SQL.



Ainsi, des concepts anonymes peuvent ĂȘtre utilisĂ©s pour implĂ©menter un analogue SQL de jointure externe gauche. Cela nĂ©cessite trois choses.



  1. Tout d'abord, remplacez le concept parent souhaité par un concept anonyme basé sur celui-ci et transférez-y toutes les connexions avec d'autres concepts parents.
  2. DeuxiÚmement, souligner que l'échec de l'inférence de ce concept ne doit pas conduire à un échec automatique de l'inférence de l'ensemble du concept de l'enfant. Pour ce faire, vous devez marquer ce concept parent avec le mot-clé facultatif .
  3. Et troisiÚmement, dans la section where du concept enfant, vous pouvez vérifier s'il existe des solutions à ce concept anonyme.


Regardons un petit exemple:



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') 
      
      





Les attributs du concept taskAssignedTo incluent les objets de la tĂąche, son exĂ©cuteur et sĂ©parĂ©ment le nom de l'exĂ©cuteur. Les concepts parents sont tĂąche et utilisateur , et ce dernier peut ĂȘtre vide si la tĂąche n'a pas dĂ©jĂ  d'exĂ©cuteur. Il est enveloppĂ© dans une dĂ©finition de concept anonyme, prĂ©cĂ©dĂ©e du mot-clĂ© facultatif . La procĂ©dure d'infĂ©rence va d' abord trouver des objets de la tĂąche notion , puis, en fonction de l' utilisateur, il va crĂ©er un concept anonyme, associant Ă  une valeur spĂ©cifique de l' AssignedTo attribut de la tĂąche notion . Mot-clĂ© optionnel indique Ă  la routine d'infĂ©rence que si le concept Ă©choue, son objet sera associĂ© Ă  la valeur spĂ©ciale UNDEFINED . Et vĂ©rifier le rĂ©sultat de sa sortie au niveau du concept enfant permet Ă  l'attribut assigneeName de dĂ©finir une valeur par dĂ©faut. Si le mot clĂ© facultatif n'Ă©tait pas spĂ©cifiĂ©, l'Ă©chec de la dĂ©duction d'un concept anonyme entraĂźnerait l'Ă©chec de la branche actuelle de la recherche de concept enfant. Ce serait analogue Ă  la jointure interne de SQL.



Parce que les conditions dans le oĂč la clause de la notion anonyme comprennent l' AssignedTo attribut de l' autre concept parent tĂąche , la recherche d'objets de l' utilisateur conceptuel n'est possible qu'aprĂšs avoir liĂ© des objets de tĂąche avec des valeurs. Ils ne peuvent pas ĂȘtre Ă©changĂ©s:



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





Étant donnĂ© qu'au stade initial, la valeur de t.assignedTo sera inconnue, cela ne fonctionnera pas pour crĂ©er une dĂ©finition d'un concept anonyme.



Si en SQL l'ordre des tables dans la section fromn'a pas d'importance, alors dans Prolog, l'ordre des prĂ©dicats dans une rĂšgle dĂ©termine de maniĂšre unique la sĂ©quence de traversĂ©e de l'arbre de dĂ©cision. Il en va de mĂȘme pour le composant de simulation, dont la rĂšgle de sortie est basĂ©e sur la rĂ©solution SLD utilisĂ©e dans Prolog. Dans celui-ci, le rĂ©sultat de la sortie des objets du concept de gauche dĂ©termine les restrictions pour la sortie des objets du concept de droite. Pour cette raison, malheureusement, il ne fonctionnera pas pour implĂ©menter les opĂ©rations de jointure externe et de jointure externe complĂštes de la mĂȘme maniĂšre naturelle. Étant donnĂ© que la cardinalitĂ© de l'ensemble des rĂ©sultats du concept de parent droit peut ĂȘtre supĂ©rieure Ă  celle du concept de gauche, ils devront sortir dans la direction opposĂ©e - du concept droit vers la gauche. Malheureusement, les particularitĂ©s de la procĂ©dure d'infĂ©rence choisie imposent leurs limites Ă  la fonctionnalitĂ© du langage. Mais l'opĂ©ration de jointure externe complĂšte peut ĂȘtre Ă©mulĂ©e en rejoignant l'intĂ©rieur,unions gauche et droite:



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>);
      
      





Ce concept gĂ©nĂ©ralisĂ© est dĂ©crit Ă  l'aide de la logique d'ordre supĂ©rieur dĂ©crite dans l' article prĂ©cĂ©dent . Sa dĂ©finition est divisĂ©e en trois parties. Le premier trouvera des intersections de concepts, le second - des objets que possĂšde le concept de gauche et celui de gauche pas, et le troisiĂšme - vice versa. Puisque les noms de chaque partie sont les mĂȘmes, les rĂ©sultats de leur infĂ©rence seront combinĂ©s.



Agrégation



L'agrĂ©gation fait partie intĂ©grante de l'algĂšbre relationnelle et de la programmation logique. En SQL, la clause GROUP BY vous permet de regrouper les lignes qui ont la mĂȘme valeur de clĂ© dans des lignes rĂ©capitulatives. Il vous permet de supprimer les valeurs en double et est couramment utilisĂ© avec des fonctions d'agrĂ©gation telles que sum , count , min , max , avg.... Pour chaque groupe de lignes, les fonctions d'agrĂ©gation renvoient une valeur ordinaire basĂ©e sur toutes les lignes de ce groupe. En programmation logique, l'agrĂ©gation a une sĂ©mantique plus complexe. Cela est dĂ» au fait que dans certains cas de dĂ©finition rĂ©cursive des rĂšgles SLD, la rĂ©solution entre dans une boucle infinie et ne peut pas se terminer. Comme dans le cas du refus comme Ă©chec, le problĂšme de la rĂ©cursivitĂ© dans l'opĂ©ration d'agrĂ©gation est rĂ©solu en utilisant une sĂ©mantique de modĂšle persistante ou une sĂ©mantique bien fondĂ©e. J'ai essayĂ© de parler briĂšvement de ces approches dans l' article prĂ©cĂ©dent . Mais comme la sĂ©mantique du composant de modĂ©lisation doit ĂȘtre aussi simple que possible, la rĂ©solution SLD standard est prĂ©fĂ©rĂ©e. Et le problĂšme d'Ă©viter la rĂ©cursion infinie est mieux rĂ©solu en reformant les connexions entre les concepts.



L'agrĂ©gation pourrait naturellement ĂȘtre implĂ©mentĂ©e dans un style fonctionnel en utilisant le composant de calcul d'un langage hybride. Pour ce faire, une fonction qui regroupe les rĂ©sultats d'infĂ©rence en groupes uniques et calcule les fonctions d'agrĂ©gation pour chacun d'entre eux est suffisante. Mais diviser la dĂ©finition d'un concept en parties logiques et fonctionnelles ne sera pas la solution la plus pratique pour un outil aussi important que l'agrĂ©gation. Mieux vaut Ă©tendre la syntaxe de la dĂ©finition pour inclure une section de regroupement et des fonctions d'agrĂ©gation:



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





Le groupe par section , tout comme en SQL, contient une liste d'attributs par lesquels le regroupement est effectuĂ©. Les expressions de relation peuvent Ă©galement inclure des fonctions d'agrĂ©gation. Les expressions contenant de telles fonctions seront considĂ©rĂ©es comme non dĂ©finies jusqu'Ă  ce que les valeurs de tous les concepts parents soient trouvĂ©es et que le regroupement soit effectuĂ©. Ensuite, leurs valeurs peuvent ĂȘtre calculĂ©es pour chaque groupe, associĂ©es Ă  des attributs et / ou utilisĂ©es pour filtrer les groupes. Avec cette approche paresseuse pour Ă©valuer et vĂ©rifier les conditions, il n'est pas nĂ©cessaire de disposer d'une section HAVING sĂ©parant les conditions de filtre avant et aprĂšs le regroupement. Le runtime le fera automatiquement.



Les principales fonctions d'agrĂ©gation sont count , sum, moy , min , max . Le but des fonctions peut ĂȘtre compris Ă  partir de leurs noms. Étant donnĂ© que le composant de modĂ©lisation peut naturellement fonctionner avec des types de donnĂ©es composites, vous pouvez Ă©galement ajouter une fonction qui renvoie des valeurs groupĂ©es sous forme de liste. Appelons ça groupe . Son argument d'entrĂ©e est une expression. La fonction renvoie une liste des rĂ©sultats de l'Ă©valuation de cette expression pour chaque Ă©lĂ©ment du groupe. En le combinant avec d'autres fonctions, vous pouvez implĂ©menter n'importe quelle fonction d'agrĂ©gation arbitraire. La fonction de groupe sera plus pratique que les fonctions SQL telles que group_concat ou json_arrayagqui sont souvent utilisĂ©es comme Ă©tape intermĂ©diaire pour obtenir un tableau de valeurs de champ.



Exemple de regroupement:



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
      
      





L'attribut orders contiendra une liste de toutes les commandes des utilisateurs, ordersTotal - le total de toutes les commandes. La condition ordersTotal> 100 sera vérifiée une fois le regroupement effectué et la fonction de somme calculée .



Concept défini par la fonction



La forme logique dĂ©clarative de la description des concepts n'est pas toujours pratique. Parfois, il sera plus pratique de dĂ©finir une sĂ©quence de calculs dont le rĂ©sultat sera l'essence mĂȘme du concept. Cette situation se produit souvent lorsqu'il est nĂ©cessaire de charger des faits Ă  partir de sources de donnĂ©es externes, par exemple d'une base de donnĂ©es, de fichiers, d'envoyer des demandes Ă  des services externes, etc. Il sera pratique de reprĂ©senter le concept sous la forme d'une fonction qui traduit la requĂȘte d'entrĂ©e en requĂȘte vers la base de donnĂ©es et renvoie le rĂ©sultat de l'exĂ©cution de cette requĂȘte. Parfois, il est logique d'abandonner une conclusion logique, en la remplaçant par une implĂ©mentation spĂ©cifique qui prend en compte les spĂ©cificitĂ©s d'un problĂšme spĂ©cifique et le rĂ©sout plus efficacement. Il est Ă©galement plus pratique de dĂ©crire dans un style fonctionnel des sĂ©quences infinies qui gĂ©nĂšrent des entitĂ©s conceptuelles, par exemple, une sĂ©quence d'entiers.



Les principes de travail avec de tels concepts devraient ĂȘtre les mĂȘmes qu'avec les autres concepts dĂ©crits ci-dessus. La recherche de solutions doit ĂȘtre lancĂ©e avec les mĂȘmes mĂ©thodes. Ils peuvent eux-mĂȘmes ĂȘtre utilisĂ©s comme concepts parents dans les dĂ©finitions de concepts. Seule la mise en Ɠuvre interne de la recherche d'une solution devrait diffĂ©rer. Par consĂ©quent, nous allons introduire une autre façon de dĂ©finir un concept Ă  l'aide d'une fonction:



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





Pour définir un concept défini à l'aide d'une fonction, vous devez spécifier une liste de ses attributs et une fonction de génération d'objets. J'ai décidé de faire de la liste des attributs un élément obligatoire de la définition, car cela simplifiera l'utilisation d'un tel concept - pour comprendre sa structure, vous n'aurez pas à étudier la fonction de génération d'objets.



Parlons maintenant de la fonction de gĂ©nĂ©ration d'objets. De toute Ă©vidence, il devrait recevoir une demande en entrĂ©e - les valeurs initiales des attributs. Puisque ces valeurs peuvent ĂȘtre spĂ©cifiĂ©es ou non, pour plus de commoditĂ©, elles peuvent ĂȘtre placĂ©es dans un tableau associatif, qui sera l'argument d'entrĂ©e de la fonction. Il sera Ă©galement utile de savoir dans quel mode la recherche des significations des concepts est lancĂ©e - trouver toutes les valeurs possibles, ne trouver que la premiĂšre ou vĂ©rifier uniquement l'existence d'une solution. Par consĂ©quent, nous ajoutons le mode de recherche comme deuxiĂšme argument d'entrĂ©e.



Le rĂ©sultat de l'Ă©valuation de la fonction doit ĂȘtre une liste d'objets conceptuels. Mais, Ă©tant donnĂ© que la procĂ©dure d'infĂ©rence basĂ©e sur la recherche avec retour arriĂšre consommera ces valeurs une par une, il est possible de faire de l'argument de sortie de la fonction non pas la liste elle-mĂȘme, mais un itĂ©rateur. Cela rendrait la dĂ©finition du concept plus flexible, par exemple, cela permettrait, si nĂ©cessaire, de mettre en Ɠuvre une Ă©valuation paresseuse ou une sĂ©quence infinie d'objets. Vous pouvez utiliser un itĂ©rateur de n'importe quelle collection standard ou crĂ©er votre propre implĂ©mentation personnalisĂ©e. L'Ă©lĂ©ment de collection doit ĂȘtre un tableau associatif avec les valeurs des attributs de concept. Les essences du concept seront créées automatiquement sur leur base.

L'utilisation d'un itérateur comme type de retour a ses inconvénients. C'est plus encombrant et moins convivial que de simplement renvoyer une liste de résultats. Trouver la meilleure option qui allie polyvalence, simplicité et convivialité est un défi pour l'avenir.



À titre d'exemple, considĂ©rons le concept qui dĂ©crit les intervalles de temps. Disons que nous voulons diviser la journĂ©e de travail en intervalles de 15 minutes. Nous pouvons le faire avec une fonction assez simple:



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();
}
      
      





La fonction renvoie un itĂ©rateur pour toutes les valeurs possibles d'un intervalle de temps de 15 minutes pour une journĂ©e de travail. Il peut ĂȘtre utilisĂ©, par exemple, pour rechercher des emplacements gratuits qui n'ont pas encore Ă©tĂ© rĂ©servĂ©s:



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





La fonction ne vĂ©rifie pas le rĂ©sultat des calculs pour la conformitĂ© avec la requĂȘte de requĂȘte , cela sera fait automatiquement lors de la conversion d'un tableau d'attributs en une entitĂ©. Mais si nĂ©cessaire, des champs de requĂȘte peuvent ĂȘtre utilisĂ©s pour optimiser la fonction. Par exemple, formez une requĂȘte de base de donnĂ©es basĂ©e sur les champs de requĂȘte d'un concept.



Un concept Ă  travers une fonction combine sĂ©mantique logique et fonctionnelle. Si dans le paradigme fonctionnel la fonction calcule le rĂ©sultat pour les valeurs donnĂ©es des arguments d'entrĂ©e, alors dans le paradigme logique, il n'y a pas de division en arguments de sortie et d'entrĂ©e. Seule une partie des arguments peut ĂȘtre donnĂ©e, et dans n'importe quelle combinaison, et la fonction a besoin de trouver les valeurs des arguments restants. En pratique, il n'est pas toujours possible d'implĂ©menter une telle fonction capable d'effectuer des calculs dans n'importe quelle direction, il est donc logique de limiter les combinaisons possibles d'arguments libres. Par exemple, dĂ©clarez que certains arguments doivent ĂȘtre liĂ©s Ă  des valeurs avant d'Ă©valuer une fonction. Pour ce faire, marquez ces attributs dans la dĂ©finition du concept avec le mot-clĂ© requis .



À titre d'exemple, considĂ©rons le concept qui dĂ©finit les valeurs d'une certaine Ă©chelle exponentielle.



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; 
    }
}}
      
      





La fonction renvoie un itĂ©rateur qui gĂ©nĂšre des entitĂ©s de concept Ă  l'aide d'une Ă©valuation diffĂ©rĂ©e. La taille de la sĂ©quence est limitĂ©e par la valeur de l'attribut limit , mais si elle est nulle, elle devient infinie. Les concepts basĂ©s sur des sĂ©quences infinies doivent ĂȘtre utilisĂ©s trĂšs soigneusement, car ils ne garantissent pas que la routine d'infĂ©rence se terminera. L'attribut limit est de nature auxiliaire et est utilisĂ© pour organiser les calculs. Nous ne pouvons pas le dĂ©duire des valeurs d'autres attributs, il doit ĂȘtre connu avant le dĂ©but du calcul, il a donc Ă©tĂ© marquĂ© comme obligatoire



Nous avons envisagé l'une des options pour ce à quoi pourrait ressembler un concept en tant que fonction. Mais les problÚmes de sécurité et d'utilisation de ces concepts nécessitent des recherches plus détaillées à l'avenir.



Aplatissement des collections imbriquées



Certains dialectes SQL qui peuvent travailler avec des données sous forme d'objet prennent en charge une opération telle que UNNEST , qui convertit le contenu d'une collection en un format de table (ensemble de lignes) et ajoute la table résultante à la clause FROM . Il est généralement utilisé pour aplatir des objets avec des structures imbriquées, en d'autres termes, pour les aplatir ou les aplatir. Des exemples de tels langages sont BigQuery et SQL ++.



Disons que nous stockons les informations utilisateur en tant qu'objet 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"
}]
}, 

]
      
      





Les objets utilisateur stockent des collections imbriquĂ©es avec des listes d'amis et de lieux de travail. Il est possible d'extraire les informations jointes sur les lieux de travail de l'utilisateur en les collant avec les donnĂ©es sur l'utilisateur prises au niveau supĂ©rieur de l'objet Ă  l'aide d'une requĂȘte 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;
      
      





Le résultat sera:



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





Cette opération est discutée plus en détail ici .



Contrairement Ă  SQL, dans le composant de modĂ©lisation, les donnĂ©es incorporĂ©es doivent ĂȘtre converties non pas au format tabulaire, mais au format objet. Les concepts discutĂ©s ci-dessus nous y aideront - un concept dĂ©fini Ă  travers une fonction et un concept anonyme. Un concept Ă  travers une fonction vous permettra de transformer une collection imbriquĂ©e en format objet, et un concept anonyme vous permettra d'intĂ©grer sa dĂ©finition dans la liste des concepts parents et d'accĂ©der aux valeurs de leurs attributs contenant la collection imbriquĂ©e souhaitĂ©e.



Puisque la dĂ©finition complĂšte d'un concept Ă  travers une fonction est trop lourde pour ĂȘtre utilisĂ©e comme un concept anonyme:



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





nous devons trouver un moyen de le raccourcir. Tout d'abord, vous pouvez supprimer le titre de la dĂ©finition de fonction avec les paramĂštres de requĂȘte et de mode . Dans la position du concept parent, l'argument mode sera toujours "trouver toutes les valeurs de concept". L'argument de requĂȘte sera toujours vide, car les dĂ©pendances sur les attributs d'autres concepts peuvent ĂȘtre incorporĂ©es dans le corps de la fonction. Le mot-clĂ© concept peut Ă©galement ĂȘtre supprimĂ©. Ainsi, on obtient:



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





Si le nom du concept n'est pas important, il peut ĂȘtre omis et il sera gĂ©nĂ©rĂ© automatiquement:



(attribute1, attribute2, ...) {
}
      
      





Si Ă  l'avenir, il est possible de crĂ©er un compilateur capable de dĂ©duire une liste d'attributs Ă  partir du type d'objets retournĂ©s par une fonction, alors la liste d'attributs peut ĂȘtre supprimĂ©e:



{
}
      
      







Ainsi, un exemple avec les utilisateurs et leurs lieux de travail en tant que concept ressemblerait Ă  ceci:



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





La solution s'est avĂ©rĂ©e un peu verbeuse, mais universelle. Dans le mĂȘme temps, si aucune transformation des objets de la collection imbriquĂ©e n'est requise, elle peut ĂȘtre considĂ©rablement simplifiĂ©e:



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





résultats



Cet article s'est concentrĂ© sur deux questions. Tout d'abord, le transfert de certaines fonctionnalitĂ©s du langage SQL vers le composant de modĂ©lisation: requĂȘtes imbriquĂ©es, jointures externes, agrĂ©gations et jointures avec des collections imbriquĂ©es. DeuxiĂšmement, l'introduction de deux nouvelles constructions dans le composant de modĂ©lisation: des dĂ©finitions anonymes de concepts et des concepts dĂ©finis Ă  travers une fonction.



Un concept anonyme est une forme abrĂ©gĂ©e d'une dĂ©finition de concept, destinĂ©e Ă  ĂȘtre utilisĂ©e comme arguments pour des fonctions ( find , findOne et existe ) ou comme dĂ©finition de concept imbriquĂ© dans une clause where... Il peut ĂȘtre considĂ©rĂ© comme analogue aux dĂ©finitions de fonction anonymes dans les langages de programmation fonctionnelle.



Un concept dĂ©fini Ă  travers une fonction est un concept dont la maniĂšre de gĂ©nĂ©rer des objets est exprimĂ©e Ă  l'aide d'un algorithme sous une forme explicite. C'est une sorte d '"interface" entre les mondes de la programmation fonctionnelle ou orientĂ©e objet et de la programmation logique. Elle sera utile dans de nombreux cas lorsqu'une maniĂšre logique de dĂ©finir un concept n'est pas pratique ou impossible: par exemple, pour charger les faits initiaux Ă  partir de leurs bases de donnĂ©es, fichiers ou des requĂȘtes vers des services distants, pour remplacer la recherche logique universelle par ses propres mise en Ɠuvre optimisĂ©e, ou pour implĂ©menter des rĂšgles arbitraires crĂ©ant des objets.



Il est intĂ©ressant de noter que les emprunts Ă  SQL, tels que les requĂȘtes imbriquĂ©es, les jointures externes et les jointures de collection imbriquĂ©es, n'ont pas nĂ©cessitĂ© de changements majeurs dans la logique du composant de modĂ©lisation et ont Ă©tĂ© implĂ©mentĂ©s Ă  l'aide de concepts tels que des concepts anonymes et des concepts via une fonction. Cela suggĂšre que ces types de concepts sont des outils flexibles et polyvalents avec une grande puissance expressive. Je pense qu'il existe de nombreuses façons plus intĂ©ressantes de les utiliser.



Ainsi, dans cet article et dans deux articles prĂ©cĂ©dents, j'ai dĂ©crit les concepts de base et les Ă©lĂ©ments du composant de modĂ©lisation d'un langage de programmation hybride. Mais, avant de passer aux problĂ©matiques d'intĂ©gration d'un composant de modĂ©lisation avec un composant de calcul qui implĂ©mente un style de programmation fonctionnel ou orientĂ© objet, j'ai dĂ©cidĂ© de consacrer l'article suivant Ă  ses options possibles pour son application. De mon point de vue, le composant de modĂ©lisation prĂ©sente des avantages par rapport aux langages de requĂȘte traditionnels, principalement SQL, et peut ĂȘtre utilisĂ© seul sans intĂ©gration profonde avec le composant de calcul, que je souhaite dĂ©montrer avec plusieurs exemples dans le prochain article.



Le texte scientifique complet en anglais est disponible Ă  l' adresse : papers.ssrn.com/sol3/papers.cfm?abstract_id=3555711



Liens vers les publications précédentes:



Conception d'un langage de programmation multi-paradigme. Partie 1 - À quoi ça sert?

Nous concevons un langage de programmation multi-paradigme. Partie 2 - Comparaison des modĂšles de construction en PL / SQL, LINQ et GraphQL Nous

concevons un langage de programmation multi-paradigmes. Partie 3 - Vue d'ensemble des langages de représentation des connaissances Nous

concevons un langage de programmation multi-paradigmes. Partie 4 - Constructions de base du langage de modélisation Nous

concevons un langage de programmation multi-paradigmes. Partie 5 - Caractéristiques de la programmation logique



All Articles