Il sogno della programmazione dichiarativa [chiuso]


26

Perché il sogno della programmazione dichiarativa non è stato realizzato? Quali sono alcuni ostacoli concreti che si frappongono? Per un semplice esempio, perché non posso dirlo

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

e ne ricava automaticamente un algoritmo di ordinamento. permsignifica permutazioni e ascsignifica ascendente.


4
A proposito, il tuo esempio specifico è già disponibile: gkoberger.github.io/stacksort
Den

3
Hai sentito parlare di Prolog? Cerca "Programmazione set di risposte". Esistono molti sistemi basati sulla logica predefinita.
schlingel,

16
Bene, a questa domanda è facile rispondere. Tentativo di implementare un tale sistema . Cosa ti ha impedito di farlo con successo? Le probabilità sono buone che qualunque cosa ti fermi ha fermato tutti gli altri.
Eric Lippert,

4
Sono tentato di credere che questa domanda meriti più credito di quanto non stia ottenendo. Quando lo guardi a prima vista, potresti pensare: Beh, è ​​semplice! Devi programmare tutta quella logica dietro di essa e i computer non sono così intelligenti. ... Ma poi torni a dare una seconda occhiata a questa domanda e pensi ancora una volta, beh, sì, è semplice e devi programmare tutta quella logica - ei computer non sono necessariamente gli strumenti più nitidi nella tettoia, vero - ma c'è molta più profondità in quella spiegazione di ciò che sta semplicemente in superficie.
Panzercrisis,

3
La tua descrizione di un algoritmo di ordinamento è dichiarativa, sì, ma sicuramente non è efficiente. Esistono n!permutazioni di una sequenza e nel peggiore dei casi il tuo algoritmo dovrà provarle tutte per trovarne una ordinata. Il tempo fattoriale è tanto grave quanto può fare un algoritmo per elaborare una sequenza.
Benjamin Hodgson,

Risposte:


8

Ci sono alcune risposte molto buone. Proverò a contribuire alla discussione.

Sul tema della programmazione dichiarativa e logica in Prolog, c'è il grande libro "The Craft of Prolog" di Richard O'Keefe . Si tratta di scrivere programmi efficienti utilizzando un linguaggio di programmazione che consente di scrivere programmi molto inefficienti. In questo libro, mentre discute le implementazioni efficienti di numerosi algoritmi (nel capitolo "Metodi di programmazione"), l'autore adotta il seguente approccio:

  • definire il problema in inglese
  • scrivere una soluzione funzionante il più dichiarativa possibile; di solito, questo significa praticamente esattamente quello che hai nella tua domanda, solo Prolog corretto
  • da lì, prendere provvedimenti per perfezionare l'implementazione per renderla più veloce

L'osservazione più illuminante (per me) che sono stato in grado di fare mentre mi facevo strada attraverso questi:

Sì, la versione finale dell'implementazione è molto più efficiente della specifica "dichiarativa" con cui l'autore ha iniziato. È ancora molto dichiarativo, conciso e di facile comprensione. Ciò che è accaduto nel mezzo è che la soluzione finale cattura le proprietà del problema a cui la soluzione iniziale era ignara.

In altre parole, durante l'implementazione di una soluzione, abbiamo utilizzato quante più conoscenze possibili sul problema. Confrontare:

Trova una permutazione di un elenco in modo tale che tutti gli elementi siano in ordine crescente

a:

L'unione di due elenchi ordinati comporterà un elenco ordinato. Poiché potrebbero essere presenti elenchi già ordinati, utilizzarli come punto di partenza, anziché elenchi di lunghezza 1.

Un piccolo inconveniente: una definizione come quella che hai dato è attraente perché è molto generale. Tuttavia, non posso sfuggire alla sensazione che ignori intenzionalmente il fatto che le permutazioni sono, beh, un problema combinatorio. Questo è qualcosa che già sappiamo ! Questa non è una critica, solo un'osservazione.

Per quanto riguarda la vera domanda: come andare avanti? Bene, un modo è quello di fornire la stessa conoscenza del problema che stiamo dichiarando al computer.

