O sonho da programação declarativa [fechado]


26

Por que o sonho da programação declarativa não foi realizado? Quais são alguns obstáculos concretos no caminho? Para um exemplo simples, por que não posso dizer

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

e automaticamente obtém um algoritmo de classificação. permsignifica permutações e ascsignifica ascender.


4
A propósito, seu exemplo específico já está disponível: gkoberger.github.io/stacksort
Den

3
Você já ouviu falar de Prolog? Basta procurar "Programação do conjunto de respostas". Existem muitos sistemas baseados na lógica padrão.
Schlingel

16
Bem, esta pergunta é facilmente respondida. Tente implementar esse sistema . O que o impediu de fazer isso com sucesso? As probabilidades são boas de que o que quer que tenha parado você parou todos os outros.
Eric Lippert

4
Estou tentado a acreditar que esta pergunta merece mais crédito do que está recebendo. Quando você olha para ela à primeira vista, você pode pensar: Bem, isso é simples! Você precisa programar toda essa lógica por trás disso e os computadores não são tão inteligentes assim. ... Mas então você volta e dá uma segunda olhada nesta pergunta e pensa mais uma vez: Bem, sim, isso é simples, e você precisa programar toda essa lógica - e os computadores não são necessariamente as ferramentas mais precisas no galpão, é verdade - mas há muito mais profundidade nessa explicação do que simplesmente na superfície.
Panzercrisis

3
Sua descrição de um algoritmo de classificação é declarativa, sim, mas com certeza não é eficiente. Existem n!permutações de uma sequência e, na pior das hipóteses, seu algoritmo precisará tentar todas para encontrar uma ordenada. O tempo fatorial é tão ruim quanto um algoritmo para processar uma sequência.
Benjamin Hodgson

Respostas:


8

Existem algumas respostas muito boas. Vou tentar contribuir para a discussão.

Sobre o tema da programação lógica declarativa em Prolog, há o grande livro "The Craft of Prolog", de Richard O'Keefe . Trata-se de escrever programas eficientes usando uma linguagem de programação que permite escrever programas muito ineficientes. Neste livro, ao discutir as implementações eficientes de vários algoritmos (no capítulo "Métodos de programação"), o autor adota a seguinte abordagem:

  • defina o problema em inglês
  • escreva uma solução funcional que seja o mais declarativa possível; normalmente, isso significa exatamente o que você tem na sua pergunta, corrija o Prolog
  • a partir daí, tome medidas para refinar a implementação para torná-la mais rápida

A observação mais esclarecedora (para mim) que pude fazer enquanto trabalhava no caminho:

Sim, a versão final da implementação é muito mais eficiente que a especificação "declarativa" com a qual o autor começou. Ainda é muito declarativo, sucinto e fácil de entender. O que aconteceu no meio é que a solução final captura propriedades do problema ao qual a solução inicial estava alheia.

Em outras palavras, ao implementar uma solução, usamos o máximo de nosso conhecimento sobre o problema possível. Comparar:

Encontre uma permutação de uma lista de forma que todos os elementos estejam em ordem crescente

para:

Mesclar duas listas classificadas resultará em uma lista classificada. Como pode haver sublistas que já estão classificadas, use-as como ponto de partida, em vez de sublistas de comprimento 1.

Um pequeno aparte: uma definição como a que você forneceu é atraente porque é muito geral. No entanto, não posso escapar da sensação de que ela propositalmente ignora o fato de que permutações são, bem, um problema combinatório. Isso é algo que já sabemos ! Isso não é uma crítica, apenas uma observação.

Quanto à verdadeira questão: como avançar? Bem, uma maneira é fornecer o máximo de conhecimento sobre o problema que estamos declarando ao computador.

A melhor tentativa que conheço para realmente resolver o problema é apresentada nos livros de co-autoria de Alexander Stepanov, "Elements of Programming" e "From Mathematics to Generic Programming" . Infelizmente, não estou preparado para resumir (ou mesmo entender completamente) tudo nesses livros. No entanto, a abordagem adotada para definir algoritmos de biblioteca e estruturas de dados eficientes (ou mesmo ótimos), sob a condição de que todas as propriedades relevantes da entrada sejam conhecidas antecipadamente. O resultado final é:

  • Cada transformação bem definida é um refinamento das restrições que já existem (as propriedades conhecidas);
  • Deixamos o computador decidir qual transformação é ideal com base nas restrições existentes.

