Le chiusure con effetti collaterali sono considerate "stile funzionale"?

9

Molti linguaggi di programmazione moderni supportano alcuni concetti di chiusura , ovvero di un pezzo di codice (un blocco o una funzione) che

  1. Può essere considerato come un valore, e quindi memorizzato in una variabile, passato a diverse parti del codice, essere definito in una parte di un programma e invocato in una parte totalmente diversa dello stesso programma.
  2. Può acquisire variabili dal contesto in cui è definito e accedervi quando viene successivamente richiamato (possibilmente in un contesto completamente diverso).

Ecco un esempio di chiusura scritta in Scala:

def filterList(xs: List[Int], lowerBound: Int): List[Int] =
  xs.filter(x => x >= lowerBound)

La funzione letterale x => x >= lowerBound contiene la variabile libera lowerBound , che viene chiusa (vincolata) dall'argomento della funzione filterList che ha lo stesso nome. La chiusura viene passata al metodo di libreria filter , che può richiamarlo ripetutamente come una funzione normale.

Ho letto molte domande e risposte su questo sito e, per quanto ho capito, il termine chiusura è spesso associato automaticamente alla programmazione funzionale e funzionale stile di programmazione.

La definizione della programmazione delle funzioni su wikipedia recita:

In computer science, functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions and avoids state and mutable data. It emphasizes the application of functions, in contrast to the imperative programming style, which emphasizes changes in state.

e più avanti

[...] in functional code, the output value of a function depends only on the arguments that are input to the function [...]. Eliminating side effects can make it much easier to understand and predict the behavior of a program, which is one of the key motivations for the development of functional programming.

D'altro canto, molti costrutti di chiusura forniti dai linguaggi di programmazione consentono a una chiusura di acquisire variabili non locali e modificarle quando viene richiamata la chiusura, producendo quindi un effetto collaterale sull'ambiente in cui sono state definite.

In questo caso, le chiusure implementano la prima idea di programmazione funzionale (le funzioni sono entità di prima classe che possono essere spostate come altri valori) ma trascurano la seconda idea (evitando gli effetti collaterali).

Questo uso di chiusure con effetti collaterali è considerato stile funzionale o le chiusure sono considerate un costrutto più generale che può essere utilizzato sia per uno stile di programmazione funzionale che non funzionale? C'è qualche letteratura su questo argomento?

NOTA IMPORTANTE

Non sto mettendo in dubbio l'utilità di effetti collaterali o di avere chiusure con effetti collaterali. Inoltre, non sono interessato a una discussione sui vantaggi / svantaggi delle chiusure con o senza effetti collaterali.

Mi interessa solo sapere se l'uso di tali chiusure è ancora considerato uno stile funzionale dal proponente della programmazione funzionale o se, al contrario, il loro uso è scoraggiato quando si utilizza uno stile funzionale.

    
posta Giorgio 07.11.2012 - 20:56
fonte

3 risposte

7

No; la definizione di paradigma funzionale riguarda la mancanza di stato e implicitamente la mancanza di effetti collaterali. Non si tratta di funzioni di alto livello, chiusure, manipolazione di liste supportate dalla lingua o altre caratteristiche linguistiche ...

Il nome della programmazione funzionale deriva dalla nozione matematica di funzioni - chiamate ripetute sullo stesso input fornisce sempre la stessa funzione - nullipotent. Questo può essere ottenuto solo se i dati sono immutabili . Per facilitare lo sviluppo, le funzioni diventano mutabili (le funzioni cambiano, i dati sono ancora immutabili) e quindi la nozione di funzioni di ordine superiore (funzionali in matematica, come derivati per esempio) - una funzione che richiede come input un'altra funzione. Per la possibilità che le funzioni vengano portate in giro e passate come argomenti, sono state adottate funzioni di prima classe ; seguendo questi, per migliorare ulteriormente la produttività, è apparso chiusure

Questa è, ovviamente, una visualizzazione molto semplificata.

    
risposta data 07.11.2012 - 21:33
fonte
9

No. "Stile funzionale" implica la programmazione senza effetti collaterali.

