Programación funcional, declarativa e imperativa [cerrado]


466

¿Qué significan los términos programación funcional, declarativa e imperativa?


3
Hay algunas respuestas geniales aquí. Una cosa interesante que no se ha aclarado por completo es que la declaración y el imperativo son complementarios y simbióticos, más que simplemente estilos diferentes o qué versus cómo .
Kit

1
@ Kit Imo, algunas de las respuestas en esta página están combinando los términos. DP == transparencia referencial (RT). DP e IP son opuestos, por lo tanto, los elementos no son complementos de un todo, es decir, un programa completo se puede escribir en cualquier estilo. La llamada a una función puede ser DP (RT) o IP, su implementación puede ser o una combinación. No son simbióticos en el sentido de que una llamada a una función IP en una función DP de otro modo puede hacer que la llamada de la función DP sea IP. Son simbióticos en el sentido de que los programas del mundo real (por ejemplo, reactivos funcionales) pueden emplear una combinación, por ejemplo, llamadas de nivel superior IP en funciones DP.
Shelby Moore III

debería agregarse a la wiki o un enlace en algo similar a la wiki, etc. Aquí hay un excelente enlace en wikipedia en.wikipedia.org/wiki/Comparison_of_programming_paradigms
Joe


1
Esta pregunta se está discutiendo en Meta: meta.stackoverflow.com/q/342784/2751851
duplode

Respuestas:


262

Al momento de escribir esto, las respuestas más votadas en esta página son imprecisas y confusas en la definición declarativa frente a la imperativa, incluida la respuesta que cita a Wikipedia. Algunas respuestas combinan los términos de diferentes maneras.

Consulte también mi explicación de por qué la programación de hojas de cálculo es declarativa, independientemente de que las fórmulas muten las celdas.

Además, varias respuestas afirman que la programación funcional debe ser un subconjunto de declarativa. De ese punto depende si diferenciamos "función" de "procedimiento". Vamos a manejar imperativo vs. declarativo primero.

Definición de expresión declarativa

El único atributo que posiblemente puede diferenciar una expresión declarativa de una expresión imperativa es la transparencia referencial (RT) de sus subexpresiones. Todos los demás atributos se comparten entre ambos tipos de expresiones o se derivan del RT.

Un lenguaje 100% declarativo (es decir, uno en el que cada expresión posible es RT) no permite (entre otros requisitos de RT) la mutación de los valores almacenados, por ejemplo, HTML y la mayoría de Haskell.

Definición de expresión RT

RT a menudo se conoce como "sin efectos secundarios". El término efectos no tiene una definición precisa, por lo que algunas personas no están de acuerdo en que "sin efectos secundarios" es lo mismo que RT. RT tiene una definición precisa .

Dado que cada subexpresión es conceptualmente una llamada a función, RT requiere que la implementación de una función (es decir, la (s) expresión (s) dentro de la función llamada) no pueda acceder al estado mutable que es externo a la función (acceder al estado local mutable es permitido). En pocas palabras, la función (implementación) debe ser pura .

Definición de función pura

A menudo se dice que una función pura no tiene "efectos secundarios". El término efectos no tiene una definición precisa, por lo que algunas personas no están de acuerdo.

Las funciones puras tienen los siguientes atributos.

  • La única salida observable es el valor de retorno.
  • La única dependencia de salida son los argumentos.
  • los argumentos están completamente determinados antes de que se genere cualquier salida.

Recuerde que RT se aplica a expresiones (que incluye llamadas a funciones) y la pureza se aplica a (implementaciones de) funciones.

Un oscuro ejemplo de funciones impuras que hacen expresiones RT es concurrencia, pero esto se debe a que la pureza se rompe en la capa de abstracción de interrupción. Realmente no necesitas saber esto. Para hacer expresiones RT, se llaman funciones puras.

Atributos derivados de RT

Cualquier otro atributo citado para la programación declarativa, por ejemplo, la cita de 1999 utilizada por Wikipedia, deriva de RT o se comparte con la programación imperativa. De este modo, pruebo que mi definición precisa es correcta.

Tenga en cuenta que la inmutabilidad de los valores externos es un subconjunto de los requisitos para RT.

  • Los lenguajes declarativos no tienen estructuras de control de bucle, por ejemplo, fory while, debido a la inmutabilidad , la condición del bucle nunca cambiaría.

  • Los lenguajes declarativos no expresan el flujo de control que no sea el orden de función anidado (también conocido como dependencias lógicas), porque debido a la inmutabilidad , otras opciones de orden de evaluación no cambian el resultado (ver más abajo).

  • Los lenguajes declarativos expresan "pasos" lógicos (es decir, el orden de llamada de función RT anidada), pero si cada llamada de función es una semántica de nivel superior (es decir, "qué hacer") no es un requisito de programación declarativa. La distinción del imperativo es que debido a la inmutabilidad (es decir, más generalmente RT), estos "pasos" no pueden depender del estado mutable, sino solo del orden relacional de la lógica expresada (es decir, el orden de anidamiento de las llamadas de función, también conocido como sub-expresiones )

    Por ejemplo, el párrafo HTML <p>no se puede mostrar hasta que se hayan evaluado las subexpresiones (es decir, las etiquetas) en el párrafo. No hay un estado mutable, solo una dependencia de orden debido a la relación lógica de la jerarquía de etiquetas (anidamiento de subexpresiones, que son llamadas de función anidadas análogamente ).

  • Por lo tanto, existe el atributo derivado de la inmutabilidad (más generalmente RT), que las expresiones declarativas expresan solo las relaciones lógicas de las partes constituyentes (es decir, de los argumentos de la función de subexpresión) y no las relaciones de estado mutable .

