O componente de modelagem se destina a uma descrição declarativa de um modelo de domínio na forma de uma ontologia - uma rede de instâncias de dados (fatos) e conceitos abstratos interconectados por meio de relacionamentos. É baseado na lógica de quadros - um híbrido de uma abordagem orientada a objetos para representação de conhecimento e lógica de primeira ordem. Seu elemento principal é um conceito que descreve um objeto modelado usando um conjunto de atributos. O conceito é construído com base em outros conceitos ou fatos, os conceitos iniciais serão chamados de parentais , o derivado - filho... Os relacionamentos vinculam os valores dos atributos dos conceitos filho e pai ou restringem seus valores possíveis. Decidi incluir relacionamentos na definição do conceito, de forma que todas as informações sobre ele estejam, se possível, em um só lugar. O estilo de sintaxe para definições de conceito será semelhante ao SQL - atributos, conceitos pai e relacionamentos entre eles devem ser separados em seções diferentes.
Neste post, quero apresentar as principais formas de definição de conceitos.
Primeiro, um conceito que é construído pela transformação de conceitos parentais .
Em segundo lugar, o estilo orientado a objetos implica herança , o que significa que você precisa de um mecanismo que permite criar um conceito herdando os atributos e relacionamentos dos conceitos pais, expandindo ou estreitando-os.
Terceiro, acredito que um mecanismo seria útil para definir a relação entre os conceitos de pares - sem dividir em conceitos de filho e pai.
Agora, vamos examinar detalhadamente os principais tipos de componentes de modelagem.
Vamos começar com os fatos
Os fatos representam uma descrição do conhecimento específico sobre um domínio na forma de um conjunto nomeado de pares de valores-chave:
fact < > {
< > : < >
...
}
Por exemplo:
fact product {
name: “Cabernet Sauvignon”,
type: “red wine”,
country: “Chile”
}
O nome do fato pode não ser exclusivo, por exemplo, pode haver muitos produtos com diferentes nomes, tipos e países de origem. Consideraremos os fatos idênticos se seus nomes e os nomes e valores de seus atributos coincidirem.
Uma analogia pode ser traçada entre os fatos do componente de modelagem e os fatos da linguagem Prolog. Eles diferem apenas na sintaxe. No Prolog, argumentos de fato são identificados por sua posição e os atributos dos fatos do componente de modelagem são identificados por nome.
Conceitos
Um conceito é uma estrutura que descreve uma entidade abstrata e é baseada em outros conceitos e fatos. A definição de um conceito inclui um nome, listas de atributos e conceitos filhos. E também uma expressão lógica que descreve as dependências entre seus atributos (conceito filho) e os atributos dos conceitos pai, permitindo que você deduza o valor dos atributos do conceito filho:
concept < > < > (
< > = <>,
...
)
from
< > < > (
< > = <>
...
),
…
where < >
Um exemplo de definição de lucro com base na receita e custo :
concept profit p (
value = r.value – c.value,
date
) from revenue r, cost c
where p.date = r.date = c.date
A definição de um conceito é semelhante em forma a uma consulta SQL, mas em vez do nome da tabela, você precisa especificar os nomes dos conceitos pai e, em vez das colunas retornadas - os atributos do conceito filho. Além disso, um conceito tem um nome pelo qual pode ser referido nas definições de outros conceitos ou em consultas de modelo. O conceito pai pode ser o próprio conceito ou os fatos. A expressão de relacionamento na cláusula where é uma expressão booleana que pode incluir operadores lógicos, condições de igualdade, operadores aritméticos, chamadas de função, etc. Seus argumentos podem ser variáveis, constantes e referências a atributos de conceitos pai e filho. As referências de atributos têm o seguinte formato:
< >.< >
Em comparação com a lógica de quadro, na definição de um conceito, sua estrutura (atributos) é combinada com relacionamentos com outros conceitos (conceitos pais e expressão de relacionamentos). Do meu ponto de vista, isso permite tornar o código mais compreensível, uma vez que todas as informações sobre o conceito são coletadas em um só lugar. Também está em conformidade com o princípio de encapsulamento, no sentido de que os detalhes de implementação de um conceito estão ocultos em sua definição. Para comparação, um pequeno exemplo na linguagem da lógica de quadro pode ser encontrado na publicação anterior .
A expressão das relações tem uma forma conjuntiva (consiste em expressões conectadas por operações lógicas "E") e deve incluir condições de igualdade para todos os atributos do conceito filho, suficientes para determinar seus valores. Além disso, pode incluir condições que limitam o significado dos conceitos principais ou os conectam entre si. Se nem todos os conceitos pai estiverem relacionados na cláusula where , o mecanismo de inferência retornará todas as combinações possíveis de seus valores como resultado (semelhante à operação FULL JOIN em SQL).
Por conveniência, algumas das condições para igualdade de atributos podem ser colocadas na seção de atributos dos conceitos filho e pai. Por exemplo, na definição de lucro, a condição para o atributoo valor é movido para a seção de atributos e para o atributo de data é deixado na seção where . Você também pode transferi-los para o de seção :
concept profit p (
value = r.value – c.value,
date = r.date
) from revenue r, cost c (date = r.date)
Este açúcar sintático permite tornar as dependências entre os atributos mais explícitas e distingui-los de outras condições.
Os conceitos seguem as regras do Prolog, mas têm semânticas ligeiramente diferentes. Prolog se concentra na construção de afirmações e questões logicamente relacionadas a eles. Os conceitos têm como objetivo principal estruturar dados de entrada e extrair informações deles. Os atributos do conceito correspondem aos argumentos dos termos do Prolog. Mas se no Prolog o termo argumentos são vinculados por meio de variáveis, então, em nosso caso, os atributos podem ser acessados diretamente por seus nomes.
Visto que a lista de conceitos pai e as condições do relacionamento são separados em seções separadas, a inferência será ligeiramente diferente daquela no Prolog. Descreverei em geral seu algoritmo. Conceitos pais serão emitidos na ordem em que são especificados no de seção . A busca por uma solução para o próximo conceito é realizada para cada solução parcial dos conceitos anteriores da mesma forma que na resolução SLD. Mas para cada solução parcial, a validade da expressão de relação da cláusula where é verificada.... Como essa expressão está na forma de conjunção, cada subexpressão é testada separadamente. Se a subexpressão for falsa, essa solução parcial é rejeitada e a pesquisa prossegue para a próxima. Se algum dos argumentos da subexpressão ainda não estiver definido (não associado a valores), sua validação será adiada. Se a subexpressão for um operador de igualdade e apenas um de seus argumentos for definido, o sistema de inferência encontrará seu valor e tentará associá-lo ao argumento restante. Isso é possível se o argumento livre for um atributo ou variável.
Por exemplo, ao exibir as entidades do conceito de lucro , primeiro serão encontradas as entidades do conceito de receita e, consequentemente, os valores de seus atributos. Então a igualdade p.date = r.date = c.datena seção onde permitirá que você associe atributos de data e outros conceitos a valores . Quando a busca lógica chega ao conceito de custo , o valor de seu atributo de data já será conhecido e será o argumento de entrada para este ramo da árvore de busca. Pretendo falar em detalhes sobre algoritmos de inferência em uma das próximas publicações.
A diferença do Prolog é que nas regras do Prolog tudo são predicados - e chamadas para outras regras e predicados embutidos de igualdade, comparação, etc. E a ordem de sua verificação deve ser especificada explicitamente, por exemplo, primeiro duas regras devem ir e depois igualdade de variáveis:
profit(value,date) :- revenue(rValue, date), cost(cValue, date), value = rValue – cValue
Nesta ordem, eles serão executados. O componente de modelagem assume que todos os cálculos de condições na cláusula where são determinísticos, ou seja, não requerem mergulho recursivo no próximo ramo de pesquisa. Visto que seu cálculo depende apenas de seus argumentos, eles podem ser calculados em ordem arbitrária, pois os argumentos estão vinculados a valores.
Como resultado da inferência, todos os atributos do conceito filho devem ser associados a valores. E também a expressão das relações deve ser verdadeira e não conter subexpressões indefinidas. É importante notar que a derivação de conceitos parentais não precisa ser bem-sucedida. Há casos em que é necessário verificar a falha da derivação do conceito pai a partir dos dados originais, por exemplo, em operações de negação. A ordem dos conceitos pai no de seção determina a ordem em que a árvore de decisão é atravessado. Isso permite otimizar a busca por uma solução, a partir dos conceitos que mais limitam o espaço de busca.
A tarefa da inferência lógica é encontrar todas as substituições possíveis de atributos do conceito filho e representar cada um deles como um objeto. Esses objetos são considerados idênticos se os nomes de seus conceitos, nomes e valores de atributos coincidirem.
É considerado aceitável criar vários conceitos com o mesmo nome, mas com implementações diferentes, incluindo um conjunto diferente de atributos. Podem ser versões diferentes de um conceito, conceitos relacionados que podem ser convenientemente combinados sob um nome, conceitos idênticos de fontes diferentes, etc. Na conclusão lógica, todas as definições existentes do conceito serão consideradas e os resultados de sua pesquisa serão combinados. Vários conceitos com os mesmos nomes são análogos à regra do Prolog, em que uma lista de termos tem uma forma disjuntiva (os termos são ORed).
Herança de conceito
Uma das relações mais comuns entre os conceitos são as relações hierárquicas, como gênero-espécie. Sua peculiaridade é que as estruturas dos conceitos de filho e pai serão muito semelhantes. Portanto, o suporte do mecanismo de herança no nível da sintaxe é muito importante: sem ele, os programas estarão cheios de código repetitivo. Ao construir uma rede de conceitos, seria conveniente reutilizar seus atributos e relacionamentos. Embora a lista de atributos seja fácil de expandir, encurtar ou redefinir alguns deles, a situação com relações de modificação é mais complicada. Uma vez que são uma expressão lógica na forma conjuntiva, é fácil adicionar subexpressões adicionais a ela. No entanto, a exclusão ou alteração pode exigir complicações de sintaxe significativas. Os benefícios disso não são tão óbviosportanto, adiaremos essa tarefa para o futuro.
Você pode declarar um conceito com base na herança usando a seguinte construção:
concept < > < > is
< > < > (
< > = <>,
...
),
...
with < > = <>, ...
without < >, ...
where < >
A seção is contém uma lista de conceitos herdados. Seus nomes podem ser especificados diretamente nesta seção. Ou, especificar a lista completa dos conceitos de pais na de seção , e em é - aliases de somente aqueles deles que será herdada:
concept < > < > is
< >,
…
from
< > < > (
< > = <>
...
),
…
with < > = <>, ...
without < >, ...
where < >
A seção com permite expandir a lista de atributos de conceitos herdados ou substituir alguns deles, a seção sem - para encurtar.
O algoritmo de inferência de um conceito baseado em herança é o mesmo do conceito discutido acima. A única diferença é que a lista de atributos é gerada automaticamente com base na lista de atributos do conceito pai, e a expressão das relações é complementada com operações de igualdade de atributos dos conceitos filho e pai.
Vamos considerar vários exemplos de uso do mecanismo de herança. A herança permite que você crie um conceito baseado em um existente, livrando-se dos atributos que são significativos apenas para o conceito pai, mas não para o conceito filho. Por exemplo, se os dados de origem são apresentados na forma de uma tabela, então as células de certas colunas podem receber seus próprios nomes (eliminando o atributo com o número da coluna):
concept revenue is tableCell without columnNum where columnNum = 2
Você também pode converter vários conceitos relacionados em um formulário genérico. A seção com é necessária para converter alguns dos atributos para o formato geral e adicionar os que faltam. Por exemplo, os dados de origem podem ser documentos de diferentes versões, cuja lista de campos mudou ao longo do tempo:
concept resume is resumeV1 with skills = 'N/A'
concept resume is resumeV2 r with skills = r.coreSkills
Vamos supor que a primeira versão do conceito "Currículo" não tivesse um atributo com habilidades e a segunda versão tivesse um nome diferente.
Em muitos casos, pode ser necessário expandir a lista de atributos. Tarefas comuns são alterar o formato de atributos, adicionar atributos que dependem funcionalmente de atributos existentes ou dados externos, etc. Por exemplo:
concept price is basicPrice with valueUSD = valueEUR * getCurrentRate('USD', 'EUR')
Também é possível simplesmente combinar vários conceitos em um nome sem alterar sua estrutura. Por exemplo, para indicar que são do mesmo gênero:
concept webPageElement is webPageLink
concept webPageElement is webPageInput
Ou crie um subconjunto de um conceito filtrando algumas de suas entidades:
concept exceptionalPerformer is employee where performanceEvaluationScore > 0.95
A herança múltipla também é possível, na qual um conceito filho herda os atributos de todos os conceitos pai. Se houver nomes de atributos idênticos, a prioridade será dada ao conceito pai à esquerda da lista. Você também pode resolver esse conflito manualmente substituindo explicitamente o atributo desejado na seção
with. Por exemplo, esse tipo de herança seria conveniente se você precisar coletar vários conceitos relacionados em uma estrutura "plana":
concept employeeInfo is employee e, department d where e.departmentId = d.id
A herança sem alterar a estrutura dos conceitos complica a verificação da identidade dos objetos. Como exemplo, considere a definição de ExcepcionalPerformer . As consultas sobre os conceitos pai ( funcionário ) e filho ( Executor excepcional ) retornarão a mesma entidade funcionário. Os objetos que o representam terão significados idênticos. Eles terão uma fonte de dados comum, a mesma lista e valores de atributo, para um nome de conceito diferente, dependendo do conceito para o qual a consulta foi feita. Portanto, a operação de igualdade de objetos deve levar esse recurso em consideração. Os nomes dos conceitos são considerados iguais se coincidirem ou estiverem ligados por uma relação de herança transitiva sem alterar a estrutura.
Herança é um mecanismo útil que permite expressar explicitamente os relacionamentos entre conceitos como classe-subclasse, private-common e set-subset. E também se livre de código duplicado nas definições de conceito e torne o código mais compreensível. O mecanismo de herança é baseado na adição / remoção de atributos, combinando vários conceitos sob um nome e adicionando condições de filtragem. Nenhuma semântica especial está embutida nele, todos podem perceber e aplicá-lo como quiserem. Por exemplo, construa uma hierarquia do particular ao geral, como nos exemplos com os conceitos currículo , preço e webPageElement . Ou, inversamente, de geral para específico, como nos exemplos com os conceitos de receita e desempenho excepcional... Isso permitirá que você se ajuste com flexibilidade às especificações das fontes de dados.
Conceito para descrever relacionamentos
Foi decidido que, para a conveniência de compreender o código e facilitar a integração do componente de modelagem com o modelo OOP, o relacionamento do conceito filho com o pai deve ser construído em sua definição. Assim, essas relações definem a forma de se obter um conceito de filho dos pais. Se o modelo de domínio for construído em camadas, e cada nova camada for baseada na anterior, isso se justifica. Mas, em alguns casos, a relação entre os conceitos deve ser declarada separadamente e não incluída na definição de um dos conceitos. Pode ser um relacionamento universal que você deseja definir em termos gerais e aplicar a diferentes conceitos, por exemplo, o relacionamento pai-filho. Ou uma relação conectando dois conceitos deve ser incluída na definição de ambos os conceitos, de modo que tanto a essência do primeiro conceito com os atributos conhecidos do segundo possam ser encontrados, e vice-versa.Então, para evitar duplicação de código, será conveniente definir a relação separadamente.
Na definição de um relacionamento, é necessário listar os conceitos nele incluídos e definir uma expressão lógica conectando-os entre si:
relation < >
between < > < > (
< > = <>,
...
),
...
where < >
Por exemplo, um relacionamento que descreve retângulos aninhados pode ser definido da seguinte maneira:
relation insideSquareRelation between square inner, square outer
where inner.xLeft > outer.xLeft and inner.xRight < outer.xRight
and inner.yBottom > outer.yBottom and inner.yUp < outer.yUp
Essa relação, na verdade, é um conceito comum, cujos atributos são as essências de conceitos aninhados:
concept insideSquare (
inner = i
outer = o
) from square i, square o
where i.xLeft > o.xLeft and i.xRight < o.xRight
and i.yBottom > o.yBottom and i.yUp < o.yUp
O relacionamento pode ser usado em definições de conceito junto com outros conceitos pai. Os conceitos incluídos na relação serão acessíveis de fora e cumprirão o papel de seus atributos. Os nomes dos atributos corresponderão aos aliases de conceito aninhados. O exemplo a seguir afirma que o formulário HTML inclui os elementos HTML que estão localizados dentro dele na página HTML:
oncept htmlFormElement is e
from htmlForm f, insideSquareRelation(inner = e, outer = f), htmlElement e
Ao procurar por uma solução, ele primeiro encontrará todos os valores do conceito htmlForm , em seguida, eles serão associados ao conceito aninhado externo do relacionamento insideSquare e os valores de seu atributo interno serão encontrados . No final, aqueles valores internos relacionados ao conceito de htmlElement serão filtrados .
A relação também pode receber semântica funcional - ela pode ser usada como uma função de um tipo booleano para verificar se a relação é satisfeita para as entidades fornecidas de conceitos aninhados:
oncept htmlFormElement is e
from htmlElement e, htmlForm f
where insideSquareRelation(e, f)
Ao contrário do caso anterior, aqui a relação é tratada como uma função, o que afetará a ordem de inferência. A avaliação da função será adiada até o momento em que todos os seus argumentos sejam associados a valores. Ou seja, primeiro todas as combinações de valores dos conceitos htmlElement e htmlForm serão encontradas , e então aquelas que não corresponderem à relação dentro deSquareRelation serão filtradas . Pretendo falar com mais detalhes sobre a integração dos paradigmas de programação lógico e funcional em uma das próximas publicações.
Agora é hora de ver um pequeno exemplo.
Definições de fatos e tipos básicos de conceitos são suficientes para implementar o exemplo com os devedores desde a primeira publicação. Suponha que temos dois arquivos CSV armazenando informações sobre clientes (ID do cliente, nome e e-mail) e faturas (ID da conta, ID do cliente, data, valor devido, valor pago).
E também existe um determinado procedimento que lê o conteúdo desses arquivos e os converte em um conjunto de fatos:
fact cell {
table: “TableClients”,
value: 1,
rowNum: 1,
columnNum: 1
};
fact cell {
table: “TableClients”,
value: “John”,
rowNum: 1,
columnNum: 2
};
fact cell {
table: “TableClients”,
value: “john@somewhere.net”,
rowNum: 1,
columnNum: 3
};
…
fact cell {
table: “TableBills”,
value: 1,
rowNum: 1,
columnNum: 1
};
fact cell {
table: “TableBills”,
value: 1,
rowNum: 1,
columnNum: 2
};
fact cell {
table: “TableBills”,
value: 2020-01-01,
rowNum: 1,
columnNum: 3
};
fact cell {
table: “TableBills”,
value: 100,
rowNum: 1,
columnNum: 4
};
fact cell {
table: “TableBills”,
value: 50,
rowNum: 1,
columnNum: 5
};
Primeiro, vamos dar às células da tabela nomes significativos:
concept clientId is cell where table = “TableClients” and columnNum = 1;
concept clientName is cell where table = “TableClients” and columnNum = 2;
concept clientEmail is cell where table = “TableClients” and columnNum = 3;
concept billId is cell where table = “TableBills” and columnNum = 1;
concept billClientId is cell where table = “TableBills” and columnNum = 2;
concept billDate is cell where table = “TableBills” and columnNum = 3;
concept billAmountToPay is cell where table = “TableBills” and columnNum = 4;
concept billAmountPaid is cell where table = “TableBills” and columnNum = 5;
Agora você pode combinar células de uma linha em um único objeto:
concept client (
id = id.value,
name = name.value,
email = email.value
) from clientId id, clientName name, clientEmail email
where id.rowNum = name.rowNum = email.rowNum;
concept bill (
id = id.value,
clientId = clientId.value,
date = date.value,
amountToPay = toPay.value,
amountPaid = paid.value
) from billId id, billClientId clientId, billDate date, billAmountToPay toPay, billAmountPaid paid
where id.rowNum = clientId.rowNum = date.rowNum = toPay.rowNum = paid.rowNum;
Vamos apresentar os conceitos "Fatura não paga" e "Devedor":
concept unpaidBill is bill where amountToPay > amountPaid;
concept debtor is client c where exist(unpaidBill {clientId: c.id});
Ambas as definições usam herança, o conceito unpaidBill é um subconjunto dos conceitos fatura , devedor - o conceito de cliente . A definição de devedor contém uma subconsulta para o conceito unpaidBill . Consideraremos em detalhes o mecanismo de consultas aninhadas posteriormente em uma das seguintes publicações.
A título de exemplo de um conceito "plano", iremos também definir o conceito de "Dívida do cliente", no qual combinamos alguns campos dos conceitos de "Cliente" e "Conta":
concept clientDebt (
clientName = c.name,
billDate = b.date,
debt = b. amountToPay – b.amountPaid
) from unpaidBill b, client c(id = b.client);
A dependência entre os atributos dos conceitos cliente e fatura é movida para a seção from , e as dependências do conceito filho clientDebt - para a seção de seus atributos. Se desejado, eles podem ser colocados na seção where - o resultado será o mesmo. Mas, do meu ponto de vista, a versão atual é mais concisa e enfatiza melhor o propósito dessas dependências - definir relacionamentos entre conceitos.
Agora, vamos tentar definir o conceito de um inadimplente malicioso que tem pelo menos 3 faturas não pagas consecutivas. Para fazer isso, você precisa de um relacionamento que permita solicitar as faturas de um cliente pela data. Uma definição genérica seria assim:
relation billsOrder between bill next, bill prev
where next.date > prev.date and next.clientId = prev.clientId and not exist(
bill inBetween
where next.clientId = inBetween.clientId
and next.date > inBetween.date > prev.date
);
Afirma que duas faturas são consecutivas se pertencerem ao mesmo cliente, a data de uma for superior à data da outra e não existir outra fatura entre elas. Neste estágio, não quero me alongar sobre a complexidade computacional de tal definição. Mas se, por exemplo, sabemos que todas as notas fiscais são emitidas com intervalo de 1 mês, então pode ser bastante simplificado:
relation billsOrder between bill next, bill prev
where next.date = prev.date + 1 month and next.clientId = prev.clientId;
A sequência de 3 faturas não pagas terá a seguinte aparência:
concept unpaidBillsSequence (clientId = b1.clientId, bill1 = b1, bill2 = b2, bill3 = b3)
from
unpaidBill b1,
billsOrder next1 (next = b1, prev = b2)
unpaidBill b2
billsOrder next2 (next = b2, prev = b3)
unpaidBill b3;
Neste conceito, primeiro todas as faturas não pagas serão encontradas, então para cada uma delas a próxima fatura será encontrada usando a relação next1 . A noção b2 permitirá que você verifique se esta fatura não foi paga. Pelo mesmo princípio, usando next2 e b3 , será encontrada a terceira fatura não paga consecutiva. O identificador do cliente foi adicionado à lista de atributos separadamente, a fim de facilitar ainda mais a conexão deste conceito com o conceito de clientes:
concept hardCoreDefaulter is client c where exist(unpaidBillsSequence{clientId: c.id});
O exemplo do devedor demonstra como um modelo de domínio pode ser totalmente descrito em um estilo declarativo. Comparado com a implementação deste exemplo em OOP ou estilo funcional, o código resultante é muito conciso, compreensível e próximo de descrever o problema em uma linguagem natural.
Breves conclusões.
Então, eu propus três tipos principais de conceitos do componente de modelagem de linguagem híbrida:
- conceitos criados com base na transformação de outros conceitos;
- conceitos que herdam a estrutura e relacionamentos de outros conceitos;
- conceitos que definem as relações entre outros conceitos.
Esses três tipos de conceitos têm formas e finalidades diferentes, mas a lógica interna de encontrar soluções é a mesma para eles, apenas o método de formação da lista de atributos difere.
As definições de conceito se assemelham a consultas SQL - tanto na forma quanto na lógica interna de execução. Portanto, espero que a linguagem proposta seja compreensível para os desenvolvedores e tenha um limite de entrada relativamente baixo. E recursos adicionais, como o uso de conceitos nas definições de outros conceitos, herança, relações derivadas e definições recursivas, permitirão que você vá além do SQL e torne mais fácil estruturar e reutilizar o código.
Ao contrário de RDF e OWL, o componente de modelagem não faz distinção entre conceitos e relacionamentos - tudo são conceitos. Em contraste com as linguagens da lógica de quadros, os quadros que descrevem a estrutura de um conceito e as regras que definem as conexões entre eles são combinados. Ao contrário das linguagens de programação lógicas tradicionais, como Prolog, o elemento principal do modelo são os conceitos que possuem uma estrutura orientada a objetos, e não as regras que possuem uma estrutura plana. Esse design de linguagem pode não ser tão conveniente para criar ontologias em grande escala ou um conjunto de regras, mas é muito melhor para trabalhar com dados semiestruturados e para integrar fontes de dados distintas. Os conceitos do componente de modelagem se aproximam das classes do modelo OOP, o que deve facilitar a tarefa de incluir uma descrição declarativa do modelo no código da aplicação.
A descrição do componente de modelagem ainda não está completa. No próximo artigo, pretendo discutir questões do mundo da lógica do computador, como variáveis booleanas, negação e elementos de lógica de ordem superior. E depois disso - definições aninhadas de conceitos, agregações e conceitos que geram suas entidades usando uma determinada função.
O texto completo em estilo científico em inglês está disponível em: papers.ssrn.com/sol3/papers.cfm?abstract_id=3555711
Links para publicações anteriores:
Projetando uma linguagem de programação multiparadigma. Parte 1 - Para que serve?
Nós projetamos uma linguagem de programação multiparadigma. Parte 2 - Comparação de construção de modelo em PL / SQL, LINQ e GraphQL
Nós projetamos uma linguagem de programação multiparadigma. Parte 3 - Visão geral das linguagens de representação do conhecimento