El sueño de la programación declarativa [cerrado]


26

¿Por qué no se ha cumplido el sueño de la programación declarativa? ¿Cuáles son algunos obstáculos concretos que se interponen en el camino? Por un simple ejemplo, ¿por qué no puedo decir

sort(A) is defined by sort(A) in perm(A) && asc(sort(A))

y automáticamente obtiene un algoritmo de clasificación. permsignifica permutaciones y ascsignifica ascendente.


44
Por cierto, su ejemplo específico ya está disponible: gkoberger.github.io/stacksort
Den

3
¿Has oído hablar de Prolog? Simplemente busque "Programación de conjunto de respuestas". Hay muchos sistemas basados ​​en la lógica predeterminada.
schlingel

16
Bueno, esta pregunta es fácil de responder. Intente implementar dicho sistema . ¿Qué te impidió hacerlo con éxito? Las probabilidades son buenas de que lo que sea que te haya detenido haya detenido a los demás.
Eric Lippert

44
Estoy tentado a creer que esta pregunta merece más crédito del que está recibiendo. Cuando lo miras a primera vista, puedes pensar: ¡ Bueno, eso es simple! Tienes que programar toda esa lógica detrás, y las computadoras simplemente no son tan inteligentes. ... Pero luego regresas y le echas un segundo vistazo a esta pregunta, y piensas una vez más: Bueno, sí, eso es simple, y tienes que programar toda esa lógica, y las computadoras no son necesariamente las herramientas más agudas en el cobertizo, cierto, pero esa explicación tiene mucha más profundidad que lo que simplemente se encuentra en la superficie.
Panzercrisis

3
Su descripción de un algoritmo de clasificación es declarativa, sí, pero seguro que no es eficiente. Hay n!permutaciones de una secuencia y, en el peor de los casos, su algoritmo tendrá que probarlas todas para encontrar una ordenada. El tiempo factorial es tan malo como lo puede hacer un algoritmo para procesar una secuencia.
Benjamin Hodgson

Respuestas:


8

Hay algunas muy buenas respuestas. Intentaré contribuir a la discusión.

Sobre el tema de la programación declarativa y lógica en Prolog, está el gran libro "The Craft of Prolog" de Richard O'Keefe . Se trata de escribir programas eficientes utilizando un lenguaje de programación que le permite escribir programas muy ineficientes. En este libro, mientras discute las implementaciones eficientes de varios algoritmos (en el capítulo "Métodos de programación"), el autor adopta el siguiente enfoque:

  • define el problema en inglés
  • escriba una solución de trabajo que sea lo más declarativa posible; por lo general, eso significa exactamente lo que tienes en tu pregunta, solo corrige Prolog
  • a partir de ahí, tome medidas para refinar la implementación para que sea más rápida

La observación más esclarecedora (para mí) que pude hacer mientras trabajaba a través de estos:

Sí, la versión final de la implementación es mucho más eficiente que la especificación "declarativa" con la que comenzó el autor. Todavía es muy declarativo, sucinto y fácil de entender. Lo que sucedió en el medio es que la solución final captura las propiedades del problema al que la solución inicial era ajena.

En otras palabras, al implementar una solución, hemos utilizado tanto de nuestro conocimiento sobre el problema como hemos podido. Comparar:

Encuentre una permutación de una lista de manera que todos los elementos estén en orden ascendente

a:

Fusionar dos listas ordenadas dará como resultado una lista ordenada. Como puede haber sublistas que ya están ordenadas, úselas como punto de partida, en lugar de sublistas de longitud 1.

Un pequeño aparte: una definición como la que has dado es atractiva porque es muy general. Sin embargo, no puedo escapar de la sensación de que ignora deliberadamente el hecho de que las permutaciones son, bueno, un problema combinatorio. ¡Esto es algo que ya sabemos ! Esto no es una crítica, solo una observación.

En cuanto a la verdadera pregunta: ¿cómo avanzar? Bueno, una forma es proporcionar tanto conocimiento sobre el problema que estamos declarando a la computadora.