Orden de evaluación

La elección del orden de evaluación de las subexpresiones solo puede dar un resultado variable cuando alguna de las llamadas a funciones no es RT (es decir, la función no es pura), por ejemplo, se accede a algún estado mutable externo a una función dentro de la función.

Por ejemplo, dada algunas expresiones anidadas, por ejemplo f( g(a, b), h(c, d) ), la evaluación impaciente y perezoso de los argumentos de la función dará los mismos resultados si las funciones f, gy hson puros.

Mientras que, si las funciones f, gy hno son puras, la elección del orden de evaluación puede dar un resultado diferente.

Tenga en cuenta que las expresiones anidadas son funciones anidadas conceptualmente, ya que los operadores de expresión son solo llamadas a funciones enmascaradas como prefijo unario, postfijo unario o notación de infijo binario.

Tangencialmente, si todos los identificadores, por ejemplo a, b, c, d, son inmutables en todas partes, no se puede acceder estado externos al programa (es decir, I / O), y no hay rotura de la capa de abstracción, a continuación, las funciones son siempre pura.

Por cierto, Haskell tiene una sintaxis diferente, f (g a b) (h c d).

Detalles del pedido de evaluación

Una función es una transición de estado (no un valor almacenado mutable) de la entrada a la salida. Para composiciones RT de llamadas a funciones puras , el orden de ejecución de estas transiciones de estado es independiente. La transición de estado de cada llamada de función es independiente de las demás, debido a la falta de efectos secundarios y al principio de que una función RT puede ser reemplazada por su valor en caché . Para corregir un concepto erróneo popular , la composición monádica pura siempre es declarativa y RT , a pesar del hecho de que la IOmónada de Haskell es posiblemente impura y, por lo tanto, imperativa en el Worldestado externo al programa (pero en el sentido de la advertencia a continuación, los efectos secundarios están aislados).

La evaluación entusiasta significa que los argumentos de las funciones se evalúan antes de que se llame a la función, y la evaluación diferida significa que los argumentos no se evalúan hasta (y si) se accede a ellos dentro de la función.

Definición : los parámetros de función se declaran en el sitio de definición de función y los argumentos de función se proporcionan en el sitio de llamada de función . Conoce la diferencia entre parámetro y argumento .

Conceptualmente, todas las expresiones son (una composición de) las llamadas de función, por ejemplo, las constantes se funciona sin entradas, operadores unarios son funciones con una entrada, operadores infijos binarios son funciones con dos entradas, los constructores son funciones, e instrucciones de control incluso (por ejemplo if, for, while) Se puede modelar con funciones. El orden en que estos argumentos funciones (no confundir con el fin de llamada a la función anidada) son evaluados no se declara por la sintaxis, por ejemplo, f( g() )podría evaluar con impaciencia ga continuación, fen gconsecuencia 's o podría evaluar fy sólo perezosamente evaluar gcuando se necesita su resultado dentro f.

Advertencia: el lenguaje completo de Turing (es decir, que permite una recursión ilimitada) es perfectamente declarativo, por ejemplo, la evaluación perezosa introduce la memoria y el indeterminismo del tiempo. Pero estos efectos secundarios debido a la elección del orden de evaluación se limitan al consumo de memoria, el tiempo de ejecución, la latencia, la no terminación y la histéresis externa, por lo tanto, la sincronización externa.

Programación funcional

Debido a que la programación declarativa no puede tener bucles, entonces la única forma de iterar es la recursión funcional. Es en este sentido que la programación funcional está relacionada con la programación declarativa.

Pero la programación funcional no se limita a la programación declarativa . La composición funcional se puede contrastar con el subtipo , especialmente con respecto al problema de expresión , donde la extensión se puede lograr agregando subtipos o descomposición funcional . La extensión puede ser una mezcla de ambas metodologías.

La programación funcional generalmente hace que la función sea un objeto de primera clase, lo que significa que el tipo de función puede aparecer en la gramática en cualquier otro lugar. El resultado es que las funciones pueden ingresar y operar en funciones, proporcionando así una separación de preocupaciones enfatizando la composición de la función, es decir, separando las dependencias entre las subcomputaciones de un cálculo determinista.