Il miglior tentativo che conosco per risolvere davvero il problema è presentato nei libri scritti da Alexander Stepanov, "Elementi di programmazione" e "Dalla matematica alla programmazione generica" . Purtroppo non sono all'altezza del compito di riassumere (o persino comprendere appieno) tutto in questi libri. Tuttavia, l'approccio è quello di definire algoritmi e strutture di dati efficienti (o addirittura ottimali), a condizione che tutte le proprietà pertinenti dell'input siano note in anticipo. Il risultato finale è:

  • Ogni trasformazione ben definita è un affinamento dei vincoli che sono già in atto (le proprietà conosciute);
  • Lasciamo che il computer decida quale trasformazione è ottimale in base ai vincoli esistenti.

Per quanto riguarda il motivo per cui non è ancora del tutto accaduto, beh, l'informatica è un campo davvero giovane e stiamo ancora affrontando apprezzando davvero la novità di gran parte di esso.

PS

Per darti un'idea di cosa intendo "perfezionando l'implementazione": prendi ad esempio il semplice problema di ottenere l'ultimo elemento di un elenco, in Prolog. La soluzione dichiarativa canonica è quella di dire:

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

Qui, il significato dichiarativo di append/3è:

List1AndList2è la concatenazione di List1eList2

Dato che nel secondo argomento append/3abbiamo una lista con un solo elemento, e il primo argomento viene ignorato (il trattino basso), otteniamo una divisione della lista originale che scarta il fronte della lista ( List1nel contesto di append/3) e richiede che il retro ( List2nel contesto di append/3) è davvero un elenco con un solo elemento: quindi, è l'ultimo elemento.

L' attuale implementazione fornita da SWI-Prolog , tuttavia, dice:

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

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

Questo è ancora ben dichiarativo. Leggi dall'alto verso il basso, dice:

L'ultimo elemento di un elenco ha senso solo per un elenco di almeno un elemento. L'ultimo elemento per una coppia di coda e la testa di un elenco, quindi, è: la testa, quando la coda è vuota, o l'ultimo della coda non vuota.

Il motivo per cui viene fornita questa implementazione è aggirare le questioni pratiche che circondano il modello esecutivo di Prolog. Idealmente, non dovrebbe fare la differenza sull'implementazione utilizzata. Allo stesso modo, avremmo potuto dire:

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

L'ultimo elemento di un elenco è il primo elemento dell'elenco invertito.

Se vuoi fare il pieno di discussioni inconcludenti su ciò che è buono, Prolog dichiarativo, basta passare attraverso alcune delle domande e risposte nel tag Prolog su Stack Overflow .


2
+1 per mostrare come un progetto dichiarativo può passare da una semplice astrazione a un'implementazione più concreta attraverso un processo di progettazione iterativo.
itsbruce

1
@Boris Questa è una buona risposta. Quel libro è seduto sulla mia libreria. Probabilmente è ora che l'ho aperto.
davidk01,

1
@ davidk01 Uno dei libri migliori là fuori. Presuppone che tu sia abbastanza a tuo agio con Prolog e con la programmazione in generale, ma l'approccio che prende alla programmazione è sia pragmatico che molto approfondito.
XXX

2
@Boris So che l'esempio non è complesso ma la produttività del processo di progettazione iterativa - un vero punto di forza dei linguaggi dichiarativi - ed è un valore molto pratico, è cruciale. Le lingue dichiarative offrono un approccio chiaro, coerente e ricorsivo al miglioramento iterativo. Le lingue imperative no.
itsbruce

1
+1 per "riempirsi di discussioni inconcludenti su ciò che è buono, Prolog dichiarativo" ... molto vero che tendiamo a non essere d'accordo!
Daniel Lyons,

50

I linguaggi logici lo fanno già. Puoi definire l'ordinamento in modo simile come lo stai facendo.

Il problema principale è la prestazione. I computer potrebbero essere bravi a calcolare un sacco di cose, ma sono intrinsecamente stupidi. Ogni decisione "intelligente" che un computer potrebbe prendere è stata programmata da un programmatore. E questa decisione viene generalmente descritta non da come appare il risultato finale, ma da come raggiungere, passo dopo passo, questo risultato finale.