Quanto ao porquê ainda não aconteceu, bem, a ciência da computação é um campo muito jovem, e ainda estamos lidando com a verdadeira apreciação da novidade da maior parte dele.

PS

Para dar uma idéia do que quero dizer com "refinar a implementação": considere, por exemplo, o problema fácil de obter o último elemento de uma lista, no Prolog. A solução declarativa canônica é dizer:

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

Aqui, o significado declarativo de append/3é:

List1AndList2é a concatenação List1eList2

Como no segundo argumento append/3, temos uma lista com apenas um elemento e o primeiro argumento é ignorado (o sublinhado), obtemos uma divisão da lista original, que descarta a frente da lista ( List1no contexto de append/3) e exige que o verso ( List2no contexto de append/3) é de fato uma lista com apenas um elemento: portanto, é o último elemento.

A implementação real fornecida pelo SWI-Prolog , no entanto, diz:

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

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

Isso ainda é muito declarativo. Leia de cima para baixo, diz:

O último elemento de uma lista só faz sentido para uma lista de pelo menos um elemento. O último elemento para um par de cauda e a cabeça de uma lista é: a cabeça, quando a cauda está vazia, ou o último da cauda não vazia.

O motivo pelo qual essa implementação é fornecida é solucionar as questões práticas que envolvem o modelo de execução do Prolog. Idealmente, não deve fazer diferença qual implementação é usada. Da mesma forma, poderíamos ter dito:

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

O último elemento de uma lista é o primeiro elemento da lista invertida.

Se você deseja obter o preenchimento de discussões inconclusivas sobre o que é bom, o Prolog declarativo, basta passar por algumas das perguntas e respostas na tag Prolog no Stack Overflow .


2
+1 para mostrar como um design declarativo pode progredir de uma simples abstração para uma implementação mais concreta por meio de um processo de design iterativo.
itsbruce

1
@ Boris Esta é uma boa resposta. Esse livro está sentado na minha estante. Provavelmente é hora de abrir.
Davidk01 10/03

1
@ davidk01 Um dos melhores livros por aí. Ele pressupõe que você esteja bastante confortável com o Prolog e a programação em geral, mas a abordagem necessária à programação é pragmática e muito completa.
XXX

2
@ Boris Eu sei que o exemplo não é complexo, mas a produtividade do processo de design iterativo - uma força real das linguagens declarativas - e seu valor muito prático, é crucial. Linguagens declarativas oferecem uma abordagem clara, consistente e recursiva à melhoria iterativa. Linguagens imperativas não.
itsbruce

1
+1 em "obtenha o seu preenchimento de discussões inconclusivas sobre o que é bom, Prolog declarativo" ... é verdade que tendemos a discordar!
Daniel Lyons

50

Idiomas lógicos já fazem isso. Você pode definir a classificação da mesma forma que está fazendo.

O principal problema é o desempenho. Os computadores podem ser ótimos em calcular muitas coisas, mas são inerentemente burros. Toda decisão "inteligente" que um computador pode tomar foi programada por um programador. E essa decisão geralmente é descrita não pela aparência do resultado final, mas por como atingir, passo a passo, esse resultado final.

Imagine a história de um golem . Se você tentar dar a ele um comando abstrato, na melhor das hipóteses, ele fará isso de maneira ineficiente e, na pior das hipóteses, se machucará, você ou outra pessoa. Mas se você descrever o que deseja com o máximo de detalhes possível, terá a garantia de que a tarefa será concluída com eficácia e eficiência.

O trabalho do programador é decidir qual nível de abstração usar. Para o aplicativo que você está criando, você vai subir de nível e descrevê-lo de maneira abstrata e diminuir o desempenho ou ficar baixo e sujo, gastar 10x mais tempo com ele, mas obter um algoritmo 1000x mais eficiente?


6
Talvez seja útil saber que a palavra Golem גולם realmente significa "matéria-prima", ou seja, o estado mais básico em que a máquina / entidade pode estar.
dotancohen

2
Linguagens declarativas não são inerentemente um obstáculo para níveis mais baixos de abstração. Haskell e Standard ML, de maneiras diferentes, permitem que você faça declarações declarativas simples sobre tipos / funções em um único local, forneça uma variedade de implementações de funções concretas e específicas em um local separado e maneiras de corresponder tipos a implementações em outro local. Enquanto isso, a melhor prática nas linguagens OO / Imperative agora é muito sobre iniciar alto / simples e adicionar detalhes de implementação. A diferença é que alta abstração é mais fácil no FP, baixo nível é mais fácil imperativamente.
itsbruce