Por ejemplo, en lugar de escribir una función separada (y emplear recursividad en lugar de bucles si la función también debe ser declarativa) para cada uno de un número infinito de posibles acciones especializadas que podrían aplicarse a cada elemento de una colección, la programación funcional emplea iteración reutilizable funciones, por ejemplo map, fold, filter. Estas funciones de iteración ingresan una función de acción especializada de primera clase. Estas funciones de iteración iteran la colección y llaman a la función de acción especializada de entrada para cada elemento. Estas funciones de acción son más concisas porque ya no necesitan contener las declaraciones de bucle para iterar la colección.

Sin embargo, tenga en cuenta que si una función no es pura, entonces es realmente un procedimiento. Quizás podamos argumentar que la programación funcional que utiliza funciones impuras, es realmente una programación de procedimiento. Por lo tanto, si estamos de acuerdo en que las expresiones declarativas son RT, entonces podemos decir que la programación procesal no es programación declarativa, y por lo tanto podríamos argumentar que la programación funcional siempre es RT y debe ser un subconjunto de la programación declarativa.

Paralelismo

Esta composición funcional con funciones de primera clase puede expresar la profundidad en el paralelismo separando la función independiente.

Principio de Brent: el cálculo con trabajo w y profundidad d puede implementarse en una PRAM de procesador p en el tiempo O (max (w / p, d)).

Tanto la concurrencia como el paralelismo también requieren programación declarativa , es decir, inmutabilidad y RT.

Entonces, ¿de dónde surgió esta peligrosa suposición de que Paralelismo == Concurrencia? Es una consecuencia natural de los lenguajes con efectos secundarios: cuando su lenguaje tiene efectos secundarios en todas partes, cada vez que intenta hacer más de una cosa a la vez, esencialmente tiene un no determinismo causado por la intercalación de los efectos de cada operación . Entonces, en lenguajes de efectos secundarios, la única forma de obtener paralelismo es la concurrencia; Por lo tanto, no es sorprendente que a menudo los veamos combinados.

Orden de evaluación de FP

Tenga en cuenta que el orden de evaluación también afecta la terminación y el rendimiento de los efectos secundarios de la composición funcional.

Ansioso (CBV) y perezoso (CBN) son duelos categóricos [ 10 ], porque han invertido el orden de evaluación, es decir, si las funciones externas o internas respectivamente se evalúan primero. Imagine un árbol al revés, luego impaciente evalúa desde la rama del árbol de funciones inclina la jerarquía de la rama hasta el tronco de la función de nivel superior; mientras que, lazy evalúa desde el tronco hasta las puntas de las ramas. Eager no tiene productos conjuntivos ("y", a / k / a "productos" categóricos) y lazy no tiene coproductos disyuntivos ("o", a / k / a "sumas" categóricas) [ 11 ].

Actuación

  • Ansioso

    Al igual que con la no terminación, ansioso es demasiado ansioso con la composición funcional conjuntiva, es decir, la estructura de control de composición realiza un trabajo innecesario que no se realiza con pereza. Por ejemplo , ansioso e innecesariamente asigna la lista completa a booleanos, cuando está compuesta con un pliegue que termina en el primer elemento verdadero.

    Este trabajo innecesario es la causa del supuesto "hasta" un factor de registro adicional en la complejidad de tiempo secuencial de ansioso versus perezoso, ambos con funciones puras. Una solución es usar functores (p. Ej., Listas) con constructores perezosos (es decir, ansiosos con productos perezosos opcionales), porque con ansias la falta de entusiasmo se origina en la función interna. Esto se debe a que los productos son tipos constructivos, es decir, tipos inductivos con un álgebra inicial en un punto de fijación inicial [ 11 ]

  • Perezoso

    Al igual que con la no terminación, perezoso es demasiado perezoso con composición funcional disyuntiva, es decir, la finalidad coinductiva puede ocurrir más tarde de lo necesario, lo que resulta en un trabajo innecesario y no determinismo de la tardanza que no es el caso con ansioso [ 10 ] [ 11 ] . Ejemplos de finalidad son las excepciones de estado, tiempo, no terminación y tiempo de ejecución. Estos son efectos secundarios imperativos, pero incluso en un lenguaje declarativo puro (por ejemplo, Haskell), hay un estado en la mónada IO imperativa (nota: ¡no todas las mónadas son imperativas!) Implícita en la asignación de espacio, y el tiempo es un estado relativo al imperativo mundo real. El uso de la pereza incluso con los coproductos ansiosos opcionales filtra la "pereza" en los coproductos internos, porque con la pereza, la incorrección de la pereza se origina en la función externa.(vea el ejemplo en la sección Sin terminación, donde == es una función de operador binario externo). Esto se debe a que los coproductos están limitados por la finalidad, es decir, tipos coinductores con un álgebra final en un objeto final [ 11 ].

    Lazy causa indeterminismo en el diseño y depuración de funciones para latencia y espacio, cuya depuración probablemente está más allá de las capacidades de la mayoría de los programadores, debido a la disonancia entre la jerarquía de funciones declarada y el orden de evaluación del tiempo de ejecución. Las funciones puras perezosas evaluadas con entusiasmo, podrían introducir una no terminación nunca antes vista en tiempo de ejecución. Por el contrario, las ansiosas funciones puras evaluadas con vago, podrían potencialmente introducir indeterminismo de latencia y espacio nunca antes visto en tiempo de ejecución.