El mejor intento que conozco para resolver realmente el problema se presenta en los libros escritos por Alexander Stepanov, "Elementos de programación" y "De las matemáticas a la programación genérica" . Lamentablemente, no estoy a la altura de la tarea de resumir (o incluso comprender completamente) todo en estos libros. Sin embargo, el enfoque es definir algoritmos de biblioteca y estructuras de datos eficientes (o incluso óptimos), con la condición de que todas las propiedades relevantes de la entrada se conozcan de antemano. El resultado final es:

  • Cada transformación bien definida es un refinamiento de las restricciones que ya existen (las propiedades que se conocen);
  • Dejamos que la computadora decida qué transformación es óptima en función de las restricciones existentes.

En cuanto a por qué todavía no ha sucedido, bueno, la informática es un campo muy joven, y todavía estamos haciendo frente a apreciar realmente la novedad de la mayoría.

PD

Para darle una idea de lo que quiero decir con "refinar la implementación": tomemos, por ejemplo, el problema fácil de obtener el último elemento de una lista, en Prolog. La solución declarativa canónica es decir:

last(List, Last) :-
    append(_, [Last], List).

Aquí, el significado declarativo de append/3es:

List1AndList2es la concatenación de List1yList2

Dado que en el segundo argumento append/3tenemos una lista con solo un elemento, y el primer argumento es ignorado (el guión bajo), obtenemos una división de la lista original que descarta el frente de la lista ( List1en el contexto de append/3) y exige que la parte posterior ( List2en el contexto de append/3) es de hecho una lista con un solo elemento: por lo tanto, es el último elemento.

La implementación real proporcionada por SWI-Prolog , sin embargo, dice:

last([X|Xs], Last) :-
    last_(Xs, X, Last).

last_([], Last, Last).
last_([X|Xs], _, Last) :-
    last_(Xs, X, Last).

Esto sigue siendo muy bien declarativo. Lea de arriba a abajo, dice:

El último elemento de una lista solo tiene sentido para una lista de al menos un elemento. El último elemento para un par de la cola y el encabezado de una lista, entonces, es: el encabezado, cuando la cola está vacía, o el último de la cola no vacía.

La razón por la que se proporciona esta implementación es para solucionar los problemas prácticos que rodean el modelo de ejecución de Prolog. Idealmente, no debería marcar la diferencia qué implementación se utiliza. Del mismo modo, podríamos haber dicho:

last(List, Last) :-
    reverse(List, [Last|_]).

El último elemento de una lista es el primer elemento de la lista invertida.

Si desea llenar sus discusiones inconclusas sobre lo que es bueno, el Prolog declarativo, simplemente revise algunas de las preguntas y respuestas en la etiqueta Prolog en Stack Overflow .


2
+1 para mostrar cómo un diseño declarativo puede progresar desde una simple abstracción a una implementación más concreta a través de un proceso de diseño iterativo.
itsbruce

1
@ Boris Esta es una buena respuesta. Ese libro está sentado en mi estantería. Probablemente es hora de que lo abra.
davidk01

1
@ davidk01 Uno de los mejores libros que existen. Se supone que se siente bastante cómodo con Prolog y la programación en general, pero el enfoque que toma para programar es pragmático y muy completo.
XXX

2
@Boris Sé que el ejemplo no es complejo, pero la productividad del proceso de diseño iterativo, una verdadera fortaleza de los lenguajes declarativos, y su valor muy práctico, es crucial. Los lenguajes declarativos ofrecen un enfoque claro, consistente y recursivo para la mejora iterativa. Los lenguajes imperativos no.
itsbruce

1
+1 para "llenarse de discusiones no concluyentes sobre lo que es bueno, el Prólogo declarativo" ... ¡muy cierto que tendemos a estar en desacuerdo!
Daniel Lyons

50

Los lenguajes lógicos ya hacen esto. Puede definir la ordenación de forma similar a como lo está haciendo.