Immagina la storia di un Golem . Se provi a dargli un comando astratto, nella migliore delle ipotesi lo farà in modo inefficiente e nella peggiore delle ipotesi, farà male a te stesso, a te o a qualcun altro. Ma se descrivi ciò che desideri nel modo più dettagliato possibile, hai la certezza che l'attività verrà completata in modo efficace ed efficiente.

È compito del programmatore decidere quale livello di astrazione usare. Per l'applicazione che stai realizzando, stai andando ad alto livello e descriverlo in modo astratto e prendere il colpo di prestazione o andare basso e sporco, passare 10 volte più tempo su di esso, ma ottenere un algoritmo che è 1000 volte più performante?


6
Potrebbe essere utile sapere che la parola Golem גולם in realtà significa "materia prima", ovvero lo stato più elementare in cui la macchina / entità può trovarsi.
dotancohen

2
Le lingue dichiarative non sono intrinsecamente un ostacolo a livelli più bassi di astrazione. Haskell e Standard ML entrambi, nei loro diversi modi, consentono di fare semplici dichiarazioni dichiarative su tipi / funzioni in un posto, fornire una gamma di implementazioni di funzioni concrete e specifiche in un posto separato e modi per abbinare i tipi alle implementazioni in un altro ancora. Nel frattempo, le migliori pratiche nei linguaggi OO / Imperative riguardano molto l'avvio iniziale / semplice e l'aggiunta di dettagli di implementazione. La differenza è che l'alta astrazione è più facile in FP, il basso livello è più semplice in modo imperativo.
itsbruce

2
Dovrei dire che è anche possibile, in entrambe le lingue menzionate, risolvere automaticamente la scelta di un'implementazione specifica in base alle proprietà del tipo piuttosto che a corrispondenze specifiche codificate, che offrono praticamente ciò che l'OP vuole. In Haskell, le classi di tipi sarebbero uno strumento chiave per questo. In ML standard, funzione.
itsbruce

22
@BAR Golem! = Golum Golem è del folklore ebraico
Euforico

10
Il mio asporto da questa risposta è di scrivere in italiano sul mio laptop.
Dan J

45

Oltre all'eccellente punto di Euphoric , vorrei aggiungere che stiamo già utilizzando linguaggi dichiarativi in ​​molti luoghi in cui funzionano bene, vale a dire descrivendo lo stato che non è probabile che cambi o richieda qualcosa per cui il computer può effettivamente generare codice efficiente da solo:

  • HTML dichiara qual è il contenuto di una pagina Web.

  • Il CSS dichiara come dovrebbero apparire i vari tipi di elementi in una pagina web.

  • Ogni database relazionale ha un linguaggio di definizione dei dati che dichiara quale sia la struttura del database.

  • SQL è molto più vicino a dichiarativo che a imperativo, dal momento che dici quello che vuoi vedere e il pianificatore di query del database capisce come farlo realmente accadere.

  • Si potrebbe sostenere che la maggior parte dei file di configurazione (.vimrc, .profile, .bashrc, .gitconfig) utilizzano un linguaggio specifico del dominio ampiamente dichiarativo