No terminación

En tiempo de compilación, debido al problema de detención y la recursividad mutua en un lenguaje completo de Turing, generalmente no se puede garantizar que las funciones terminen.

  • Ansioso

    Con ansioso pero no perezoso, para la conjunción de Head"y" Tail, si termina Heado Tailno, entonces respectivamente List( Head(), Tail() ).tail == Tail()o List( Head(), Tail() ).head == Head()no es cierto porque el lado izquierdo no termina y el lado derecho termina.

    Mientras que, con perezoso, ambos lados terminan. Por lo tanto, ansioso es demasiado ansioso con los productos conjuntivos y no termina (incluidas las excepciones de tiempo de ejecución) en aquellos casos en que no es necesario.

  • Perezoso

    Con perezoso pero no ansioso, la disyunción de 1"o" 2, si fno termina, entonces List( f ? 1 : 2, 3 ).tail == (f ? List( 1, 3 ) : List( 2, 3 )).tailno es cierto porque el lado izquierdo termina, y el lado derecho no.

    Mientras que, con ansias ninguno de los lados termina, la prueba de igualdad nunca se alcanza. Por lo tanto, perezoso es demasiado perezoso con coproductos disyuntivos, y en esos casos no termina (incluidas las excepciones de tiempo de ejecución) después de hacer más trabajo del que hubiera deseado.

[ 10 ] Continuaciones declarativas y dualidad categórica, Filinski, secciones 2.5.4 Una comparación de CBV y CBN, y 3.6.1 CBV y CBN en el SCL.

[ 11 ] Continuaciones declarativas y dualidad categórica, Filinski, secciones 2.2.1 Productos y coproductos, 2.2.2 Objetos terminales e iniciales, 2.5.2 CBV con productos perezosos y 2.5.3 CBN con coproductos ansiosos.


Incluso con la programación de restricción declarativa, las restricciones no mutan mientras el solucionador encuentra la solución. Esto es obvio porque no hay forma de especificar una hora para que cambien. Incluso las restricciones especificadas con otras restricciones se establecen antes de ejecutar el solucionador para encontrar la solución. Esto es análogo a las fórmulas declarativas en la hoja de cálculo .
Shelby Moore III

3
La abreviatura no significa proporcionar una definición. Cuando escribí "RT a menudo se abrevia 'sin efectos secundarios'", eso no significa que la definición de RT sea "sin efectos secundarios", porque las personas pueden tener diferentes definiciones de "efectos". Si en cambio dije "RT se abrevia a menudo 'xyz'", un símbolo sin sentido no le da ninguna definición a RT. RT tiene una definición precisa que nunca cambia, sin importar qué símbolo se use para referirse a ella.
Shelby Moore III

No puedo encontrar un contraejemplo a mi afirmación de que todo tipo de DP es RT. Por ejemplo, el significado (es decir, el valor) de los términos de una gramática sensible al contexto no mutan en un momento o posición diferente dentro de la gramática. Vea mi comentario de programación de restricciones arriba.
Shelby Moore III

1
Igualar C en estilo ESP con RT en mónada de estado no es válido , porque cada instrucción C puede mutar el estado global, mientras que "dentro" de la mónada de estado cada instrucción correspondiente genera una COPIA del estado (así modificado). El último es RT, el primero no. La composición monádica es siempre RT. DP == RT es el único significado para DP que es un conjunto disjunto de atributos (la prueba matemática es correcta, de lo contrario, DP no tiene sentido).
Shelby Moore III

1
Desearía poder entender esto más allá de la primera oración. Estaba leyendo el manual de DAX que indicaba que era un "lenguaje funcional". ¿Qué significa esto? No sé ve a preguntarle a tu papá.
Nick.McDermaid

103

Realmente no hay ninguna definición objetiva y no ambigua para estos. Aquí es cómo lo definiría ellos:

Imperativo : la atención se centra en los pasos que debe seguir la computadora en lugar de lo que hará (ej. C, C ++, Java).

Declarativo : la atención se centra en lo que la computadora debe hacer en lugar de cómo debe hacerlo (por ejemplo, SQL).

Funcional : un subconjunto de lenguajes declarativos que se centra principalmente en la recursividad