Per capire perché, dai un'occhiata al blog di Eric Lippert voce sul metodo di estensione ForEach<T> , e perché Microsoft non ha incluso un metodo di sequenza simile a Linq :

I am philosophically opposed to providing such a method, for two reasons.

The first reason is that doing so violates the functional programming principles that all the other sequence operators are based upon. Clearly the sole purpose of a call to this method is to cause side effects. The purpose of an expression is to compute a value, not to cause a side effect. The purpose of a statement is to cause a side effect. The call site of this thing would look an awful lot like an expression (though, admittedly, since the method is void-returning, the expression could only be used in a “statement expression” context.) It does not sit well with me to make the one and only sequence operator that is only useful for its side effects.

The second reason is that doing so adds zero new representational power to the language. Doing this lets you rewrite this perfectly clear code:

foreach(Foo foo in foos){ statement involving foo; }

into this code:

foos.ForEach((Foo foo)=>{ statement involving foo; });

which uses almost exactly the same characters in slightly different order. And yet the second version is harder to understand, harder to debug, and introduces closure semantics, thereby potentially changing object lifetimes in subtle ways.

    
risposta data 07.11.2012 - 21:08
fonte
0

La programmazione funzionale porta sicuramente le funzioni di prima classe al prossimo livello concettuale, ma dichiarare funzioni anonime o passare funzioni ad altre funzioni non è necessariamente una cosa di programmazione funzionale. In C, tutto era un numero intero. Un numero, un puntatore ai dati, un puntatore a una funzione ... tutto a posto. È possibile passare i puntatori di funzione ad altre funzioni, creare elenchi di puntatori di funzione ... Heck, se si lavora in linguaggio assembly, le funzioni sono in realtà solo gli indirizzi in memoria in cui sono memorizzati i blocchi di istruzioni della macchina. Dare un nome a una funzione è un overhead in più per le persone che hanno bisogno di un compilatore per scrivere il codice. Quindi le funzioni erano "di prima classe" in tal senso in un linguaggio completamente non funzionale.

Se l'unica cosa che fai è calcolare le formule matematiche in un REPL, allora puoi essere funzionalmente puro con la tua lingua. Ma la maggior parte della programmazione aziendale ha effetti collaterali. Perdere denaro in attesa che un programma di lunga durata sia completato è un effetto collaterale. Prendendo qualsiasi azione esterna: scrivere su un file, aggiornare un database, registrare eventi in ordine, ecc. Richiede un cambio di stato. Potremmo discutere se lo stato è davvero cambiato se incapsulate queste azioni in involucri immutabili che spengono gli effetti collaterali in modo che il vostro codice non debba preoccuparsi di loro. Ma è come discutere se un albero fa rumore se cade nella foresta con nessuno lì a sentirlo. Il fatto è che l'albero è iniziato in posizione verticale e finito a terra. Lo stato viene modificato quando viene eseguito, anche se solo per segnalare che le cose sono state fatte. La vera purezza nella programmazione funzionale è pienamente realizzata solo in sezioni isolate di codice o su carta, non in programmi reali e utili nel loro insieme.

Quindi rimaniamo con una scala di purezza funzionale, non in bianco e nero, ma con sfumature di grigio. E su quella scala, meno effetti collaterali, meno mutabilità, meglio è (più funzionale).

Se hai assolutamente bisogno di un effetto collaterale o di uno stato mutabile nel tuo codice funzionale, ti sforzerai di incapsularlo dal resto del tuo programma nel miglior modo possibile. L'uso di una chiusura (o qualsiasi altra cosa) per iniettare effetti collaterali o stato mutabile in funzioni altrimenti pure è l'antitesi della programmazione funzionale. L'unica eccezione potrebbe essere se la chiusura fosse il modo più efficace per incapsulare gli effetti collaterali dal codice al quale è passato. Non è ancora "programmazione funzionale", ma potrebbe essere l'armadio che puoi ottenere in certe situazioni.

    
risposta data 07.11.2012 - 21:37
fonte

Leggi altre domande sui tag