El principal problema es el rendimiento. Las computadoras pueden ser excelentes para calcular muchas cosas, pero son inherentemente tontas. Cada decisión "inteligente" que una computadora podría tomar fue programada por un programador. Y esta decisión generalmente se describe no por cómo se ve el resultado final, sino por cómo lograr, paso a paso, este resultado final.

Imagina la historia de un Golem . Si intentas darle un comando abstracto, en el mejor de los casos, lo hará de manera ineficiente y, en el peor de los casos, se lastimará a ti mismo, a ti oa alguien más. Pero si describe lo que desea con el mayor detalle posible, tiene la garantía de que la tarea se completará de manera efectiva y eficiente.

El trabajo del programador es decidir qué nivel de abstracción usar. Para la aplicación que está haciendo, ¿va a ir a un nivel superior y describirlo de manera abstracta y tomar el rendimiento o ir bajo y sucio, pasar 10 veces más tiempo en él, pero obtener un algoritmo que es 1000 veces más eficaz?


66
Puede ser útil saber que la palabra Golem גולם en realidad significa "materia prima", es decir, el estado más básico en el que puede estar la máquina / entidad.
dotancohen

2
Los lenguajes declarativos no son inherentemente un obstáculo para niveles más bajos de abstracción. Haskell y Standard ML, en sus diferentes formas, le permiten hacer declaraciones declarativas simples sobre tipos / funciones en un lugar, proporcionan una gama de implementaciones de funciones concretas y específicas en un lugar separado y formas de combinar tipos con implementaciones en otro. Mientras tanto, la mejor práctica en lenguajes OO / Imperativos ahora se trata principalmente de comenzar alto / simple y luego agregar detalles de implementación. La diferencia es que la alta abstracción es más fácil en FP, el nivel bajo es más fácil imperativamente.
itsbruce

2
Debería decir que también es posible, en cualquiera de los idiomas mencionados, resolver la elección de una implementación específica automáticamente en función de las propiedades del tipo en lugar de codificar coincidencias específicas, lo que prácticamente entrega lo que el OP quiere. En Haskell, las clases de tipos serían una herramienta clave para esto. En Standard ML, functors.
itsbruce

22
@BAR Golem! = Golum Golem es del folklore judío
Euphoric

10
Mi conclusión de esta respuesta es escribir אמת en mi computadora portátil.
Dan J

45

Además del excelente punto de Euphoric , me gustaría agregar que ya estamos usando lenguajes declarativos en muchos lugares donde funcionan bien, es decir, describir un estado que no es probable que cambie o solicitar algo para lo cual la computadora realmente puede generar código eficiente por sí mismo:

  • HTML declara cuál es el contenido de una página web.

  • CSS declara cómo deberían ser los distintos tipos de elementos en una página web.

  • Cada base de datos relacional tiene un lenguaje de definición de datos que declara cuál es la estructura de la base de datos.

  • SQL está mucho más cerca de lo declarativo que de lo imperativo, ya que usted le dice lo que quiere ver y el planificador de consultas de la base de datos descubre cómo hacerlo realidad.

  • Se podría argumentar que la mayoría de los archivos de configuración (.vimrc, .profile, .bashrc, .gitconfig) están utilizando un lenguaje específico de dominio que es en gran parte declarativo


3
Mencionaré GNU Make, XSLT, Angular.js como cosas ampliamente utilizadas que también son declarativas (aunque angular quizás empuje un poco la definición).
Mark K Cowan

Permítanme agregar expresiones regulares a esa lista.
Schwern

77
La gente tiende a olvidar que los lenguajes declarativos son comunes . Por lo general, no son turing idiomas completos. Agregue expresiones regulares a esa lista.
slebetman

Un poco pedante, pero aún así: no todas las bases de datos tienen un DDL, solo piense en la gran cantidad de bases de datos NoSQL sin esquema. Todas las bases de datos relacionales pueden tener, pero no todas las bases de datos.
Restablecer Monica - dirkk