1
Tenga en cuenta un par de cosas: 1) la explicación pretende ser simple en lugar de todo incluido 2) como dije, hay varias formas de definir estos idiomas. Por lo tanto, la respuesta podría ser incorrecta para usted y para otra persona.
Jason Baker,

3
La programación funcional no es "un subconjunto de lenguajes declarativos". La programación declarativa requiere la inmutabilidad de los valores almacenados, la programación funcional no, si no es FP puro . Mira mi respuesta . Consulte también la explicación de las celdas de la hoja de cálculo . Las definiciones objetivas correctas no son "ambiguas". La programación imperativa también se centra "en lo que la computadora debe hacer". La única distinción es que la programación imperativa tiene que tratar con valores almacenados mutables.
Shelby Moore III

55
@ShelbyMooreIII: tiendo a estar de acuerdo con Eric Meijer en este caso. No existe realmente un "lenguaje funcional no puro". En lo que a mí respecta, Ocaml, F # y similares son lenguajes imprescindibles con estructuras de datos funcionales. Pero como dije en mi respuesta, no creo que haya una respuesta objetiva y no ambigua a esta pregunta. Hay múltiples formas de definir las cosas.
Jason Baker

3
Se puede demostrar matemáticamente que está combinando términos, cuando ninguna de las definiciones es inequívoca, porque los atributos elegidos no son un conjunto disjunto. Si define FP como solo FP puro (es decir, RT), entonces no es distinto de DP, cf. mi respuesta . Los atributos disjuntos de FP incluyen el tipo de función de primera clase, que puede ser una función imperativa. Encontré términos más fundamentales de ambigüedad aquí y aquí . Preferir FP puro es ortogonal a la definición de solo FP.
Shelby Moore III

21
@ShelbyMooreIII - Estaba asumiendo que el OP quería su respuesta en inglés, no Math Nerd-ese. Si esa fue una suposición no válida, entonces mis disculpas.
Jason Baker

54

imperativo y declarativo describen dos estilos opuestos de programación. imperativo es el enfoque tradicional de "receta paso a paso", mientras que declarativo es más "esto es lo que quiero, ahora ya sabes cómo hacerlo".

Estos dos enfoques se producen a lo largo de la programación, incluso con el mismo lenguaje y el mismo programa. en general, el enfoque declarativo se considera preferible, ya que libera al programador de tener que especificar tantos detalles, a la vez que tiene menos posibilidades de errores (si describe el resultado que desea, y un proceso automático bien probado puede funcionar al revés) defina los pasos, entonces puede esperar que las cosas sean más confiables que tener que especificar cada paso a mano).

Por otro lado, un enfoque imperativo le brinda más control de bajo nivel: es el "enfoque de microgestor" para la programación. y eso puede permitir al programador explotar el conocimiento sobre el problema para dar una respuesta más eficiente. así que no es inusual que algunas partes de un programa se escriban en un estilo más declarativo, sino que las partes críticas para la velocidad sean más imperativas.

como se puede imaginar, el lenguaje que usa para escribir un programa afecta cuán declarativo puede ser: un lenguaje que tiene "inteligencia" incorporada para determinar qué hacer, dada una descripción del resultado, permitirá una declaración mucho más declarativa enfoque que uno en el que el programador necesita agregar primero ese tipo de inteligencia con código imperativo antes de poder construir una capa más declarativa en la parte superior. así, por ejemplo, un lenguaje como prolog se considera muy declarativo porque tiene un proceso incorporado que busca respuestas.

Hasta ahora, notará que no he mencionado la programación funcional . eso se debe a que es un término cuyo significado no está inmediatamente relacionado con los otros dos. en su forma más simple, la programación funcional significa que usa funciones. en particular, que usa un lenguaje que admite funciones como "valores de primera clase", lo que significa que no solo puede escribir funciones, sino que también puede escribir funciones que escriben funciones (que escriben funciones que ...), y pasen funciones a funciones en resumen: las funciones son tan flexibles y comunes como cosas como cadenas y números.

Puede parecer extraño, entonces, que funcional, imperativo y declarativo a menudo se mencionen juntos. La razón de esto es una consecuencia de llevar la idea de la programación funcional "al extremo". una función, en su sentido más puro, es algo de las matemáticas, una especie de "recuadro negro" que toma algo de entrada y siempre da el mismo resultado. y ese tipo de comportamiento no requiere almacenar variables cambiantes. por lo tanto, si diseña un lenguaje de programación cuyo objetivo es implementar una idea de programación funcional muy pura y matemáticamente influenciada, terminará rechazando, en gran medida, la idea de valores que pueden cambiar (en cierto sentido técnico limitado).

y si hace eso, si limita cómo pueden cambiar las variables, entonces casi por accidente termina obligando al programador a escribir programas que sean más declarativos, porque una gran parte de la programación imperativa describe cómo cambian las variables, y ya no puede ¡Haz eso! Resulta que la programación funcional, particularmente la programación en un lenguaje funcional, tiende a dar más código declarativo.

