Diseñamos un lenguaje de programación multiparadigma. Parte 6 - Préstamo de SQL

Continuamos la historia sobre la creación de un lenguaje de programación multi-paradigma que combina un estilo lógico declarativo con un estilo orientado a objetos y funcional, lo cual sería conveniente cuando se trabaja con datos semiestructurados e integrando datos de fuentes dispares. El lenguaje constará de dos componentes estrechamente integrados entre sí: el componente declarativo será el encargado de describir el modelo de dominio y el componente imperativo o funcional será el encargado de describir los algoritmos para trabajar con el modelo y la computación.



El componente de modelado de lenguaje híbrido es un conjunto de conceptos-objetos conectados por relaciones lógicas. Logré hablar sobre las principales formas de definir conceptos, incluida la herencia y la definición de relaciones entre ellos. Y también sobre algunos de los matices de la programación lógica, incluida la semántica del operador de negación y la lógica de orden superior. Puede encontrar una lista completa de publicaciones sobre este tema al final de este artículo.



En el campo del trabajo con datos, el líder indiscutible es el lenguaje SQL. Algunas de sus funcionalidades, que resultaron muy convenientes en la práctica, como la agregación, luego migraron a la programación lógica. Por lo tanto, será útil tomar prestado de SQL tanto como sea posible para el componente de modelado. En este artículo, quiero mostrarle cómo las consultas anidadas, las combinaciones externas y la agregación se pueden incrustar en las definiciones de conceptos. También hablaré de otro tipo de conceptos, que se describen utilizando una función que genera objetos (entidades) en un estilo algorítmico sin recurrir a la búsqueda lógica. Y le mostraré cómo puede usar matrices de objetos como conceptos principales por analogía con la operación SQL UNNESTque convierte colecciones a formato de tabla y permite unirlas a otras tablas en la cláusula FROM .



Definiciones anónimas de conceptos



En el mundo SQL, las consultas anidadas son una herramienta que se suele utilizar cuando es necesario obtener datos intermedios para su posterior procesamiento en la consulta principal. No existe una necesidad tan urgente de datos intermedios en el componente de modelado, ya que la forma de obtenerlos puede formalizarse como un concepto separado. Pero hay casos en los que las definiciones de conceptos anidadas serían útiles.



A veces es necesario modificar ligeramente el concepto, seleccionar sus atributos individuales, filtrar los valores. Si esta modificación es necesaria solo en un lugar, entonces no tiene sentido crear un concepto separado con un nombre único. Esta situación se encuentra a menudo cuando los conceptos son argumentos para funciones tales como existe , encontrar o findOne , que verifica la deducibilidad del concepto, encontrando todo o solo el primer objeto (entidad) del concepto. Aquí puede dibujar una analogía con funciones anónimas en lenguajes de programación funcional, que a menudo se usan como argumentos para funciones como mapa , buscar , filtrar , etc.



Considere la sintaxis para definir un concepto anónimo. En general, sigue la sintaxis de una definición de concepto común, excepto que en algunos casos puede omitir las listas de atributos y el nombre del concepto hijo. Si se utiliza un concepto anónimo como argumento para existir, entonces su nombre y la lista de atributos no son importantes, basta con comprobar que hay al menos algún resultado. Es posible que las funciones find y findOne no necesiten el nombre del concepto si la salida no se usa como un concepto completo, sino solo como un conjunto de atributos en una matriz asociativa. Si no se especifican atributos, el mecanismo de herencia se utiliza de forma predeterminada y los atributos se heredarán de los conceptos principales. Si no se especifica ningún nombre de concepto, se generará automáticamente.



Intentemos explicar lo que se escribió anteriormente usando algunos ejemplos. Con la función existe , puede verificar la deducibilidad o no deducibilidad de un concepto anidado:



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





En este ejemplo, el concepto anónimo:



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





es en realidad un concepto que hereda todos los atributos del concepto de tarea :



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





La función de búsqueda le permite devolver como una lista todos los valores de un concepto, que luego se pueden asociar con un atributo:



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





En este ejemplo, ampliamos el concepto de cliente con una lista de pedidos, que son objetos que contienen los atributos seleccionados del concepto de pedido . Hemos especificado una lista de atributos para un concepto anónimo, pero falta su nombre.



Las condiciones en la sección donde los conceptos anónimos pueden incluir otros atributos de los conceptos de matriz o sus filiales, en este caso c.id . Una característica de los conceptos anónimos es que todas esas variables y atributos externos deben estar necesariamente asociados con valores en el momento de iniciar la búsqueda de soluciones. Por tanto, los objetos del concepto anónimo sólo se pueden encontrar después de encontrar los objetos del concepto de cliente . ...