3
Citerò GNU Make, XSLT, Angular.js come cose ampiamente usate che sono anche dichiarative (sebbene angolare forse spinga un po 'la definizione).
Mark K Cowan,

Vorrei aggiungere espressioni regolari a quell'elenco.
Schwern,

7
Le persone tendono a dimenticare che le lingue dichiarative sono comuni . In genere non stanno turing lingue complete. Aggiungi regex all'elenco.
Slebetman,

Un po 'pedante, ma comunque: non tutti i database hanno un DDL, basti pensare all'enorme numero di database NoSQL senza schemi. Ogni database relazionale potrebbe avere, ma non tutti i database.
Ripristina Monica - Dirkk,

1
@dirkk Non ci avevo pensato. Ho corretto la mia risposta.
Ixrec,

17

Le astrazioni perdono

È possibile implementare un sistema dichiarativo in cui si dichiara ciò che si desidera e il compilatore o l'interprete stabilisce un ordine di esecuzione. Il vantaggio teorico è che ti libera dal dover pensare al "come" e non devi dettagliare questa implementazione. Tuttavia, in pratica per il calcolo per scopi generali devi ancora pensare al "come" e scrivere tutti i tipi di trucchi tenendo presente come questo verrà implementato, poiché altrimenti il ​​compilatore può (e spesso sceglierà) un'implementazione che sarà molto, molto, molto lento (es. n! operazioni in cui n sarebbe sufficiente).

Nel tuo esempio particolare, otterrai un algoritmo di ordinamento - non significa che ne otterrai uno buono o addirittura un po 'utilizzabile. La tua definizione data, se implementata letteralmente (come probabilmente farebbe un compilatore) si tradurrà in http://en.wikipedia.org/wiki/Bogosort che è inutilizzabile per set di dati più grandi - è tecnicamente corretto, ma ha bisogno di un'eternità per ordinare mille numeri .

Per alcuni domini limitati puoi scrivere sistemi che quasi sempre fanno bene a capire una buona implementazione, ad esempio SQL. Per il calcolo per scopi generici che non funziona particolarmente bene - puoi scrivere sistemi in, diciamo, Prolog ma devi visualizzare come alla fine le tue dichiarazioni verranno convertite in un ordine di esecuzione imperativo e che perde gran parte del dichiarativo atteso vantaggi di programmazione.


Mentre ciò che dici è essenzialmente vero, le cattive prestazioni non sono un segno di un'astrazione che perde, a meno che l'interfaccia / contratto non ti fornisca garanzie, ad esempio sui tempi di esecuzione.
valenterry,

3
Peters non sta dicendo che le cattive prestazioni siano un segno di astrazione che perde, @valenterry. Semmai, sta dicendo il contrario: che per ottenere buone prestazioni, i dettagli di implementazione sono costretti a perdere.
itsbruce

2
Penso che sia fuorviante affermare che le astrazioni perdono solo perché è necessario comprendere l'implementazione per capire come influisce sulle prestazioni. Lo scopo di un'astrazione non è quello di proteggerti dal dover pensare alla performance.
Doval

1
@jamesqf Nella programmazione dichiarativa, dovresti semplicemente dire che qualcosa è ordinato. È possibile dichiarare che l'ordinamento è associato ad alcune variabili / proprietà. E poi sarebbe così. Non è necessario chiamare in modo esplicito l'ordinamento ogni volta che vengono aggiunti nuovi dati o le modifiche all'ordinamento.
hyde,

1
@jamesqf Non puoi davvero vedere il punto senza provarci (raccomanderei QML di Qt per giocare con idee dichiarative). Immagina qualcuno che conosca solo la programmazione imperativa e che cerchi di capire il punto di OOP o la programmazione funzionale senza provarlo davvero.
hyde,

11

La decidibilità computazionale è il motivo più importante per cui la programmazione dichiarativa non si è dimostrata facile come sembra.

Molti problemi relativamente facili da affermare si sono rivelati indecidibili o hanno una complessità NP-completa da risolvere. Ciò si verifica spesso quando prendiamo in considerazione le classi e la classificazione, la numerabilità e la ricorsione negative.

Vorrei chiarire questo con alcuni domini che sono ben noti.

La decisione sulla classe CSS da utilizzare richiede conoscenza e considerazione di tutte le regole CSS. L'aggiunta di nuove regole potrebbe invalidare tutte le altre decisioni. Le classi CSS negative non vengono aggiunte intenzionalmente al linguaggio, a causa di problemi NP-completi, ma la mancanza di classi negative complica le decisioni di progettazione CSS.

All'interno di un Query Optimizer (SQL), c'è il fastidioso problema di decidere in quale ordine unire, quali indici usare e quale memoria assegnare a quali risultati temporanei. Questo è un problema noto NP completo e complica la progettazione del database e la formulazione di query. Per formularlo in modo diverso: quando si progetta un database o una query, il progettista deve conoscere le azioni e l'ordine delle azioni che è probabile che l'ottimizzatore di query esegua. Un ingegnere esperto necessita della conoscenza dell'euristica utilizzata dai principali fornitori di database.

I file di configurazione sono dichiarativi, ma alcune configurazioni sono difficili da dichiarare. Ad esempio, per configurare correttamente le funzionalità è necessario tenere conto del controllo delle versioni, della distribuzione (e della cronologia delle distribuzioni), delle possibili sostituzioni manuali e dei possibili conflitti con altre impostazioni. Convalidare correttamente una configurazione potrebbe diventare un problema NP-completo.

Il risultato è che queste complicazioni sorprendono i principianti, spezzano la "bellezza" della programmazione dichiarativa e fanno sì che alcuni ingegneri cerchino altre soluzioni. La migrazione di ingegneri inesperti da SQL a NoSQL potrebbe essere stata innescata dalle complessità sottostanti dei database relazionali.


2
"Le classi CSS negative non vengono aggiunte intenzionalmente al linguaggio, a causa di problemi NP-completi" - puoi elaborare?
John Dvorak,

È un po 'un esercizio, ma con selettori CSS negativi è possibile riscriverli a un problema 3SAT (con l'ultima clausola che è il DOM), che richiederebbe di provare tutte le possibili combinazioni, per vedere se corrisponde.
Dibbeke,

