Beneficios de LINQ sobre el método funcional chaines

Hubo una discusión sobre Kotlin Slack sobre la posibilidad de agregar treees de códigos para soportar cosas como C # LINQ.

En C # LINQ tiene muchas aplicaciones, pero quiero centrarme en una sola (porque otras ya están cubiertas por la syntax de Kotlin): networkingactar consultas SQL en bases de datos remotas.

Prerrequisitos:

  • Tenemos un esquema de datos de una database SQL expresada de alguna manera en el código para que las herramientas estáticas (o el sistema de tipo) puedan verificar la corrección (al less la denominación) de una consulta SQL.

  • Tenemos que generar las consultas como cadenas

  • Queremos una syntax cercana a secuencias SQL o Java

Pregunta : ¿qué agregan los treees de expresión a la syntax que es tan crucial para la tarea en cuestión? ¿Qué tan bueno puede ser un generador de SQL sin ellos?

    ¿Qué tan buena puede ser una consulta dsl sin treees de expresiones?

    Como lo ha demostrado JINQ , puede llegar muy lejos al analizar el bytecode para comprender la intención del desarrollador y traducir el pnetworkingicado a SQL. Así que, en principio, los treees de expresión no son esenciales para build una consulta de gran apariencia dsl:

    val alices = database.customerStream().where { it.name == "Alice" } 

    Incluso sin hackers como el análisis de códigos de bytes, es posible get una consulta decente dsl con generación de código. Querydsl y JOOQ son excelentes ejemplos. Con un poco de código de envoltura de Kotlin, puede escribir

     val alices = db.findAll(QCustomer.customer, { it.name.eq("Alice") }) 

    ¿Cómo ayudan los treees de expresión a crear consultas dsl?

    El tree de expresiones es una estructura que representa algún código que se resuelve en un valor. Al tener dicha estructura generada por el comstackdor uno no necesita análisis de bytecode para entender qué se supone que debe hacer. Dado el ejemplo

     val alices = database.customerStream().where { it.name == "Alice" } 

    El argumento where function sería una expression que podemos inspeccionar en time de ejecución y traducirlo a SQL u otro lenguaje de consulta. Debido a que los treees de expresiones representan el código, no es necesario cambiar entre Kotlin y el paradigma SQL para escribir consultas. El código de consulta expresado usando linq / jinq se ve más o less igual independientemente de si se ejecutan en la memory usando POCO / POJO o en el motor de la database utilizando su lenguaje de consulta. El comstackdor también puede hacer más comprobación de types. Además, es muy fácil replace la database subyacente con una representación de memory para hacer que las testings se realicen mucho más rápido.

    Otras lecturas:

    • ¿Qué son los Árboles de Expresión y cómo los usa y por qué los usaría?
    • Uso práctico de treees de expresión

    JOOQ y Querydsl:

    La solución típica para ORM ha sido emplear una DSL o una DSL incorporada desde la lógica de la aplicación. Si bien se han logrado grandes avances con estos esquemas, que culminan en JOOQ y Querydsl, todavía existen muchas advertencias sobre dicho sistema:

    • muchos de los paradigmas a los que las personas que escriben estas consultas están acostumbrados (es decir, tipo de security) o faltan o son diferentes en forma key
    • la semántica exacta no es obvia: en la respuesta anterior, se sugiere que usemos un método de extensión eq para realizar un filter de igualdad de db-native. Es muy probable que un nuevo desarrollador use erróneamente equals lugar de eq .
    • este segundo punto se ve agravado por la dificultad de las testings: el uso de un conector activo con datos falsos es un problema complicadísimo, por lo que dependiendo del procedimiento de testing, el código incorrecto jooqDB.where { it.name.equals("alice") } puede no ser descubierto hasta mucho más abajo en la línea de desarrollo.

    Jinq

    Jinq no es un conector de datos de primera parte. Si bien creo que la importancia de esto es en gran medida psicosomática, es importante, no obstante. Muchos proyectos van a utilizar las herramientas sugeridas por los proveedores, y todos los principales proveedores de DB tienen conectores de Java, por lo que es probable que la mayoría de los desarrolladores simplemente los utilicen.

    Si bien no he usado Jinq, creo que otra razón por la que Jinq no ha sido ampliamente adoptado es en gran medida porque está intentando utilizar un dominio mucho más complejo para resolver el problema: crear consultas desde AST es mucho más fácil que generar consultas desde byte código por la misma razón que build el back-end de un comstackdor es más fácil que build un transcomstackdor. Aunque no puedo dejar de recomendar al equipo de Jinq por hacer un trabajo tan increíble, tampoco puedo evitar pensar que sus herramientas les dificultan: crear consultas fuera de bytecode es difícil. Por definición, bytecode java está comprometido a ejecutarse en la JVM, tratar de adaptar ese compromiso para otro intérprete es un problema muy difícil.

    Mi trabajo actual no me permite usar una database tradicional, pero si tuviera que cambiar de proyecto, sabiendo que necesitaría una gran cantidad de exposition de datos en mi DAL, probablemente me retiraría de Kotlin y Java de nuevo a .NET, en gran parte debido a Linq, en lugar de investigar Jinq. "Linq de Kotlin" bien podría cambiar mi opinión.

    Soporte de proveedores de DB:

    Los conectores de database LINQ-to-SQL y LINQ-a-mongo han sido ampliamente adoptados en la comunidad .net. Esto se debe a que son de primera class, de alta calidad y actúan de una manera razonablemente sencilla: la compilation de un AST a SQL (o el lenguaje mongo-query) es, al less, conceptualmente sencillo. Muchas de las advertencias tradicionales de ORM se aplican, pero los proveedores (Microsoft y Mongo) continúan abordando estos problemas.

    Si Kotlin soportaba treees de códigos de time de ejecución en una línea similar a Linq, y si Kotlin continúa ganando tracción a su velocidad actual, entonces creo que los equipos MongoDB y Hibernate comenzarían rápidamente a adaptar sus conectores existentes LINQ-to-X para soportar Los clientes de Kotlin, y eventualmente incluso las empresas más grandes y lentas como Microsoft e IBM, comenzarían a respaldar el mismo flujo.

    Linq de Kotlin

    Además, las funciones exactas que los conceptos únicos de Kotlin de un "tipo de receptor" y la implementación agresiva de inline podrían jugar en el espacio de Linq son interesantes. Linq-from-Kotlin bien podría ser más efectivo que LINQ-from-C #.

    donde C # tiene

     someInterface .where(customer -> customer.Name.StartsWith("A") && ComplexFunctionCallDoneByMongoDriver(customer)) .select(customer -> new ResultObject(customer.Name, customer.Age, OtherContext())) 

    Kotlin podría hacer avances:

     someInterface .filter { it.name startsWith "A" && inlinedComplexFunctionCallDoneOnDB(it) } //inlined methods would have their AST exposed -> can be run on the DB process instead of on the driver. .map { ResultObject(name, age, otherContext()) } //uses a reciever type, no need to specify "customer ->" //otherContext() might also be inlined 

    Esto está fuera de mi cabeza, sospecho que cerebros más inteligentes que los míos pueden usar mejor estas herramientas.

    Otros usos:

    Vale la pena mencionar que la suposition sobre las aplicaciones de runtime-code-AST es falsa:

    otros [dominios problemáticos AST en time de ejecución] [ya] están supuestamente cubiertos por la syntax de Kotlin

    La razón por la que mencioné esto fue porque estaba molesto con la característica de security nula de Kotlin y su interacción con Mockito : después de haber pasado un time investigando el tema, no hay un marco de Mocking diseñado para Kotlin, solo frameworks de Java que pueden ser usado de Kotlin, con algo de dolor.

    Algunos problemas actualmente no resueltos en el dominio de Java y el dominio de Kotlin:

    • Marcos burlones, como arriba. Con acceso al AST, todos los trucos ingeniosos pero extraños en torno al argumento-operación-order empleados por Mockito se vuelven obsoletos. Otros frameworks burlones más tradicionales ganan un front-end mucho más intuitivo y seguro.
    • expresiones bindings, para frameworks de interfaz de usuario o de otra manera, a menudo se vuelven a cadenas. Considere un marco de interfaz de usuario donde los desarrolladores podrían escribir notifyOfUIElementChange { this.model.displayName } lugar de notifyOfUIElementChange("model.displayName") . Este último sufre del problema paralizante de ser obsoleto si alguien cambia el nombre de la propiedad.
      • Estoy muy emocionado de ver lo que los chicos de ControlsFX o Thomas Mikula podrían hacer con una característica como esta.
    • similar a Linq específico de Kotlin: sospecho que las aplicaciones de Kotlin aquí podrían proporcionar una cantidad de herramientas que no conozco. Pero estoy muy seguro de que existen.

    Realmente me gusta Linq, y no puedo evitar pensar que con el enfoque de Kotlin en los problemas de la industria, un module de Linq-from-Kotlin encajaría perfectamente y haría un poco más fácil la vida de varias personas, incluida la mía.