1
@dirkk no había pensado en eso. Corrigió mi respuesta.
Ixrec

17

Las abstracciones tienen fugas

Puede implementar un sistema declarativo donde declare lo que desea, y el compilador o el intérprete descifran un orden de ejecución. El beneficio teórico es que te libera de tener que pensar en el "cómo" y no tienes que detallar esta implementación. Sin embargo, en la práctica para la informática de propósito general, aún debe pensar en el 'cómo' y escribir todo tipo de trucos, teniendo en cuenta cómo se implementará, ya que de lo contrario el compilador puede (y a menudo elegirá) una implementación que será muy, muy, muy lento (por ejemplo, n! operaciones donde n sería suficiente).

En su ejemplo particular, obtendrá un algoritmo de clasificación A , no significa que obtendrá uno bueno o incluso algo utilizable. Su definición dada, si se implementa literalmente (como probablemente lo haría un compilador) da como resultado http://en.wikipedia.org/wiki/Bogosort, que no se puede usar para conjuntos de datos más grandes: es técnicamente correcto, pero necesita una eternidad para ordenar mil números .

Para algunos dominios limitados, puede escribir sistemas que casi siempre funcionan bien para descubrir una buena implementación, por ejemplo, SQL. Para la informática de propósito general que no funciona particularmente bien: puede escribir sistemas en, por ejemplo, Prolog, pero tiene que visualizar cómo exactamente sus declaraciones se convertirán en un orden de ejecución imperativo al final, y eso pierde gran parte de la declaración declarada Beneficios de programación.


Si bien lo que usted dice es esencialmente cierto, el mal desempeño no es una señal de una abstracción permeable a menos que la interfaz / contrato le brinde garantías, por ejemplo, sobre el tiempo de ejecución.
valenterry

3
Peters no dice que el mal desempeño sea un signo de abstracción permeable, @valenterry. En todo caso, está diciendo lo contrario: que para lograr un buen rendimiento, los detalles de implementación se ven obligados a filtrarse.
itsbruce

2
Creo que es engañoso decir que las abstracciones tienen fugas solo porque necesita comprender la implementación para comprender cómo afecta el rendimiento. El propósito de una abstracción no es protegerte de tener que pensar en el rendimiento.
Doval

1
@jamesqf En la programación declarativa, simplemente declararías que algo está ordenado. Puede declarar que el orden de clasificación está vinculado a alguna variable / propiedad. Y entonces sería así. No es necesario llamar explícitamente a la ordenación cada vez que se agregan nuevos datos o cambia el orden de clasificación.
hyde

1
@jamesqf Realmente no puedes ver el punto sin probarlo realmente (recomendaría QML de Qt para jugar con ideas declarativas). Imagínese a alguien que solo conoce la programación imperativa, y que intenta comprender el punto de la POO o la programación funcional sin realmente probarlo de verdad.
hyde

11

La capacidad de decisión computacional es la razón más importante por la que la programación declarativa no ha resultado ser tan fácil como parece.

Muchos problemas que son relativamente fáciles de enunciar han demostrado ser indecidibles o tienen una complejidad NP completa para resolver. Esto ocurre a menudo cuando tenemos en cuenta las clases negativas y la clasificación, la contabilidad y la recursividad.

Me gustaría ejemplificar esto con algunos dominios que son bien conocidos.

La decisión sobre qué clase de CSS usar necesita conocimiento y consideración de todas las reglas de CSS. Agregar nuevas reglas podría invalidar todas las demás decisiones. Las clases negativas de CSS no se agregan intencionalmente al lenguaje, debido a problemas de NP completo, pero la falta de clases negativas complica las decisiones de diseño de CSS.