1
Piccola aggiunta. Nei CSS 3 e 4, i selettori negativi sono consentiti, ma: non le pseudo-classi potrebbero non essere nidificate.
Dibbeke,

2

Abbiamo una differenza nella dichiaratività dei linguaggi di programmazione che viene messa a frutto nella verifica della logica digitale.

Normalmente la logica digitale è descritta a livello di trasferimento dei registri (RTL) dove è definito il livello logico dei segnali tra i registri. Per verificare che stiamo aggiungendo sempre più proprietà definite in modo più astratto e dichiarativo.

Uno dei sottoinsiemi linguistici / linguistici più dichiarativi è chiamato PSL per Language Specification Property. Quando si verifica un modello RTL di un moltiplicatore in cui, ad esempio, è necessario specificare tutte le operazioni logiche di spostamento e aggiunta su più cicli di clock; puoi scrivere una proprietà nel modo di assert that when enable is high, this output will equal the multiplication of these two inputs after no more than 8 clock cycles. La descrizione del PSL può quindi essere controllata insieme al RTL in una simulazione, oppure si può dimostrare formalmente al PSL di essere valido per la descrizione del RTL.

Il modello PSL più dichiarativo obbliga a descrivere lo stesso comportamento della descrizione RTL, ma in un modo sufficientemente diverso che può essere verificato automaticamente contro RTL per vedere se sono d'accordo.


1

Principalmente il problema è come modellare i dati; e la programmazione dichiarativa non aiuta qui. Nelle lingue imperative hai già tonnellate di librerie che fanno un sacco di cose per te, quindi devi solo sapere come chiamare. In un modo particolare si potrebbe considerare questa programmazione dichiarativa (probabilmente il miglior esempio per questo è Stream API in Java 8 ). Detto questo, l'astrazione è già stata risolta e la programmazione dichiarativa non è necessaria.

Inoltre, come è stato detto, i linguaggi di programmazione logica fanno già esattamente quello che vuoi. Si potrebbe dire che il problema è rappresentato dalle prestazioni, ma con l'hardware e la ricerca odierni in questo settore, le cose possono essere migliorate per essere pronte per l'uso in produzione; in realtà Prolog è usato per l'intelligenza artificiale, ma credo solo dal mondo accademico.

Va notato che si applica ai linguaggi di programmazione generici. Per le lingue specifiche del dominio, le lingue dichiarative sono molto migliori; SQL è probabilmente l'esempio migliore.


3
Modellazione dei dati? Hai scelto gli imperativi peggiori. I linguaggi funzionali dichiarativi come Haskell e ML eccellono nella modellazione dei dati. I tipi di dati algebrici e i tipi di dati ricorsivi, ad esempio, possono di solito essere definiti in modo completo in una o due righe. Certo, hai ancora le funzioni da scrivere ma il loro codice segue inesorabilmente dalla definizione del tipo e ne è vincolato. Bizzarro confronto da fare.
itsbruce