para resumir, entonces:

  • imperativo y declarativo son dos estilos opuestos de programación (los mismos nombres se usan para lenguajes de programación que fomentan esos estilos)

  • La programación funcional es un estilo de programación donde las funciones se vuelven muy importantes y, como consecuencia, los valores cambiantes se vuelven menos importantes. La capacidad limitada para especificar cambios en los valores obliga a un estilo más declarativo.

entonces "programación funcional" a menudo se describe como "declarativa".


55
La mejor explicación hasta ahora. Parece que Functional y OOP son ortogonales a Imperativo y Declarativo.
Didier A.

¿Diría que la programación lógica es declarativa? ¿O es en sí mismo ortogonal?
Didier A.

51

En una palabra:

Un lenguaje imperativo especifica una serie de instrucciones que la computadora ejecuta en secuencia (haga esto, luego haga eso).

Un lenguaje declarativo declara un conjunto de reglas sobre qué salidas deberían resultar de qué entradas (por ejemplo, si tiene A, entonces el resultado es B). Un motor aplicará estas reglas a las entradas y dará una salida.

Un lenguaje funcional declara un conjunto de funciones matemáticas / lógicas que definen cómo se traduce la entrada a la salida. p.ej. f (y) = y * y. Es un tipo de lenguaje declarativo.


1
La programación funcional no es "un tipo de lenguaje declarativo". La programación declarativa requiere la inmutabilidad de los valores almacenados, la programación funcional impura no. Mira mi respuesta . Consulte también la explicación de las celdas de la hoja de cálculo . La única razón por la que la lógica imperativa (es decir, las instrucciones) se ejecuta en secuencia es que, debido a la presencia de valores almacenados mutables, el resultado depende del orden de evaluación. Usando su vocabulario, una "instrucción" puede (y una "regla" no puede) operar con valores mutables.
Shelby Moore III

23

Imperativo: cómo lograr nuestro objetivo

   Take the next customer from a list.
   If the customer lives in Spain, show their details.
   If there are more customers in the list, go to the beginning

Declarativo: lo que queremos lograr

   Show customer details of every customer living in Spain

Está describiendo programación funcional vs. no FP, no declarativa vs. programación imperativa. La programación funcional es ortogonal a la polaridad entre la programación imperativa y declarativa. La programación declarativa requiere la inmutabilidad de los valores almacenados, la programación funcional impura no. Mira mi respuesta .
Shelby Moore III

22

Programación imperativa significa cualquier estilo de programación donde su programa está estructurado a partir de instrucciones que describen cómo ocurrirán las operaciones realizadas por una computadora .

Programación declarativa significa cualquier estilo de programación donde su programa es una descripción del problema o la solución, pero no indica explícitamente cómo se realizará el trabajo .

La programación funcional es la programación mediante la evaluación de funciones y funciones de funciones ... Como la programación funcional (estrictamente definida) significa programar definiendo funciones matemáticas libres de efectos secundarios, por lo que es una forma de programación declarativa pero no es el único tipo de programación declarativa .

La programación lógica (por ejemplo, en Prolog) es otra forma de programación declarativa. Involucra la computación al decidir si una declaración lógica es verdadera (o si se puede satisfacer). El programa suele ser una serie de hechos y reglas, es decir, una descripción en lugar de una serie de instrucciones.

La reescritura de términos (por ejemplo, CASL) es otra forma de programación declarativa. Implica la transformación simbólica de los términos algebraicos. Es completamente distinto de la programación lógica y la programación funcional.


La programación funcional no es "una forma de programación declarativa". La programación declarativa requiere la inmutabilidad de los valores almacenados, la programación funcional impura no. Mira mi respuesta . Consulte también la explicación de las celdas de la hoja de cálculo . El término "trabajo" en "describir cómo hacer el trabajo" no está definido. La única razón por la que la lógica imperativa (también conocida como "instrucciones") se ejecuta en secuencia es que debido a la presencia de valores almacenados mutables, el resultado depende del orden de evaluación.
Shelby Moore III

2
Tómelo como leído que estaba hablando de programación funcional pura . Estos paradigmas pueden cruzarse y no quiero atascarme comparando lenguajes híbridos. En teoría, al menos la programación funcional se trata de funciones en lugar de describir cómo una computadora realizará cada cálculo, por lo que mantengo que es declarativa.
Dafydd Rees

Edité mi respuesta , y en la sección "Programación funcional", agregué un escenario en el que podríamos argumentar que FP siempre es puro, e impuro FP es realmente "programación procesal". Disculpas por no incluir esa interpretación antes.
Shelby Moore III

13

imperativo - las expresiones describen la secuencia de acciones a realizar (asociativo)

declarativo : las expresiones son declaraciones que contribuyen al comportamiento del programa (asociativo, conmutativo, idempotente, monotónico)

funcional : las expresiones tienen valor como único efecto; la semántica apoya el razonamiento equitativo