Dentro de un optimizador de consultas (SQL), existe el problemático problema de decidir en qué orden unirse, qué índices usar y qué memoria asignar a qué resultados temporales. Este es un problema conocido de NP completo y complica el diseño de la base de datos y la formulación de consultas. Para formularlo de manera diferente: al diseñar una base de datos o una consulta, el diseñador necesita conocer las acciones y el orden de las acciones que probablemente tomará el optimizador de consultas. Un ingeniero experimentado necesita conocimiento de la heurística utilizada por los principales proveedores de bases de datos.

Los archivos de configuración son declarativos, pero ciertas configuraciones son difíciles de declarar. Por ejemplo, para configurar adecuadamente las características, uno debe tener en cuenta el control de versiones, la implementación (y el historial de implementación), las posibles anulaciones manuales y los posibles conflictos con otras configuraciones. Validar adecuadamente una configuración puede convertirse en un problema NP-completo.

El resultado es que estas complicaciones toman por sorpresa a los principiantes, rompen la 'belleza' de la programación declarativa y hacen que algunos ingenieros busquen otras soluciones. La migración de ingenieros sin experiencia de SQL a NoSQL podría haber sido provocada por las complejidades subyacentes de las bases de datos relacionales.


2
"Las clases negativas de CSS no se agregan intencionalmente al lenguaje, debido a problemas de NP completo". ¿Puede dar más detalles?
John Dvorak

Es un poco un ejercicio, pero con selectores negativos de CSS es posible reescribirlos a un problema 3SAT (siendo la última cláusula el DOM), lo que requeriría probar todas las combinaciones posibles, para ver si coincide.
Dibbeke

1
Pequeña adición. En CSS 3 y 4, se permiten selectores negativos, pero: no se pueden anidar las pseudo-clases.
Dibbeke

2

Tenemos una diferencia en la declarativa de los lenguajes de programación que se usa bien en la verificación de la lógica digital.

Normalmente, la lógica digital se describe en el nivel de transferencia de registro (RTL) donde se define el nivel lógico de las señales entre registros. Para comprobar que estamos agregando cada vez más propiedades definidas de una manera más abstracta y declarativa.

Uno de los subconjuntos de idiomas / idiomas más declarativos se llama PSL para el lenguaje de especificación de propiedades. Al probar un modelo RTL de un multiplicador en el que, por ejemplo, se deben especificar todas las operaciones lógicas de cambio y suma en múltiples ciclos de reloj; puede escribir una propiedad de la manera de assert that when enable is high, this output will equal the multiplication of these two inputs after no more than 8 clock cycles. La descripción de la PSL se puede verificar junto con la RTL en una simulación, o se puede demostrar formalmente que la PSL es válida para la descripción de la RTL.

El modelo PSL más declarativo obliga a uno a describir el mismo comportamiento que la descripción RTL, pero de una manera suficientemente diferente que puede verificarse automáticamente contra el RTL para ver si están de acuerdo.


1

Principalmente el problema es cómo modela los datos; y la programación declarativa no está ayudando aquí. En los idiomas imperativos ya tienes toneladas de bibliotecas que hacen muchas cosas por ti, por lo que solo necesitas saber a qué llamar. De una manera particular, uno podría considerar esta programación declarativa (probablemente el mejor ejemplo para esto es Stream API en Java 8 ). Teniendo esto, la abstracción ya está resuelta y la programación declarativa no es necesaria.

Además, como se ha dicho, los lenguajes de programación lógica ya hacen exactamente lo que quieres. Se podría decir que el problema es el rendimiento, pero con el hardware y la investigación actuales en esta área, las cosas pueden mejorarse para estar listas para su uso en producción; en realidad, Prolog se usa para cosas de IA, pero creo que solo la academia.

Cabe señalar que se aplica a los lenguajes de programación de uso general. Para lenguajes específicos de dominio, los lenguajes declarativos son mucho mejores; SQL probablemente es el mejor ejemplo.


3
Modelado de datos? Escogiste la cosa en la que los imperativos son peores Lenguajes funcionales declarativos como Haskell y ML se destacan en el modelado de datos. Los tipos de datos algebraicos y los tipos de datos recursivos, por ejemplo, generalmente se pueden definir de manera integral en una o dos líneas. Claro, todavía tiene las funciones para escribir, pero su código se deriva inexorablemente de la definición de tipo y está limitado por él. Extraña comparación para hacer.
itsbruce