2
Deve-se dizer que também é possível, em qualquer idioma mencionado, resolver a escolha de implementação específica automaticamente com base nas propriedades do tipo, em vez de correspondências específicas de codificação codificada, o que praticamente fornece o que o OP deseja. No Haskell, as classes de tipos seriam uma ferramenta essencial para isso. No padrão ML, functors.
itsbruce

22
! @BAR Golem = Golum Golem é do folclore judaico
Euphoric

10
Minha resposta para esta resposta é escrever no meu laptop.
Dan J

45

Além do excelente argumento de Euphoric , eu gostaria de acrescentar que já estamos usando linguagens declarativas em muitos lugares onde elas funcionam bem, ou seja, descrevendo o estado que provavelmente não muda ou solicitando algo para o qual o computador realmente pode gerar código eficiente sozinho:

  • O HTML declara qual é o conteúdo de uma página da web.

  • O CSS declara como devem ser os vários tipos de elementos em uma página da web.

  • Todo banco de dados relacional possui uma linguagem de definição de dados que declara qual é a estrutura do banco de dados.

  • O SQL está muito mais próximo de declarativo do que imperativo, já que você diz o que deseja ver e o planejador de consultas do banco de dados descobre como realmente fazer isso acontecer.

  • Alguém poderia argumentar que a maioria dos arquivos de configuração (.vimrc, .profile, .bashrc, .gitconfig) está usando uma linguagem específica de domínio que é amplamente declarativa


3
Mencionarei GNU Make, XSLT, Angular.js como coisas amplamente usadas que também são declarativas (embora angular talvez empurre um pouco a definição).
Mark K Cowan

Deixe-me adicionar expressões regulares a essa lista.
Schwern 9/03/2015

7
As pessoas tendem a esquecer que linguagens declarativas são comuns . Eles não costumam alterar idiomas completos. Adicione regex a essa lista.
Slebetman 9/03/2015

Um pouco pedante, mas ainda assim: nem todo banco de dados possui DDL, pense no grande número de bancos de dados NoSQL sem esquema. Todo banco de dados relacional pode ter, mas nem todo banco de dados.
Reintegrar Monica - dirkk

1
@dkkkk Não tinha pensado nisso. Corrigida minha resposta.
Ixrec 10/03/2015

17

Abstrações estão vazando

Você pode implementar um sistema declarativo em que declara o que deseja, e o compilador ou interpretador descobre uma ordem de execução. O benefício teórico é que ele libera você de pensar no 'como' e não precisa detalhar essa implementação. No entanto, na prática para computação de uso geral, você ainda precisa pensar sobre o 'como' e escrever todos os tipos de truques, mantendo em mente como isso será implementado, pois, caso contrário, o compilador pode (e geralmente escolherá) uma implementação que será muito, muito, muito lento (por exemplo, n! operações onde n seria suficiente).

No seu exemplo em particular, você obterá um algoritmo de classificação A - isso não significa que você obterá um bom ou até um pouco utilizável. Sua definição, se implementada literalmente (como provavelmente seria um compilador), resulta em http://en.wikipedia.org/wiki/Bogosort, que não pode ser usado em conjuntos de dados maiores - é tecnicamente correto, mas precisa de uma eternidade para classificar mil números .

Para alguns domínios limitados, você pode escrever sistemas que quase sempre se saem bem em descobrir uma boa implementação, por exemplo, SQL. Para computação de uso geral que não funciona particularmente bem - você pode escrever sistemas, digamos, em Prolog, mas precisa visualizar como exatamente suas declarações serão convertidas em uma ordem de execução imperativa no final e que perde muito do declarativo esperado benefícios de programação.


Embora o que você diz seja essencialmente verdadeiro, o desempenho ruim não é um sinal de abstração com vazamento, a menos que a interface / contrato forneça garantias, por exemplo, sobre o tempo de execução.
valenterry

3
Peters não está dizendo que o mau desempenho é um sinal de abstração com vazamento, @valenterry. Pelo contrário, ele está dizendo o contrário: para obter um bom desempenho, os detalhes da implementação são forçados a vazar.
itsbruce

2
Eu acho enganoso dizer que as abstrações estão vazando apenas porque você precisa entender a implementação para entender como isso afeta o desempenho. O objetivo de uma abstração não é protegê-lo de ter que pensar em desempenho.
Doval