1
Las expresiones declarativas contribuyen al comportamiento previsto del programa, el imperativo puede contribuir a lo intencionado o no. La declaración no necesita ser conmutativa e idempotente, si se trata de una semántica intencional. Me gusta tu esencia concisa de funcional, así que la voté.
Shelby Moore III

10

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.

Todos los tipos exóticos de programación encajan en la siguiente taxonomía de declarativo versus imperativo, ya que la siguiente definición afirma que son duales.

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 debe 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 Ciencias de la Computación 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, laApplicative , 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, se cambian los identificadores de celda. 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 la capacidad de 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 sean coherentes 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 escritura , y la escritura nunca puede capturar toda la semántica de un programa, ni siquiera para los idiomas que son No se completa, 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 de denominación 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 se pueden verificar (probar 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 divergen los intérpretes) 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.

Entonces, 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.).


Robert Harper parece estar de acuerdo conmigo sobre la falta de sentido de la mayoría de las definiciones de declarativa , pero no creo que haya visto la mía arriba. Se acerca a mi definición, donde analiza la semántica denotacional, pero no llega a mi definición. El modelo (semántica denotacional) es de nivel superior .
Shelby Moore III

7

Programación imperativa: decirle a la "máquina" cómo hacer algo, y como resultado lo que quiere que suceda sucederá.

Programación declarativa: decirle a la "máquina" lo que le gustaría que suceda y dejar que la computadora descubra cómo hacerlo.

Ejemplo de imperativo

function makeWidget(options) {
    const element = document.createElement('div');
    element.style.backgroundColor = options.bgColor;
    element.style.width = options.width;
    element.style.height = options.height;
    element.textContent = options.txt;

    return element;
}

Ejemplo de declarativo

function makeWidget(type, txt) {
    return new Element(type, txt);
}

Nota: La diferencia no es de brevedad, complejidad o abstracción. Como se dijo, la diferencia es cómo vs qué .


2
¡uno bueno pero mejor si proporciona al menos un ejemplo para ambos!
Pardeep Jain

4

Hoy en día, nuevo enfoque: ¿necesitamos las viejas clasificaciones?

Los aspectos Imperativo / Declarativo / Funcional fueron buenos en el pasado para clasificar lenguajes genéricos, pero en la actualidad todos los "grandes lenguajes" (como Java, Python, Javascript, etc.) tienen alguna opción (típicamente marcos ) para expresarse con "otro enfoque". que su principal (imperativo habitual), y para expresar procesos paralelos, funciones declarativas, lambdas, etc.

Entonces, una buena variante de esta pregunta es "¿Qué aspecto es bueno para clasificar los marcos hoy?" ... Un aspecto importante es algo que podemos etiquetar como "estilo de programación" ...

Centrarse en la fusión de datos con algoritmo

Un buen ejemplo para explicar. Como puedes leer sobre jQuery en Wikipedia ,

El conjunto de características principales de jQuery (selección de elementos DOM, recorrido y manipulación), habilitado por su motor selector (...), creó un nuevo "estilo de programación", fusionando algoritmos y estructuras de datos DOM

Entonces, jQuery es el mejor ejemplo (popular) de enfocarse en un "nuevo estilo de programación" , que no es solo la orientación a objetos, es " Fusionar algoritmos y estructuras de datos ". jQuery es algo reactivo como las hojas de cálculo, pero no está "orientado a la celda", está " orientado al nodo DOM " ... Comparando los estilos principales en este contexto:

  1. Sin fusión : en todos los "lenguajes grandes", en cualquier expresión funcional / declarativa / imperativa, lo habitual es "no fusión" de datos y algoritmos, excepto por alguna orientación a objetos, que es una fusión en el estricto punto de vista de la estructura algebric .

  2. Alguna fusión : todas las estrategias clásicas de fusión, en la actualidad tienen algún marco que lo usa como paradigma ... flujo de datos , programación dirigida por eventos (o lenguajes específicos de dominio antiguos como awk y XSLT ) ... Al igual que la programación con hojas de cálculo modernas, también son ejemplos de estilo de programación reactiva .

  3. Gran fusión : es "el estilo jQuery" ... jQuery es un lenguaje específico de dominio que se centra en " algoritmos de fusión y estructuras de datos DOM ".
    PD: otros "lenguajes de consulta", como XQuery, SQL (con PL como opción de expresión imperativa) también son ejemplos de fusión de algoritmos de datos, pero son islas , sin fusión con otros módulos del sistema ... Primavera , cuando se usan find()-variantes y cláusulas de especificación , es otro buen ejemplo de fusión.


3

La programación declarativa es la programación expresando cierta lógica intemporal entre la entrada y la salida, por ejemplo, en pseudocódigo, el siguiente ejemplo sería declarativo:

def factorial(n):
  if n < 2:
    return 1
  else:
    return factorial(n-1)

output = factorial(argvec[0])