Conexiones externas



Las definiciones de concepto anónimas también se pueden utilizar en la sección from , donde representan conceptos principales. Además, en la definición de un concepto anónimo, es posible trasladar algunas de las condiciones que lo vinculan con otros conceptos, lo que tendrá un efecto especial. Estas condiciones se verificarán en la etapa de encontrar una solución al concepto anónimo y no afectarán el proceso de inferencia del concepto hijo. Aquí puede establecer una analogía entre las condiciones en la sección where del concepto anónimo y las condiciones en la sección JOIN ON del lenguaje SQL.



Por lo tanto, se pueden utilizar conceptos anónimos para implementar un análogo SQL de combinación externa izquierda. Esto requiere tres cosas.



  1. Primero, reemplace el concepto padre deseado por un concepto anónimo basado en él y transfiera todas las conexiones con otros conceptos padre.
  2. En segundo lugar, señalar que el fracaso de la inferencia de este concepto no debería conducir a un fracaso automático de la inferencia del concepto de niño completo. Para hacer esto, debe marcar este concepto principal con la palabra clave opcional .
  3. Y en tercer lugar, en la sección where del concepto hijo, puede comprobar si existen soluciones para este concepto anónimo.


Veamos un pequeño ejemplo:



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





Los atributos del concepto taskAssignedTo incluyen objetos de la tarea, su ejecutor y por separado el nombre del ejecutor. Los conceptos padre son tarea y usuario , y este último puede estar vacío si la tarea aún no tiene un ejecutor. Está envuelto en una definición de concepto anónima, precedida por la palabra clave opcional . El procedimiento de inferencia primero buscará objetos del concepto de tarea , luego, en base al usuario, creará un concepto anónimo, asociándolo con un valor específico del atributo asignado a del concepto de tarea . Palabra clave opcional le dice a la rutina de inferencia que si el concepto falla, su objeto será asociado con el valor especial UNDEFINED . Y comprobar el resultado de su salida en el nivel del concepto secundario permite que el atributo assigneeName establezca un valor predeterminado. Si no se especificó la palabra clave opcional , la falla en deducir un concepto anónimo resultaría en que la rama actual de la búsqueda del concepto hijo fallara. Esto sería análogo a la combinación interna de SQL.



Porque las condiciones en la cláusula where del concepto anónimo incluyen el atributo asignado a del otro concepto padre tarea , entonces la búsqueda de objetos del usuario del concepto sólo es posible después de vincular los objetos de la tarea con valores. No se pueden intercambiar:



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





Dado que en la etapa inicial se desconocerá el valor de t.assignedTo , no funcionará crear una definición de un concepto anónimo.



Si en SQL el orden de las tablas en el de la sección no importa, entonces en Prolog el orden de los predicados en una regla determina de forma única la secuencia de atravesar el árbol de decisión. Lo mismo puede decirse del componente de simulación, cuya regla de salida se basa en la resolución SLD utilizada en Prolog. En él, el resultado de la salida de objetos del concepto de la izquierda determina las restricciones para la salida de los objetos del concepto de la derecha. Debido a esto, desafortunadamente, no funcionará implementar las operaciones de combinación externa derecha y combinación externa completa de la misma manera natural. Dado que la cardinalidad del conjunto de resultados del concepto padre derecho puede ser mayor que la del concepto izquierdo, deberán generar en la dirección opuesta, del concepto derecho al izquierdo. Desafortunadamente, las peculiaridades del procedimiento de inferencia elegido imponen sus limitaciones a la funcionalidad del lenguaje. Pero la operación de combinación externa completa se puede emular uniendo la interna,uniones izquierda y derecha:



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





Este concepto generalizado se describe utilizando la lógica de orden superior descrita en el artículo anterior . Su definición se divide en tres partes. El primero encontrará intersecciones de conceptos, el segundo - objetos que tiene el concepto de la izquierda y el de la izquierda no, y el tercero - viceversa. Dado que los nombres de cada parte son los mismos, se combinarán los resultados de su inferencia.



Agregación