1
@itsbruce La cosa es que la mayoría de los datos reales no se asignan fácilmente a ADT; Piense en cómo funciona la mayoría de las bases de datos. Como Prolog - Erlang, tienes razón, son idiomas diferentes. Mencioné que uno es funcional mientras que el otro es lógico, pero es mejor si elimino toda la comparación.
m3th0dman

1
@ m3th0dman Una base de datos es solo una tonelada de tuplas / registros. Haskell está un poco lisiado allí, porque carece de registros, pero tiene tuplas, y ML tiene ambos. Y en el caso de Haskell, la cantidad de repeticiones necesarias para declarar un nuevo tipo de datos de pseudo-registro es aún mucho menor de lo que se necesita para crear una estructura falsa en el lenguaje OOP de tipo estático promedio. ¿Puede explicar cómo la mayoría de los datos no se asignan fácilmente a los ADT?
Doval

1
@ m3th0dman Ah, por eso los esquemas de la base de datos se definen en un lenguaje imperativo que se adapta bien a la tarea. Oh, no, eso sería DDL declarativo. De hecho, el proceso general de modelado de datos es relevante para la aplicación que trabajará con él, los flujos de datos y las estructuras, no el lenguaje que implementa la aplicación. A veces, estos están distorsionados para que coincidan con las características OO de un lenguaje y lo que admite su ORM, pero eso generalmente es algo malo, no una característica. Los lenguajes declarativos son más adecuados para expresar el modelo de datos conceptual / lógico.
itsbruce

1
@itsbruce No estaba diciendo que el paradigma de procedimiento sea mejor que el declarativo para definir datos; Estaba diciendo que el paradigma declarativo no es mejor (ni peor) que el de procedimiento (para lenguajes de propósito general). En cuanto a la manipulación de datos, la parte declarativa de SQL no es suficiente para las aplicaciones de la vida real; de lo contrario, nadie habría inventado y usado extensiones de procedimiento. En cuanto al artículo, no estoy de acuerdo con el resumen donde contradice a Brooks; construyó sus ideas a partir de proyectos reales, mientras que esos tipos no construyeron nada sobresaliente para probar su teoría.
m3th0dman

0

Se vería así: {(lo que sea => leer un archivo y llamar a una url) | llamar a una url y leer un archivo} Sin embargo, estas son acciones para ejecutar, y el estado del sistema cambia como resultado, pero eso no es obvio desde el origen.

Los declarativos pueden describir una máquina de estados finitos y sus transiciones. El FSM es como lo opuesto a los declarativos sin acciones, incluso si la única acción es cambiar el estado al siguiente estado.

La ventaja de usar este método es que las transiciones y acciones pueden especificarse mediante predicados que se aplican a múltiples transiciones, en lugar de solo una.

Sé que esto suena un poco extraño, pero en 2008 escribí un generador de programas que usa este método, y el C ++ generado es de 2 a 15 veces más que la fuente. Ahora tengo más de 75,000 líneas de C ++ de 20,000 líneas de entrada. Dos cosas van con esto: consistencia e integridad.

Consistencia: no hay dos predicados que puedan ser verdaderos al mismo tiempo que impliquen acciones inconsistentes, ya que tanto x = 8 como x = 9, ni estados próximos diferentes.

Integridad: se especifica la lógica para cada transición de estado. Estos pueden ser difíciles de verificar para sistemas con N subestados, con> 2 ** N estados, pero existen métodos combinatorios interesantes que pueden verificar todo. En 1962 escribí la fase 1 de un sistema para las máquinas 7070, usando este tipo de generación de código condicional y depuración combinatoria. De las 8,000 líneas en el género, ¡el número de errores desde el día del primer lanzamiento para siempre fue cero!