1
@jamesqf Na programação declarativa, você indicaria apenas que algo está classificado. Você pode declarar que a ordem de classificação está vinculada a alguma variável / propriedade. E então seria assim. Não é necessário chamar explicitamente a classificação toda vez que novos dados são adicionados ou a ordem de classificação é alterada.
Hyde

1
@jamesqf Você realmente não consegue entender o ponto sem realmente tentar (recomendo o QML do Qt para brincar com idéias declarativas). Imagine alguém que apenas conhece a programação imperativa e tente entender o ponto de OOP ou a programação funcional sem realmente tentar de verdade.
Hyde

11

A decidibilidade computacional é a razão mais importante pela qual a programação declarativa não se mostrou tão fácil quanto parece.

Muitos problemas que são relativamente fáceis de declarar provaram ser indecidíveis ou têm complexidade de NP completa para resolver. Isso geralmente ocorre quando levamos em consideração classes e classificações negativas, contabilidade e recursão.

Eu gostaria de explicar isso com alguns domínios bem conhecidos.

A decisão sobre qual classe CSS usar precisa de conhecimento e consideração de todas as regras CSS. Adicionar novas regras pode invalidar todas as outras decisões. Classes negativas de CSS não são intencionalmente adicionadas à linguagem, devido a problemas de NP-completos, mas a falta de classes negativas complica as decisões de design de CSS.

Dentro de um otimizador de consultas (SQL), existe o problema incômodo de decidir em qual ordem ingressar, quais índices usar e qual memória alocar para quais resultados temporários. Esse é um problema NP-completo conhecido e complica o design do banco de dados e a formulação de consultas. Para formulá-lo de maneira diferente: ao projetar um banco de dados ou uma consulta, o designer precisa conhecer as ações e a ordem das ações que o otimizador de consultas provavelmente executará. Um engenheiro experiente precisa ter conhecimento das heurísticas usadas pelos principais fornecedores de banco de dados.

Os arquivos de configuração são declarativos, mas é difícil declarar certas configurações. Por exemplo, para configurar corretamente os recursos, é necessário levar em consideração a versão, implantação (e histórico de implantação), possíveis substituições manuais e possíveis conflitos com outras configurações. Validar corretamente uma configuração pode se tornar um problema NP-completo.

O resultado é que essas complicações pegam os iniciantes de surpresa, quebram a 'beleza' da programação declarativa e fazem com que alguns engenheiros procurem outras soluções. A migração de engenheiros inexperientes do SQL para o NoSQL pode ter sido desencadeada pelas complexidades subjacentes dos bancos de dados relacionais.


2
"Classes CSS negativas não são intencionalmente adicionadas à linguagem, por causa de problemas NP-completos" - você pode elaborar?
John Dvorak

É um pouco de exercício, mas com seletores CSS negativos, é possível reescrevê-los para um problema 3SAT (com a última cláusula sendo o DOM), o que exigiria tentar todas as combinações possíveis para ver se elas coincidem.
Dibbeke

1
Pequena adição. Nas CSS 3 e 4, seletores negativos são permitidos, mas: pseudo-classes não podem ser aninhadas.
Dibbeke

2

Temos uma diferença na declaratividade das linguagens de programação que é bem utilizada na verificação da lógica digital.

Normalmente, a lógica digital é descrita no nível de transferência de registro (RTL), onde é definido o nível lógico dos sinais entre os registros. Para verificar se estamos adicionando cada vez mais propriedades definidas de maneira mais abstrata e declarativa.

Um dos subconjuntos de idiomas / idiomas mais declarativos é chamado PSL para Property Specification Language. Ao testar um modelo RTL de um multiplicador no qual, por exemplo, todas as operações lógicas de troca e adição de múltiplos ciclos de clock precisam ser especificadas; você pode escrever uma propriedade da maneira de assert that when enable is high, this output will equal the multiplication of these two inputs after no more than 8 clock cycles. A descrição da PSL pode então ser verificada junto com a RTL em uma simulação, ou a PSL pode ser formalmente comprovada como válida para a descrição da RTL.

O modelo PSL mais declarativo obriga a descrever o mesmo comportamento que a descrição da RTL, mas de uma maneira suficientemente diferente que pode ser verificada automaticamente na RTL para verificar se eles concordam.


1