La agregación es una parte integral tanto del álgebra relacional como de la programación lógica. En SQL, la cláusula GROUP BY le permite agrupar filas que tienen el mismo valor de clave en filas de resumen. Le permite eliminar valores duplicados y se usa comúnmente con funciones agregadas como suma , recuento , mínimo , máximo , promedio.... Para cada grupo de filas, las funciones agregadas devuelven un valor ordinario basado en todas las filas de ese grupo. En la programación lógica, la agregación tiene una semántica más compleja. Esto se debe al hecho de que en algunos casos de definición recursiva de reglas SLD, la resolución entra en un bucle infinito y no se puede completar. Como en el caso de la negación como un fracaso, el problema de la recursividad en la operación de agregación se resuelve utilizando una semántica de modelo persistente o una semántica bien fundamentada. Intenté hablar brevemente sobre estos enfoques en el artículo anterior . Pero dado que la semántica del componente de modelado debe ser lo más simple posible, se prefiere la resolución SLD estándar. Y el problema de evitar la recursividad infinita se resuelve mejor reformando las conexiones entre conceptos.



La agregación podría implementarse naturalmente en un estilo funcional utilizando el componente informático de un lenguaje híbrido. Para hacer esto, es suficiente una función que colapsa los resultados de inferencia en grupos únicos y calcula funciones agregadas para cada uno de ellos. Pero dividir la definición de un concepto en partes lógicas y funcionales no será la solución más conveniente para una herramienta tan importante como la agregación. Es mejor expandir la sintaxis de la definición para incluir una sección de agrupación y funciones de agregación:



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





El grupo por sección , al igual que en SQL, contiene una lista de atributos mediante los cuales se realiza la agrupación. Las expresiones de relación también pueden incluir funciones de agregación. Las expresiones que contienen tales funciones se considerarán indefinidas hasta que se encuentren los valores de todos los conceptos principales y se realice la agrupación. Luego, sus valores pueden calcularse para cada grupo, asociarse con atributos y / o usarse para filtrar grupos. Con este enfoque perezoso para evaluar y verificar las condiciones, no es necesario que una sección HAVING separe las condiciones del filtro antes y después del agrupamiento. El tiempo de ejecución hará esto automáticamente.



Las principales funciones de agregación son recuento , suma, prom , min , max . El propósito de las funciones se puede entender por sus nombres. Dado que el componente de modelado puede trabajar naturalmente con tipos de datos compuestos, también puede agregar una función que devuelva valores agrupados como una lista. Llamémoslo grupo . Su argumento de entrada es una expresión. La función devuelve una lista de los resultados de evaluar esta expresión para cada elemento del grupo. Combinándolo con otras funciones, puede implementar cualquier función de agregación arbitraria. La función de grupo será más conveniente que funciones de SQL como group_concat o json_arrayagque se utilizan a menudo como un paso intermedio para obtener una matriz de valores de campo.



Ejemplo de agrupación:



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
      
      





El atributo pedidos contendrá una lista de todos los pedidos de los usuarios, ordersTotal : el total de todos los pedidos. La condición ordersTotal> 100 se comprobará después de que se realice la agrupación y se calcule la función de suma .



Concepto definido por función



La forma lógica declarativa de describir conceptos no siempre es conveniente. A veces será más conveniente establecer una secuencia de cálculos, cuyo resultado será la esencia del concepto. Esta situación a menudo surge cuando necesita cargar hechos de fuentes de datos externas, por ejemplo, de una base de datos, archivos, enviar solicitudes a servicios externos, etc. Será conveniente representar el concepto en forma de una función que traduzca la consulta de entrada en una consulta a la base de datos y devuelva el resultado de ejecutar esta consulta. A veces tiene sentido abandonar una conclusión lógica, reemplazándola con una implementación específica que tiene en cuenta las características específicas de un problema específico y lo resuelve de manera más eficiente. También es más conveniente describir en un estilo funcional secuencias infinitas que generan entidades conceptuales, por ejemplo, una secuencia de números enteros.



Los principios para trabajar con dichos conceptos deben ser los mismos que con el resto de los conceptos descritos anteriormente. La búsqueda de soluciones debe iniciarse con los mismos métodos. Ellos mismos pueden usarse como conceptos principales en definiciones de conceptos. Solo debe diferir la implementación interna de la búsqueda de una solución. Por lo tanto, presentaremos otra forma de definir un concepto usando una función:



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





Para definir un concepto definido mediante una función, debe especificar una lista de sus atributos y una función para generar objetos. Decidí hacer de la lista de atributos un elemento obligatorio de la definición, ya que esto simplificará el uso de dicho concepto; para comprender su estructura, no tendrá que estudiar la función de generar objetos.