Aquí definimos una relación llamada 'factorial', y definimos la relación entre la salida y la entrada como esa relación. Como debería ser evidente aquí, sobre cualquier lenguaje estructurado permite la programación declarativa hasta cierto punto. Una idea central de la programación declarativa son los datos inmutables, si asigna a una variable, solo lo hace una vez, y nunca más. Otras definiciones más estrictas implican que puede no haber efectos secundarios, estos lenguajes a veces se denominan 'puramente declarativos'.

El mismo resultado en un estilo imperativo sería:

a = 1
b = argvec[0]
while(b < 2):
  a * b--

output = a

En este ejemplo, no expresamos ninguna relación lógica estática intemporal entre la entrada y la salida, cambiamos las direcciones de memoria manualmente hasta que una de ellas contuvo el resultado deseado. Debería ser evidente que todos los lenguajes permiten la semántica declarativa hasta cierto punto, pero no todos permiten el imperativo, algunos lenguajes declarativos 'puramente' permiten efectos secundarios y mutación por completo.

A menudo se dice que los lenguajes declarativos especifican 'qué se debe hacer', en lugar de 'cómo hacerlo', creo que es un nombre inapropiado, los programas declarativos todavía especifican cómo se debe obtener de entrada a salida, pero de otra manera, el la relación que especifique debe ser efectivamente computable (término importante, búsquelo si no lo sabe). Otro enfoque es la programación no determinista , que realmente solo especifica qué condiciones cumple un resultado, antes de que su implementación simplemente agote todas las rutas en prueba y error hasta que tenga éxito.

Los lenguajes puramente declarativos incluyen Haskell y Pure Prolog. Una escala móvil de uno a otro sería: Pure Prolog, Haskell, OCaml, Scheme / Lisp, Python, Javascript, C--, Perl, PHP, C ++, Pascall, C, Fortran, Assembly


No definiste la programación funcional. Implicó incorrectamente, "algunos lenguajes declarativos 'puramente'", que la programación declarativa puede ser impura . La programación declarativa requiere la inmutabilidad de los valores almacenados, la programación imperativa no. Mira mi respuesta . La inmutabilidad es la cualidad "intemporal": asegúrese de que su declaración factorialno mute ningún valor.
Shelby Moore III

3

Algunas buenas respuestas aquí con respecto a los "tipos" señalados.

Presento algunos conceptos adicionales, más "exóticos" a menudo asociados con la multitud de programación funcional:

  • Lenguaje específico de dominio o DSL programación : crear un nuevo lenguaje para tratar el problema en cuestión.
  • Metaprogramación : cuando su programa escribe otros programas.
  • Programación evolutiva : donde construye un sistema que se mejora continuamente o genera generaciones sucesivamente mejores de subprogramas.

3

Creo que tu taxonomía es incorrecta. Hay dos tipos opuestos imperativo y declarativo. Funcional es solo un subtipo de declarativo. Por cierto, wikipedia afirma el mismo hecho.


+1: Sí, los paradigmas son las manzanas y las naranjas.
Nikhil Chelliah

FP no es "solo un subtipo de declarativa". FP es ortogonal a la polaridad de imperativo frente a DP. DP requiere la inmutabilidad de los valores almacenados, FP impuro no. Wikipedia está combinando FP puro con FP, con la afirmación absurda de que los siguientes conceptos son "generalmente ajenos a la programación imperativa": funciones de primera clase, recursividad, orden de evaluación y tipeo estático. Entonces Wikipedia admite "Programación funcional en lenguajes no funcionales" impuros .
Shelby Moore III

Wikipedia está equivocado en este punto. Muchos lenguajes funcionales populares permiten programar en un "estilo declarativo", si lo elige, pero no son lenguajes declarativos. Pero lo mismo podría decirse de C, donde aún puede programar en un estilo funcional si lo desea, usando void * s.
Plynx

Probablemente, debería haber sido más claro sobre este punto, pero desde el otro lado, no iba a confundir el tema de inicio con detalles no relevantes (imo). Veo que los lenguajes funcionales tienden a usarse de manera declarativa. Puede intentar escribir declarativamente y / o funcionalmente en ASM o C o probablemente puede escribir un programa imperativo en Lisp, pero dudo que sea muy útil o informativo para el autor de la pregunta. Así que, en esencia, todavía considero que mi respuesta es apropiada, incluso si pudiera expresarse de manera diferente.
Rorick

2

En pocas palabras, cuanto más enfatiza un estilo de programación Qué (hacer) abstrayendo los detalles de Cómo (hacerlo), más se considera que ese estilo es declarativo. Lo contrario es cierto para imperativo. La programación funcional está asociada con el estilo declarativo.


Vea mis comentarios debajo de las otras respuestas. FP no siempre es declarativa. What vs. how es la taxonomía incorrecta para IP vs. DP, ya que tanto DP como IP tienen una lógica que involucra el qué y el cómo.
Shelby Moore III
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.