Principalmente o problema é como você modela os dados; e programação declarativa não está ajudando aqui. Em linguagens imperativas, você já possui muitas bibliotecas que fazem muitas coisas para você, portanto, você só precisa saber como chamar. De uma maneira particular, pode-se considerar essa programação declarativa (provavelmente o melhor exemplo disso é a API Stream no Java 8 ). Com isso, a abstração já está resolvida e a programação declarativa não é necessária.

Além disso, como foi dito, as linguagens de programação lógica já fazem exatamente o que você deseja. Pode-se dizer que o problema é desempenho, mas com o hardware e as pesquisas atuais nessa área, é possível melhorar as coisas para estar pronto para o uso em produção; na verdade, o Prolog é usado para coisas de IA, mas acredito apenas na academia.

Note-se que se aplica a linguagens de programação de uso geral. Para idiomas específicos do domínio, os idiomas declarativos são muito melhores; SQL provavelmente é o melhor exemplo.


3
Modelagem de dados? Você escolheu a coisa em que os imperativos são piores. Linguagens funcionais declarativas como Haskell e ML se destacam na modelagem de dados. Tipos de dados algébricos e tipos de dados recursivos, por exemplo, geralmente podem ser definidos de forma abrangente em uma ou duas linhas. Claro, você ainda tem as funções para escrever, mas seu código segue inexoravelmente da definição de tipo e é limitado por ela. Comparação bizarra para fazer.
itsbruce

1
@itsbruce A coisa é que os dados mais reais não são facilmente mapeados para o ADT; pense em como a maioria dos bancos de dados funciona. Como Prolog-Erlang, você está certo, eles são idiomas diferentes. Eu mencionei que um é funcional enquanto o outro é lógico, mas é melhor se eu excluir toda a comparação.
M3th0dman 9/03/2015

1
@ m3th0dman Um banco de dados é apenas uma tonelada de tuplas / registros. Haskell está um pouco aleijado, porque não possui registros, mas possui tuplas e ML tem os dois. E, no caso de Haskell, a quantidade de clichê necessária para declarar um novo tipo de dados de pseudo-registro ainda é muito menor do que o necessário para criar uma estrutura falsa na linguagem OOP média de tipo estatístico. Você pode explicar como a maioria dos dados não é facilmente mapeada para ADTs?
Doval

1
@ m3th0dman Ah, é por isso que os esquemas de banco de dados são definidos em uma linguagem imperativa e adequada para a tarefa. Ah, não, isso seria DDLs declarativos. De fato, o processo geral de modelagem de dados é relevante para o aplicativo que irá trabalhar com ele, os fluxos e estruturas de dados, e não o idioma que implementa o aplicativo. Às vezes, elas são distorcidas para corresponder aos recursos OO de um idioma e ao que o ORM suporta, mas isso geralmente é uma coisa ruim, não um recurso. Linguagens declarativas são mais adequadas para expressar o modelo de dados conceitual / lógico.
itsbruce

1
@itsbruce Eu não estava dizendo que o paradigma processual é melhor que o declarativo na definição de dados; Eu estava dizendo que o paradigma declarativo não é melhor (nem pior) do que o processual (para linguagens de uso geral). Quanto à manipulação de dados, a parte declarativa do SQL não é suficiente para aplicativos da vida real; caso contrário, ninguém teria inventado e usado extensões processuais. Quanto ao artigo, discordo do resumo onde contradiz Brooks; ele construiu suas idéias a partir de projetos reais, enquanto esses caras não construíam nada de extraordinário para provar sua teoria.
M3th0dman 10/03/2015

0

Seria algo parecido com isto .. {(qualquer que seja => leia um arquivo e chame um url) | chamar uma url e ler um arquivo} No entanto, essas são ações a serem executadas e o estado do sistema muda como resultado, mas isso não é óbvio a partir da fonte.

Declarativos podem descrever uma máquina de estados finitos e suas transições. O FSM é como o oposto de declarativos sem ações, mesmo que a única ação seja alterar o estado para o próximo estado.

A vantagem de usar esse método é que transições e ações podem ser especificadas por predicados que se aplicam a várias transições, em vez de apenas uma.

Eu sei que isso parece um pouco estranho, mas em 2008 eu escrevi um gerador de programa que usa esse método, e o C ++ gerado é de 2 a 15 vezes mais que a fonte. Agora tenho mais de 75.000 linhas de C ++ a partir de 20.000 linhas de entrada. Duas coisas vão com isso: consistência e integridade.

Consistência: Não há dois predicados que possam ser verdadeiros ao mesmo tempo, que podem implicar ações inconsistentes, como x = 8 ex = 9, nem diferentes estados seguintes.