Ahora hablemos de la función para generar objetos. Obviamente, debería recibir una solicitud como entrada: los valores iniciales de los atributos. Dado que estos valores se pueden especificar o no, por conveniencia se pueden colocar en una matriz asociativa, que será el argumento de entrada de la función. También será útil saber en qué modo se inicia la búsqueda de significados de conceptos: busque todos los valores posibles, busque solo el primero o solo verifique la existencia de una solución. Por lo tanto, agregamos el modo de búsqueda como segundo argumento de entrada.



El resultado de evaluar la función debería ser una lista de objetos conceptuales. Pero, dado que el procedimiento de inferencia basado en la búsqueda con retroceso consumirá estos valores uno a la vez, es posible hacer que el argumento de salida de la función no sea la lista en sí, sino un iterador de ella. Esto haría más flexible la definición del concepto, por ejemplo, permitiría, si fuera necesario, implementar una evaluación perezosa o una secuencia infinita de objetos. Puede utilizar un iterador de cualquier colección estándar o crear su propia implementación personalizada. El elemento de la colección debe ser una matriz asociativa con los valores de los atributos del concepto. Las esencias del concepto se crearán automáticamente sobre su base.

El uso de un iterador como tipo de retorno tiene sus inconvenientes. Es más engorroso y menos fácil de usar que simplemente devolver una lista de resultados. Encontrar la mejor opción que combine versatilidad, simplicidad y usabilidad es un desafío para el futuro.



Como ejemplo, considere el concepto que describe los intervalos de tiempo. Digamos que queremos dividir la jornada laboral en intervalos de 15 minutos. Podemos hacer esto con una función bastante 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 función devuelve un iterador para todos los valores posibles de un intervalo de tiempo de 15 minutos para un día laborable. Se puede utilizar, por ejemplo, para buscar espacios libres que aún no se han reservado:



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





La función no verifica el resultado de los cálculos para verificar el cumplimiento de la consulta de consulta , esto se hará automáticamente al convertir una matriz de atributos en una entidad. Pero si es necesario, los campos de consulta se pueden utilizar para optimizar la función. Por ejemplo, cree una consulta de base de datos basada en los campos de consulta de un concepto.



Un concepto a través de una función combina la semántica lógica y funcional. Si en el paradigma funcional la función calcula el resultado para los valores dados de los argumentos de entrada, entonces en el paradigma lógico no hay división en argumentos de entrada y salida. Solo se puede dar parte de los argumentos, y en cualquier combinación, y la función necesita encontrar los valores de los argumentos restantes. En la práctica, no siempre es posible implementar una función de este tipo capaz de realizar cálculos en cualquier dirección, por lo que tiene sentido limitar las posibles combinaciones de argumentos libres. Por ejemplo, declare que algunos argumentos deben estar vinculados a valores antes de evaluar una función. Para ello, marque dichos atributos en la definición del concepto con la palabra clave requerida .



Como ejemplo, considere el concepto que define los valores de una determinada escala exponencial.



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 función devuelve un iterador que genera entidades conceptuales mediante la evaluación diferida. El tamaño de la secuencia está limitado por el valor del atributo límite , pero si es cero, se vuelve infinito. Los conceptos basados ​​en secuencias infinitas deben usarse con mucho cuidado, ya que no garantizan que la rutina de inferencia se complete. El atributo límite es de naturaleza auxiliar y se utiliza para organizar cálculos. No podemos deducirlo de los valores de otros atributos, debe ser conocido antes de que comience el cálculo, por lo que se marcó como requerido



Hemos considerado una de las opciones sobre cómo podría verse un concepto como función. Pero las cuestiones de seguridad y usabilidad de tales conceptos requieren una investigación más detallada en el futuro.



Acoplamiento de colecciones anidadas



Algunos dialectos SQL que pueden trabajar con datos en forma de objeto admiten una operación como UNNEST , que convierte el contenido de una colección a un formato de tabla (conjunto de filas) y agrega la tabla resultante a la cláusula FROM . Suele utilizarse para aplanar objetos con estructuras anidadas, es decir, aplanarlos o aplanarlos. Ejemplos de estos lenguajes son BigQuery y SQL ++.



Digamos que almacenamos la información del usuario como un objeto 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"
}]
}, 
…]
      
      





Los objetos de usuario almacenan colecciones anidadas con listas de amigos y lugares de trabajo. Es posible extraer la información adjunta sobre los lugares de trabajo del usuario pegándola junto con los datos sobre el usuario tomados del nivel superior del objeto mediante una consulta 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;
      
      





El resultado será:



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





Esta operación se analiza con más detalle aquí .