1
@itsbruce Il fatto è che la maggior parte dei dati reali non è facilmente mappabile su ADT; pensa a come funzionano la maggior parte dei database. Come Prolog - Erlang, hai ragione, sono lingue diverse. Ho detto che uno è funzionale mentre l'altro è logico ma è meglio se elimino l'intero confronto.
m3th0dman

1
@ m3th0dman Un database è solo una tonnellata di tuple / record. Haskell è un po 'paralizzato lì, perché manca di record, ma ha tuple e ML ha entrambe. E nel caso di Haskell la quantità di boilerplate necessaria per dichiarare un nuovo tipo di dati pseudo-record è ancora molto più piccola di quella necessaria per creare una falsa struttura nel linguaggio medio OOP tipicamente statico. Puoi approfondire come la maggior parte dei dati non è facilmente mappabile agli ADT?
Doval

1
@ m3th0dman Ah, ecco perché gli schemi di database sono definiti in un linguaggio imperativo che ben si adatta al compito. Oh, no, sarebbero DDL dichiarativi. In effetti, il processo generale di modellizzazione dei dati è rilevante per l' applicazione che funzionerà con essa, i flussi e le strutture di dati, non la lingua che implementa l'applicazione. A volte questi sono distorti per abbinare le funzionalità OO di una lingua e ciò che supporta il suo ORM, ma di solito è una cosa negativa, non una funzionalità. I linguaggi dichiarativi sono più adatti per esprimere il modello di dati concettuali / logici.
itsbruce

1
@itsbruce Non stavo dicendo che il paradigma procedurale è migliore di quello dichiarativo nel definire i dati; Stavo dicendo che il paradigma dichiarativo non è migliore (né peggiore) di quello procedurale (per le lingue di uso generale). Per quanto riguarda la manipolazione dei dati, la parte dichiarativa di SQL non è sufficiente per le applicazioni della vita reale; altrimenti nessuno avrebbe inventato e usato estensioni procedurali. Per quanto riguarda l'articolo, non sono d'accordo con l'astratto in cui contraddice Brooks; ha costruito le sue idee da progetti reali mentre quei ragazzi non hanno costruito nulla di eccezionale per dimostrare la loro teoria.
m3th0dman,

0

Sarebbe simile a questo .. {(whatever => leggi un file e chiama un url) | chiama un URL e leggi un file} Tuttavia, si tratta di azioni da eseguire e, di conseguenza, lo stato del sistema cambia, ma ciò non è ovvio dall'origine.

I dichiaranti possono descrivere una macchina a stati finiti e le sue transizioni. L'FSM è come l'opposto dei dichiarativi senza azioni, anche se l'unica azione è cambiare lo stato nello stato successivo.

Il vantaggio di utilizzare questo metodo è che le transizioni e le azioni possono essere specificate dai predicati che si applicano a più transizioni, piuttosto che a una sola.

So che sembra un po 'strano, ma nel 2008 ho scritto un generatore di programmi che utilizza questo metodo e il C ++ generato è da 2 a 15 volte più della fonte. Ora ho oltre 75.000 righe di C ++ da 20.000 righe di input. Due cose vanno con questo: coerenza e completezza.

Coerenza: non esistono due predicati che possono essere veri allo stesso tempo e possono implicare azioni incoerenti, in quanto sia x = 8 che x = 9, né diversi stati successivi.

Completezza: viene specificata la logica per ogni transizione di stato. Questi possono essere difficili da verificare per i sistemi con N sottostati, con> 2 ** N stati, ma esistono interessanti metodi combinatori che possono verificare tutto. Nel 1962 scrissi la fase 1 di un tipo di sistema per le macchine 7070, usando questo tipo di generazione di codice condizionale e debug combinatorio. Delle 8000 righe del genere, il numero di bug dal giorno della prima versione è stato per sempre zero!