Completude: a lógica para cada transição de estado é especificada. Pode ser difícil verificar se há sistemas com N subestados, com> 2 ** N estados, mas existem métodos combinatórios interessantes que podem verificar tudo. Em 1962, escrevi a fase 1 de um tipo de sistema para as máquinas 7070, usando esse tipo de geração de código condicional e depuração combinatória. Das 8.000 linhas do tipo, o número de bugs desde o dia do primeiro lançamento para sempre foi zero!

A segunda fase do tipo, 12.000 linhas, teve mais de 60 erros nos primeiros dois meses. Há muito mais a dizer sobre isso, mas funciona. Se os fabricantes de automóveis usassem esse método para verificar o firmware, não veríamos as falhas que vemos agora.


1
Isso realmente não responde à pergunta original. Como a consistência e a integridade levam em consideração o fato de que a maioria da programação ainda é processual, não declarativa?
Jay Elston

Seu parágrafo de abertura parece ser uma resposta para um ponto da resposta de arnaud programmers.stackexchange.com/a/275839/67057 , não para a pergunta em si. Deve haver um comentário lá (na minha tela, sua resposta não está mais abaixo da dele, por um motivo). Penso que o resto da sua resposta é uma ilustração de como uma pequena quantidade de código declarativo poderia gerar uma grande quantidade de código imperativo equivalente, mas não está claro. Sua resposta precisa de um pouco de arrumação, principalmente no que diz respeito aos pontos mais importantes.
itsbruce

-3

Nem tudo pode ser representado de maneira declarativa.

Freqüentemente, você deseja controlar explicitamente o fluxo de execução

Por exemplo, após o pseudocódigo: if whatever read a file call an URL else call an URL write a file como você o representaria declarativamente?

Claro, provavelmente existem algumas maneiras exóticas de fazer isso. Como mônadas . Mas estes geralmente são mais pesados, complicados e muito menos intuitivos do que a parte processual.

Tudo se resume ao fato de que "interagir" com seu ambiente / sistema não é declarativo. Tudo relacionado a E / S é processual por essência. Você precisa dizer exatamente quando e o que deve acontecer e em que ordem.

Declarativo é ótimo para tudo que está puramente relacionado à computação. Como uma função gigante, você coloca X e obtém Y. Isso é ótimo. Um exemplo disso é HTML, a entrada é texto, a saída é o que você vê no seu navegador.


2
Eu não compro isso. Por que seu exemplo não é declarativo? É o if/ else, caso em que seria uma alternativa declarativa? Certamente não são as partes read/ write/ call, já que são boas listas declarativas de valores (se você está implicando que estão envolvidas {...; ...}, por que não implica que estão envolvidas [..., ...]?) É claro que a lista são apenas monoides gratuitos; muitos outros fariam também. Não vejo por que as mônadas são relevantes aqui; eles são apenas uma API. Haskell passou de fluxos -> mônadas para ajudar na composição manual, mas as linguagens lógicas podem compor listas em ordem automaticamente.
Warbo 10/03/2015

2
-1 apenas para Mônadas. 1. Eles não são realmente exóticos (listas e conjuntos são mônadas e todo mundo usa). 2. Eles não têm nada a ver com forçar coisas a serem feitas em uma seqüência específica (Haskell fazer notação olhares imperativo mas não é). Linguagens declarativas / funcionais especificam relacionamentos e dependências. Se a função X precisar da entrada Y, Y será gerado antes de X. Acerte as dependências e a sequência apropriada de eventos se definirá. Muita interação é orientada a eventos , não em uma sequência definida. Linguagens declarativas não dificultam a reação a eventos.
itsbruce

A preguiça complica parte disso, mas a preguiça não faz parte da definição de linguagens declarativas, a maioria das quais não a utiliza. E naqueles que o fazem, o caminho para garantir a avaliação não tem nada a ver com mônadas. Para um exemplo de onde uma linguagem declarativa é usada exclusivamente para interação em vez de computação abstrata, não especifica ordem, mas garante que as coisas certas aconteçam em sequência, não procure mais, o Puppet DSL. Que tem o bônus de que apenas as coisas necessárias acontecem - e não algo que as linguagens imperativas facilitam.
itsbruce

1
Além do exemplo @itsbruce, a programação reativa é considerada declarativa e trata da interação com o ambiente.
Maciej Piechotka 10/03/2015
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.