Sigo escuchando este término en varios contextos diferentes. ¿Qué es?
Sigo escuchando este término en varios contextos diferentes. ¿Qué es?
Respuestas:
La programación declarativa es cuando escribes tu código de tal manera que describe lo que quieres hacer, y no cómo quieres hacerlo. Le corresponde al compilador averiguar el cómo.
Ejemplos de lenguajes de programación declarativos son SQL y Prolog.
Las otras respuestas ya hacen un trabajo fantástico al explicar qué es la programación declarativa, así que solo voy a proporcionar algunos ejemplos de por qué eso podría ser útil.
Los programas declarativos son independientes del contexto . Debido a que solo declaran cuál es el objetivo final, pero no los pasos intermedios para alcanzar ese objetivo, se puede usar el mismo programa en diferentes contextos. Esto es difícil de hacer con los programas imperativos , porque a menudo dependen del contexto (por ejemplo, estado oculto).
Toma yacc
como ejemplo. Es un generador de analizador alias. compilador compilador, un DSL declarativo externo para describir la gramática de un idioma, de modo que se pueda generar automáticamente un analizador para ese idioma a partir de la descripción. Debido a su independencia de contexto, puede hacer muchas cosas diferentes con dicha gramática:
yacc
)Y muchos más …
Debido a que no prescribe a la computadora qué pasos tomar y en qué orden, puede reorganizar su programa mucho más libremente, incluso puede ejecutar algunas tareas en paralelo. Un buen ejemplo es un planificador de consultas y un optimizador de consultas para una base de datos SQL. La mayoría de las bases de datos SQL le permiten mostrar la consulta que realmente están ejecutando frente a la consulta que les pidió que ejecutaran. A menudo, esas consultas no parecen nadacomo el uno al otro. El planificador de consultas tiene en cuenta cosas que ni siquiera hubiera soñado: la latencia rotacional del disco, por ejemplo, o el hecho de que una aplicación completamente diferente para un usuario completamente diferente acaba de ejecutar una consulta similar y la tabla que usted es unirse y trabajar tan duro para evitar cargar ya está en la memoria de todos modos.
No es una disyuntiva interesante aquí: la máquina tiene que trabajar más para averiguar cómo hacer algo de lo que sería en un lenguaje imperativo, pero cuando se hace entenderlo, tiene mucha más libertad y mucha más información para la optimización etapa.
Flojamente:
La programación declarativa tiende a: -
La programación imperativa tiende a: -
Como resultado, un estilo imperativo ayuda al lector a comprender la mecánica de lo que realmente está haciendo el sistema, pero puede dar poca información sobre el problema que se pretende resolver. Por otro lado, un estilo declarativo ayuda al lector a comprender el dominio del problema y el enfoque que el sistema adopta para la solución del problema, pero es menos informativo sobre el tema de la mecánica.
Los programas reales (incluso los escritos en lenguajes que favorecen los extremos del espectro, como ProLog o C) tienden a tener ambos estilos presentes en varios grados en varios puntos, para satisfacer las diversas complejidades y necesidades de comunicación de la pieza. Un estilo no es superior al otro; solo sirven para diferentes propósitos y, como con muchas cosas en la vida, la moderación es la clave.
Aquí hay un ejemplo.
En CSS (usado para diseñar páginas HTML), si desea que un elemento de imagen tenga 100 píxeles de alto y 100 píxeles de ancho, simplemente "declara" que eso es lo que quiere de la siguiente manera:
#myImageId {
height: 100px;
width: 100px;
}
Puede considerar CSS un lenguaje declarativo de "hoja de estilo".
El motor del navegador que lee e interpreta este CSS es libre de hacer que la imagen parezca tan alta y ancha como quiera. Diferentes motores de navegador (por ejemplo, el motor para IE, el motor para Chrome) implementarán esta tarea de manera diferente.
Sus implementaciones únicas, por supuesto, NO están escritas en un lenguaje declarativo, sino en un lenguaje de procedimiento como Assembly, C, C ++, Java, JavaScript o Python. Ese código es un conjunto de pasos que se llevarán a cabo paso a paso (y puede incluir llamadas a funciones). Podría hacer cosas como interpolar valores de píxeles y renderizar en la pantalla.
Lo siento, pero debo estar en desacuerdo con muchas de las otras respuestas. Me gustaría detener este malentendido confuso de la definición de programación declarativa.
Definición
La transparencia referencial (RT) de las subexpresiones es el único atributo requerido de una expresión de programación declarativa , porque es el único atributo que no se comparte con la programación imperativa.
Otros atributos citados de programación declarativa, derivan de este RT. Haga clic en el hipervínculo de arriba para obtener una explicación detallada.
Ejemplo de hoja de cálculo
Dos respuestas mencionaron la programación de hojas de cálculo. En los casos en que la programación de la hoja de cálculo (también conocida como fórmulas) no accede al estado global mutable , entonces es la programación declarativa. Esto se debe a que los valores de celda mutables son la entrada y salida monolítica del main()
(todo el programa). Los nuevos valores no se escriben en las celdas después de ejecutar cada fórmula, por lo que no son mutables durante la vida del programa declarativo (ejecución de todas las fórmulas en la hoja de cálculo). Por lo tanto, uno con respecto al otro, las fórmulas ven estas células mutables como inmutables. Se permite que una función RT acceda al estado global inmutable (y también al estado local mutable ).
Por lo tanto, la capacidad de mutar los valores en las celdas cuando el programa termina (como resultado de main()
) no los convierte en valores almacenados mutables en el contexto de las reglas. La distinción clave es que los valores de las celdas no se actualizan después de realizar cada fórmula de hoja de cálculo, por lo tanto, el orden de ejecución de las fórmulas no importa. Los valores de las celdas se actualizan después de que se hayan realizado todas las fórmulas declarativas.
La programación declarativa es la imagen, donde la programación imperativa son instrucciones para pintar esa imagen.
Estás escribiendo en un estilo declarativo si estás "diciéndole lo que es", en lugar de describir los pasos que la computadora debe seguir para llegar a donde quieres.
Cuando usas XML para marcar datos, estás usando programación declarativa porque estás diciendo "Esta es una persona, es un cumpleaños y allí hay una dirección".
Algunos ejemplos de donde la programación declarativa e imperativa se combinan para un mayor efecto:
Windows Presentation Foundation utiliza la sintaxis XML declarativa para describir el aspecto de una interfaz de usuario y las relaciones (enlaces) entre los controles y las estructuras de datos subyacentes.
Los archivos de configuración estructurados usan sintaxis declarativa (tan simple como pares "clave = valor") para identificar lo que significa una cadena o valor de datos.
HTML marca el texto con etiquetas que describen qué papel tiene cada parte del texto en relación con todo el documento.
La programación declarativa es la programación con declaraciones, es decir, oraciones declarativas. Las oraciones declarativas tienen una serie de propiedades que las distinguen de las oraciones imperativas. En particular, las declaraciones son:
Un punto relevante es que estas son todas propiedades estructurales y son ortogonales al tema. Declarativo no se trata de "Qué vs. Cómo" . Podemos declarar (representar y restringir) un "cómo" tan fácilmente como declaramos un "qué" .Declarativo se trata de estructura, no de contenido. La programación declarativa tiene un impacto significativo en cómo abstraemos y refactorizamos nuestro código, y cómo lo modularizamos en subprogramas, pero no tanto en el modelo de dominio.
A menudo, podemos convertir de imperativo a declarativo agregando contexto. Por ejemplo, "Gire a la izquierda. (... espere ...) Gire a la derecha". a "Bob girará a la izquierda en la intersección de Foo y Bar a las 11:01. Bob girará a la derecha en la intersección de Bar y Baz a las 11:06". Tenga en cuenta que en el último caso las oraciones son idempotentes y conmutativas, mientras que en el primer caso reorganizar o repetir las oraciones cambiaría severamente el significado del programa.
Con respecto a lo monótono , las declaraciones pueden agregar restricciones que restan posibilidades . Pero las restricciones aún agregan información (más precisamente, las restricciones son información). Si necesitamos declaraciones que varíen en el tiempo, es típico modelar esto con una semántica temporal explícita, por ejemplo, desde "la pelota es plana" hasta "la pelota es plana en el momento T". Si tenemos dos declaraciones contradictorias, tenemos un sistema declarativo inconsistente, aunque esto podría resolverse introduciendo restricciones blandas (prioridades, probabilidades, etc.) o aprovechando una lógica paraconsistente.
imagina una página de Excel. Con columnas pobladas con fórmulas para calcular su declaración de impuestos.
Toda la lógica se hace declarada en las celdas, el orden del cálculo se determina mediante la fórmula misma en lugar de por procedimiento.
De eso se trata la programación declarativa. Usted declara el espacio del problema y la solución en lugar del flujo del programa.
Prolog es el único lenguaje declarativo que uso. Requiere un tipo diferente de pensamiento, pero es bueno aprender si solo para exponerlo a algo que no sea el típico lenguaje de programación procesal.
He refinado mi comprensión de la programación declarativa, desde diciembre de 2011, cuando proporcioné una respuesta a esta pregunta. Aquí sigue mi comprensión actual.
La versión larga de mi comprensión (investigación) se detalla en este enlace. , que debe leer para obtener una comprensión profunda del resumen que proporcionaré a continuación.
La programación imperativa es donde se almacena y lee el estado mutable, por lo que el orden y / o la duplicación de las instrucciones del programa pueden alterar el comportamiento (semántica) del programa (e incluso causar un error, es decir, un comportamiento involuntario).
En el sentido más ingenuo y extremo (que afirmé en mi respuesta anterior), la programación declarativa (DP) está evitando todo el estado mutable almacenado, por lo tanto, ordenar y / o duplicar las instrucciones del programa NO puede alterar el comportamiento (semántica) del programa .
Sin embargo, una definición tan extrema no sería muy útil en el mundo real, ya que casi todos los programas implican un estado mutable almacenado. El ejemplo de hoja de cálculo se ajusta a esta definición extrema de DP, porque todo el código del programa se ejecuta hasta su finalización con una copia estática del estado de entrada, antes de que se almacenen los nuevos estados. Luego, si se cambia algún estado, esto se repite. Pero la mayoría de los programas del mundo real no pueden limitarse a un modelo tan monolítico de cambios de estado.
Una definición más útil de DP es que el orden y / o la duplicación de las instrucciones de programación no alteran ninguna semántica opaca. En otras palabras, no se producen cambios aleatorios ocultos en la semántica; cualquier cambio en el orden y / o duplicación de las instrucciones del programa solo causa cambios intencionados y transparentes en el comportamiento del programa.
El siguiente paso sería hablar sobre qué modelos de programación o paradigmas ayudan en DP, pero esa no es la cuestión aquí.
Functional programming
es una palabra de moda en estos días que es esencialmente un subconjunto de programación declarativa. LINQ en lenguaje C # es un elemento de programación funcional cuando el lenguaje en sí es imprescindible por naturaleza. Entonces, C # se convierte en una especie de híbrido según esa definición.
Es un método de programación basado en describir lo que algo debe hacer o ser en lugar de describir cómo debería funcionar.
En otras palabras, no escribes algoritmos hechos de expresiones, solo diseñas cómo quieres que sean las cosas. Dos buenos ejemplos son HTML y WPF.
Este artículo de Wikipedia es una buena descripción general: http://en.wikipedia.org/wiki/Declarative_programming
Desde que escribí mi respuesta anterior, he formulado una nueva definición de la propiedad declarativa que se cita a continuación. También he definido la programación imperativa como la propiedad dual.
Esta definición es superior a la que proporcioné en mi respuesta anterior, porque es sucinta y es más general. Pero puede ser más difícil de asimilar, porque la implicación de los teoremas de incompletitud aplicables a la programación y la vida en general son difíciles de entender para los humanos.
La explicación citada de la definición discute el papel que juega la programación funcional pura en la programación declarativa.
Declarativo versus imperativo
La propiedad declarativa es extraña, obtusa y difícil de capturar en una definición técnicamente precisa que sigue siendo general y no ambigua, porque es una noción ingenua de que podemos declarar el significado (también conocido como semántica) del programa sin incurrir en efectos secundarios no deseados. Existe una tensión inherente entre la expresión del significado y la evitación de efectos no deseados, y esta tensión en realidad deriva de los teoremas de incompletitud de la programación y nuestro universo.
Es una simplificación excesiva, técnicamente imprecisa y, a menudo, ambigua definir declarativo como " qué hacer " e imperativo como " cómo hacerlo " . Un caso ambiguo es el " qué " es el " cómo " en un programa que genera un programa: un compilador.
Evidentemente, la recursión ilimitada que hace que un lenguaje Turing sea completo , también está análogamente en la semántica, no solo en la estructura sintáctica de la evaluación (también conocida como semántica operativa). Esto es lógicamente un ejemplo análogo al teorema de Gödel: " cualquier sistema completo de axiomas también es inconsistente ". ¡Reflexione sobre la rareza contradictoria de esa cita! También es un ejemplo que demuestra cómo la expresión de la semántica no tiene demostrable obligado, por tanto, no podemos probar 2 que un programa (y análogamente su semántica) detuvo también conocido como el teorema de Detención.
Los teoremas de incompletitud derivan de la naturaleza fundamental de nuestro universo, que como se establece en la Segunda Ley de la Termodinámica es " la entropía (también conocida como el # de posibilidades independientes) está tendiendo al máximo para siempre ". La codificación y el diseño de un programa nunca terminan, ¡está vivo! Porque intenta abordar una necesidad del mundo real, y la semántica del mundo real siempre está cambiando y tiende a más posibilidades. Los humanos nunca dejan de descubrir cosas nuevas (incluidos errores en los programas ;-).
Para capturar de manera precisa y técnica esta noción deseada antes mencionada dentro de este universo extraño que no tiene borde (¡piense que no hay "fuera" de nuestro universo), requiere una definición concisa pero engañosamente no simple que sonará incorrecta hasta que se explique profundamente.
Definición:
La propiedad declarativa es donde puede existir un solo conjunto posible de declaraciones que pueden expresar cada semántica modular específica.
La propiedad imperativa 3 es la dual, donde la semántica es inconsistente en la composición y / o puede expresarse con variaciones de conjuntos de enunciados.
Esta definición de declarativa es distintivamente local en el ámbito semántico, lo que significa que requiere que una semántica modular mantenga su significado coherente independientemente de dónde y cómo se instancia y emplea en el ámbito global . Por lo tanto, cada semántica modular declarativa debería ser intrínsecamente ortogonal a todas las demás posibles, y no un algoritmo o modelo global imposible (debido a teoremas de incompletitud) para presenciar la coherencia, que también es el punto de " Más no siempre es mejor " por Robert Harper, profesor de Informática en la Universidad Carnegie Mellon, uno de los diseñadores de Standard ML.
Ejemplos de estos incluyen semántica declarativa modulares categoría funtores teoría, por ejemplo, la
Applicative
, mecanografía nominal, espacios de nombres, los campos de nombre y WRT a nivel operativo de la semántica a continuación, la programación funcional pura.Por lo tanto, los lenguajes declarativos bien diseñados pueden expresar más claramente el significado , aunque con cierta pérdida de generalidad en lo que se puede expresar, pero una ganancia en lo que se puede expresar con consistencia intrínseca.
Un ejemplo de la definición antes mencionada es el conjunto de fórmulas en las celdas de un programa de hoja de cálculo, que no se espera que den el mismo significado cuando se mueven a diferentes celdas de columna y fila, es decir, los identificadores de celda cambiaron. Los identificadores de celda son parte y no son superfluos para el significado pretendido. Por lo tanto, el resultado de cada hoja de cálculo es wrt único para los identificadores de celda en un conjunto de fórmulas. La semántica modular consistente en este caso es el uso de identificadores de celda como entrada y salida de funciones puras para fórmulas de celdas (ver más abajo).
Hyper Text Markup Language, también conocido como HTML, el lenguaje para páginas web estáticas, es un ejemplo de un lenguaje declarativo altamente (pero no perfectamente 3 ) que (al menos antes de HTML 5) no tenía capacidad para expresar un comportamiento dinámico. HTML es quizás el lenguaje más fácil de aprender. Para el comportamiento dinámico, un lenguaje de script imperativo como JavaScript generalmente se combinaba con HTML. HTML sin JavaScript se ajusta a la definición declarativa porque cada tipo nominal (es decir, las etiquetas) mantiene su significado consistente en la composición dentro de las reglas de la sintaxis.
Una definición competitiva para declarativa son las propiedades conmutativas e idempotentes de las declaraciones semánticas, es decir, que las declaraciones pueden reordenarse y duplicarse sin cambiar el significado. Por ejemplo, las declaraciones que asignan valores a campos con nombre pueden reordenarse y duplicarse sin cambiar el significado del programa, si esos nombres son modulares en cualquier orden implícito. Los nombres a veces implican un orden, por ejemplo, los identificadores de celda incluyen su posición de columna y fila; mover un total en la hoja de cálculo cambia su significado. De lo contrario, estas propiedades requieren implícitamente globalConsistencia de la semántica. En general, es imposible diseñar la semántica de las declaraciones para que permanezcan consistentes si se ordenan o duplican al azar, porque el orden y la duplicación son intrínsecos a la semántica. Por ejemplo, las declaraciones "Foo existe" (o construcción) y "Foo no existe" (y destrucción). Si uno considera que la inconsistencia aleatoria es endémica de la semántica prevista, entonces acepta esta definición como lo suficientemente general para la propiedad declarativa. En esencia, esta definición es vacía como una definición generalizada porque intenta hacer que la consistencia sea ortogonal a la semántica, es decir, desafiar el hecho de que el universo de la semántica es dinámicamente ilimitado y no puede ser capturado en un paradigma de coherencia global .
Requerir las propiedades conmutativas e idempotentes para la (orden de evaluación estructural de) la semántica operacional de nivel inferior convierte la semántica operacional en una semántica modular declarativa localizada , por ejemplo, programación funcional pura (incluyendo recursión en lugar de bucles imperativos). Luego, el orden operativo de los detalles de implementación no impacta (es decir, se extiende globalmente en) la consistencia de la semántica de nivel superior. Por ejemplo, el orden de evaluación de (y teóricamente también la duplicación de) las fórmulas de la hoja de cálculo no importa porque las salidas no se copian a las entradas hasta después de que todas las salidas se hayan calculado, es decir, de forma análoga a las funciones puras.
C, Java, C ++, C #, PHP y JavaScript no son particularmente declarativos. La sintaxis de Copute y la sintaxis de Python se asocian más declarativamente a los resultados esperados , es decir, una semántica sintáctica consistente que elimina lo extraño para que uno pueda comprender fácilmente el código después de haberlo olvidado. Copute y Haskell imponen el determinismo de la semántica operativa y alientan " no te repitas " (DRY), porque solo permiten el paradigma funcional puro.
2 Incluso cuando podemos probar la semántica de un programa, por ejemplo, con el lenguaje Coq, esto se limita a la semántica que se expresa en la tipificación , y la tipificación nunca puede capturar toda la semántica de un programa, ni siquiera para los idiomas que son No está completo, por ejemplo, con HTML + CSS es posible expresar combinaciones inconsistentes que tienen una semántica indefinida.
3 Muchas explicaciones afirman incorrectamente que solo la programación imperativa tiene sentencias ordenadas sintácticamente. Aclaré esta confusión entre la programación imperativa y funcional . Por ejemplo, el orden de las declaraciones HTML no reduce la coherencia de su significado.
Editar: publiqué el siguiente comentario en el blog de Robert Harper:
en programación funcional ... el rango de variación de una variable es un tipo
Dependiendo de cómo se distinga la programación funcional de la imperativa, su 'asignable' en un programa imperativo también puede tener un tipo que pone un límite a su variabilidad.
La única definición no confusa que aprecio actualmente para la programación funcional es a) funciones como objetos y tipos de primera clase, b) preferencia por la recursión sobre bucles, y / o c) funciones puras, es decir, aquellas funciones que no afectan la semántica deseada del programa cuando se memoriza (por lo tanto, la programación funcional perfectamente pura no existe en una semántica denotativa de propósito general debido a los impactos de la semántica operativa, por ejemplo, la asignación de memoria ).
La propiedad idempotente de una función pura significa que la llamada a la función en sus variables puede ser sustituida por su valor, que generalmente no es el caso para los argumentos de un procedimiento imperativo. Las funciones puras parecen ser wrt declarativas para las transiciones de estado no compuestas entre los tipos de entrada y resultado.
Pero la composición de las funciones puras no mantiene tal consistencia, porque es posible modelar un proceso imperativo de efectos secundarios (estado global) en un lenguaje de programación funcional puro, por ejemplo, IOMonad de Haskell y, además, es completamente imposible evitar hacerlo. cualquier lenguaje de programación funcional puro completo de Turing.
Como escribí en 2012, que parece un consenso similar de comentarios en su blog reciente , esa programación declarativa es un intento de capturar la noción de que la semántica prevista nunca es opaca. Ejemplos de semántica opaca son la dependencia del orden, la dependencia del borrado de la semántica de nivel superior en la capa de semántica operativa (por ejemplo, los modelos no son conversiones y los genéricos reificados limitan la semántica de nivel superior ) y la dependencia de valores variables que no pueden verificarse (demostrarse correcto) por el lenguaje de programación.
Por lo tanto, he concluido que solo los idiomas completos que no son de Turing pueden ser declarativos.
Por lo tanto, un atributo inequívoco y distinto de un lenguaje declarativo podría ser que se puede demostrar que su salida obedece a un conjunto enumerable de reglas generativas. Por ejemplo, para cualquier programa HTML específico (ignorando las diferencias en las formas en que los intérpretes divergen) que no está programado (es decir, no está completo en Turing), entonces su variabilidad de salida puede ser enumerable. O más sucintamente, un programa HTML es una función pura de su variabilidad. Lo mismo ocurre con un programa de hoja de cálculo es una función pura de sus variables de entrada.
Por lo tanto, me parece que los lenguajes declarativos son la antítesis de la recursión ilimitada , es decir, según el segundo teorema de incompletitud de Gödel, los teoremas autorreferenciales no se pueden probar.
Lesie Lamport escribió un cuento de hadas sobre cómo Euclides podría haber trabajado en torno a los teoremas de incompletitud de Gödel aplicados a las pruebas matemáticas en el contexto del lenguaje de programación por congruencia entre tipos y lógica (correspondencia de Curry-Howard, etc.).
La programación declarativa es "el acto de programar en lenguajes que se ajustan al modelo mental del desarrollador en lugar del modelo operativo de la máquina".
La diferencia entre programación declarativa e imperativa está bien ilustrada por el problema de analizar datos estructurados.
Un programa imperativo usaría funciones recursivas para consumir datos y generar datos. Un programa declarativo expresaría una gramática que define la estructura de los datos para que luego puedan analizarse.
La diferencia entre estos dos enfoques es que el programa declarativo crea un nuevo lenguaje que está más relacionado con el modelo mental del problema que su lenguaje anfitrión.
Lo explicaría como DP es una forma de expresar
... y donde hay un motor de deducciones que generalmente funciona con un algoritmo de unificación para encontrar los objetivos.
Por lo que puedo decir, comenzó a usarse para describir sistemas de programación como Prolog, porque prolog se trata (supuestamente) de declarar cosas de una manera abstracta.
Cada vez significa menos, ya que tiene la definición dada por los usuarios anteriores. Debe quedar claro que existe un abismo entre la programación declarativa de Haskell y la programación declarativa de HTML.
Un par de otros ejemplos de programación declarativa:
La programación declarativa es buena porque puede ayudar a simplificar su modelo mental * de código, y porque eventualmente podría ser más escalable.
Por ejemplo, supongamos que tiene una función que hace algo a cada elemento en una matriz o lista. El código tradicional se vería así:
foreach (object item in MyList)
{
DoSomething(item);
}
No es gran cosa allí. Pero, ¿qué sucede si usa la sintaxis más declarativa y en su lugar define DoSomething () como una Acción? Entonces puedes decirlo de esta manera:
MyList.ForEach(DoSometing);
Esto es, por supuesto, más conciso. Pero estoy seguro de que tiene más preocupaciones que solo guardar dos líneas de código aquí y allá. Rendimiento, por ejemplo. A la antigua usanza, el procesamiento debía hacerse en secuencia. ¿Qué pasaría si el método .ForEach () tuviera una forma de indicar que podría manejar el procesamiento en paralelo, automáticamente? Ahora, de repente, ha hecho su código multiproceso de una manera muy segura y solo ha cambiado una línea de código. Y, de hecho, hay una extensión para .Net que te permite hacer exactamente eso.
Depende de cómo envíe la respuesta al texto. En general, puede mirar el programa desde una determinada vista, pero depende del ángulo en que mire el problema. Comenzaré con el programa: Dim Bus, Auto, Time, Height As Integr
Nuevamente, depende de cuál sea el problema en general. Puede que tenga que acortarlo debido al programa. Espero que esto ayude y necesito los comentarios si no es así. Gracias.