La seconda fase del genere, 12.000 linee, ha avuto oltre 60 errori nei primi due mesi. C'è molto altro da dire su questo, ma funziona. Se le case automobilistiche utilizzassero questo metodo per controllare il firmware, non vedremmo gli errori che vediamo ora.


1
Questo in realtà non risponde alla domanda originale. In che modo la coerenza e la completezza incidono sul fatto che la maggior parte della programmazione è ancora procedurale, non dichiarativa?
Jay Elston,

Il tuo paragrafo di apertura sembra essere una risposta a un punto della risposta programmers.stackexchange.com/a/275839/67057 di arnaud , non alla domanda stessa. Dovrebbe essere un commento lì (sul mio schermo, la tua risposta non è più sotto la sua, per prima cosa). Penso che il resto della tua risposta sia un esempio di come una piccola quantità di codice dichiarativo potrebbe generare una grande quantità di codice imperativo equivalente ma non è chiaro. La tua risposta ha bisogno di qualche riordino, in particolare per quanto riguarda i punti salienti.
itsbruce,

-3

Non tutto può essere rappresentato in modo dichiarativo.

Spesso, si desidera esplicitamente controllare il flusso di esecuzione

Ad esempio il seguente pseudo-codice: if whatever read a file call an URL else call an URL write a file come lo rappresenteresti in modo dichiarativo?

Certo, ci sono probabilmente alcuni modi esotici per farlo. Come monadi . Ma questi di solito sono più ingombranti, complicati e molto meno intuitivi della loro parte procedurale.

Tutto si riduce al fatto che "interagire" con il proprio ambiente / sistema non è dichiarativo. Tutto ciò che riguarda l'I / O è procedurale per essenza. Devi dire esattamente quando e cosa dovrebbe accadere e in quale ordine.

Dichiarativo è ottimo per tutto ciò che è puramente correlato al calcolo. Come una funzione gigante, inserisci X e ottieni Y. Fantastico. Un esempio è HTML, l'input è testo, l'output è quello che vedi nel tuo browser.


2
Non lo compro. Perché il tuo esempio non è dichiarativo? È il if/ else, nel qual caso quale sarebbe un'alternativa dichiarativa? Certamente non sono le parti read/ write/ call, dal momento che sono dei bei elenchi dichiarativi di valori (se stai insinuando che sono racchiusi {...; ...}, perché non implica che siano avvolti in [..., ...])? Naturalmente, gli elenchi sono solo monoidi gratuiti; molti altri farebbero anche. Non vedo perché le monadi siano rilevanti qui; sono solo un'API. Haskell è passato dai flussi -> monadi per facilitare la composizione manuale, ma i linguaggi logici possono comporre automaticamente gli elenchi in ordine.
Warbo

2
-1 per le sole Monadi. 1. Non sono davvero esotici (liste e set sono Monadi e tutti li usano). 2. Non hanno nulla a che fare con forzare le cose da fare in una sequenza specifica (Haskell fare notazione aspetto di importanza fondamentale, ma non è). I linguaggi dichiarativi / funzionali specificano relazioni e dipendenze. Se la funzione X necessita di input Y, Y verrà generato prima di X. Ottenere le dipendenze giuste e la sequenza appropriata di eventi si definirà. Molta interazione è guidata dagli eventi , non in una sequenza impostata. Le lingue dichiarative non rendono più difficile reagire agli eventi.
itsbruce

La pigrizia ne complica una parte, ma la pigrizia non fa parte della definizione di lingue dichiarative, la maggior parte delle quali non la usa. E in quelli che lo fanno, il modo di garantire la valutazione non ha nulla a che fare con le monadi. Per un esempio di dove un linguaggio dichiarativo viene utilizzato esclusivamente per l'interazione piuttosto che per il calcolo astratto, non specifica l'ordine ma si accerta che le cose giuste accadano in sequenza, non guardare oltre il Puppet DSL. Il che ha il vantaggio che accadono solo le cose necessarie, non qualcosa che le lingue imperative rendono facile.
itsbruce

1
Oltre all'esempio di @itsbruce, la programmazione reattiva è considerata dichiarativa e riguarda l'interazione con l'ambiente.
Maciej Piechotka,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.