La fase dos del tipo, 12,000 líneas, tuvo más de 60 errores en los primeros dos meses. Hay mucho más que decir sobre esto, pero funciona. Si los fabricantes de automóviles usaran este método para verificar el firmware, no veríamos las fallas que vemos ahora.


1
Esto realmente no responde la pregunta original. ¿Cómo influyen la coherencia y la integridad en el hecho de que la mayoría de la programación sigue siendo de procedimiento, no declarativa?
Jay Elston

Su primer párrafo parece ser una respuesta a un punto en la respuesta de arnaud programmers.stackexchange.com/a/275839/67057 , no a la pregunta en sí. Debería haber un comentario allí (en mi pantalla, su respuesta ya no está por debajo de la suya). Creo que el resto de su respuesta es una ilustración de cómo una pequeña cantidad de código declarativo podría generar una gran cantidad de código imperativo equivalente, pero no está claro. Su respuesta necesita un poco de orden, particularmente con respecto a los puntos más destacados.
itsbruce

-3

No todo se puede representar de manera declarativa.

A menudo, usted explícitamente desea para controlar el flujo de ejecución

Por ejemplo, siguiendo el pseudocódigo: if whatever read a file call an URL else call an URL write a file ¿Cómo lo representaría declarativamente?

Claro, probablemente hay algunas formas exóticas de hacerlo. Como mónadas . Pero estos suelen ser más engorrosos, complicados y mucho menos intuitivos que su parte procesal.

Todo se reduce al hecho de que "interactuar" con su entorno / sistema no es declarativo. Todo lo relacionado con E / S es procesal por esencia. Tiene que decir exactamente cuándo y qué debe suceder, y en qué orden.

Declarative es excelente para todo lo que está puramente relacionado con la computación. Como una función gigante, pones X y obtienes Y. Eso es genial. Un ejemplo de esto es HTML, la entrada es texto, la salida es lo que ve en su navegador.


2
Yo no compro esto. ¿Por qué su ejemplo no es declarativo? ¿Es el if/ else, en cuyo caso, cómo sería una alternativa declarativa? Ciertamente no son las partes read/ write/ call, ya que son buenas listas declarativas de valores (si estás insinuando que están envueltas {...; ...}, ¿por qué no implica que están envueltas [..., ...]?) Por supuesto, la lista son solo monoides libres; muchos otros lo harían también. No veo por qué las mónadas son relevantes aquí; son solo una API. Haskell pasó de secuencias -> mónadas para ayudar a la composición manual, pero los lenguajes lógicos pueden componer listas en orden automáticamente.
Warbo

2
-1 solo para mónadas. 1. No son realmente exóticos (las listas y los conjuntos son mónadas y todos los usan). 2. No tienen nada que ver con forzar las cosas que hay que hacer en una secuencia específica (Haskell hacer notación miradas imperativas, pero no lo es). Los lenguajes declarativos / funcionales especifican relaciones y dependencias. Si la función X necesita la entrada Y, se generará Y antes que X. Obtenga las dependencias correctas y se definirá la secuencia adecuada de eventos. Mucha interacción es impulsada por eventos , no en una secuencia establecida. Los lenguajes declarativos no dificultan la reacción a los eventos.
itsbruce

La pereza complica algo de eso, pero la pereza no es parte de la definición de lenguajes declarativos, la mayoría de los cuales no la usan. Y en aquellos que lo hacen, la forma de garantizar la evaluación no tiene nada que ver con las mónadas. Para un ejemplo de dónde se usa un lenguaje declarativo exclusivamente para la interacción en lugar de la computación abstracta, no especifica el orden, pero se asegura de que las cosas correctas sucedan en secuencia, no busque más allá de Puppet DSL. Lo que tiene la ventaja de que solo suceden las cosas necesarias, no es algo que los idiomas imprescindibles faciliten.
itsbruce

1
Además del ejemplo @itsbruce, la programación reactiva se considera declarativa y se trata de la interacción con el entorno.
Maciej Piechotka
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.