A diferencia de SQL, en el componente de modelado, los datos incrustados deben convertirse no a formato tabular, sino a formato de objeto. Los conceptos discutidos anteriormente nos ayudarán con esto: un concepto definido a través de una función y un concepto anónimo. Un concepto a través de una función le permitirá transformar una colección anidada en formato de objeto, y un concepto anónimo le permitirá incrustar su definición en la lista de conceptos principales y acceder a los valores de sus atributos que contienen la colección anidada deseada.



Dado que la definición completa de un concepto a través de una función es demasiado engorrosa para usarla como un concepto anónimo:



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





necesitamos encontrar una manera de acortarlo. Primero, puede deshacerse del título de la definición de la función con los parámetros de consulta y modo . En la posición del concepto padre, el argumento de modo siempre será "encontrar todos los valores del concepto". El argumento de la consulta siempre estará vacío, ya que las dependencias de los atributos de otros conceptos se pueden incrustar en el cuerpo de la función. La palabra clave de concepto también se puede eliminar. Así obtenemos:



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





Si el nombre del concepto no es importante, entonces se puede omitir y se generará automáticamente:



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





Si en el futuro es posible crear un compilador que pueda deducir una lista de atributos del tipo de objetos devueltos por una función, entonces la lista de atributos puede descartarse:



{…}
      
      







Entonces, un ejemplo con los usuarios y sus lugares de trabajo como concepto se vería así:



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





La solución resultó ser un poco detallada, pero universal. Al mismo tiempo, si no se requiere ninguna transformación de los objetos de la colección anidada, entonces se puede simplificar significativamente:



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





recomendaciones



Este artículo se centró en dos cuestiones. Primero, la transferencia de algunas características del lenguaje SQL al componente de modelado: consultas anidadas, combinaciones externas, agregaciones y combinaciones con colecciones anidadas. En segundo lugar, la introducción de dos nuevas construcciones en el componente de modelado: definiciones anónimas de conceptos y conceptos definidos a través de una función.



Un concepto anónimo es una forma abreviada de una definición de concepto, destinado a ser utilizado como argumentos a funciones ( encontrar , findOne, y existe ) o como una definición concepto anidada en una cláusula where... Puede considerarse análogo a las definiciones de funciones anónimas en los lenguajes de programación funcional.



Un concepto definido a través de una función es un concepto, cuya forma de generar objetos se expresa mediante un algoritmo de forma explícita. Es una especie de "interfaz" entre los mundos de la programación funcional u orientada a objetos y la programación lógica. Será útil en muchos casos cuando una forma lógica de definir un concepto no sea conveniente o imposible: por ejemplo, cargar los hechos iniciales de sus bases de datos, archivos o de solicitudes a servicios remotos, para reemplazar la búsqueda lógica universal con su específico implementación optimizada, o para implementar cualquier regla arbitraria que cree objetos.



Curiosamente, los préstamos de SQL, como consultas anidadas, combinaciones externas y combinaciones de colecciones anidadas, no requirieron cambios importantes en la lógica del componente de modelado y se implementaron utilizando conceptos tales como conceptos anónimos y conceptos a través de una función. Esto sugiere que este tipo de conceptos son herramientas flexibles y versátiles con gran poder expresivo. Creo que hay muchas formas más interesantes de utilizarlos.



Entonces, en este y dos artículos anteriores, describí los conceptos y elementos básicos del componente de modelado de un lenguaje de programación híbrido. Pero, antes de pasar a los temas de la integración de un componente de modelado con un componente informático que implemente un estilo de programación funcional u orientado a objetos, decidí dedicar el siguiente artículo a sus posibles opciones para su aplicación. Desde mi punto de vista, el componente de modelado tiene ventajas sobre los lenguajes de consulta tradicionales, principalmente SQL, y se puede utilizar por sí solo sin una integración profunda con el componente de cálculo, lo que quiero demostrar con varios ejemplos en el próximo artículo.



El texto científico completo en inglés está disponible en: papers.ssrn.com/sol3/papers.cfm?abstract_id=3555711



Enlaces a publicaciones anteriores:



Diseño de un lenguaje de programación multiparadigma. Parte 1 - ¿Para qué sirve?

Diseñamos un lenguaje de programación multiparadigma. Parte 2 - Comparación de modelos de construcción en PL / SQL, LINQ y GraphQL Diseñamos

un lenguaje de programación multi-paradigma. Parte 3 - Descripción general de los lenguajes de representación del conocimiento

Diseñamos un lenguaje de programación multiparadigma. Parte 4 - Construcciones básicas del lenguaje de modelado Diseñamos

un lenguaje de programación multiparadigma. Parte 5 - Características de la programación lógica



All Articles