Le chiusure sono sufficienti per caratterizzare la programmazione funzionale?

5

Tutti i linguaggi di programmazione funzionale che conosco (ad esempio Haskell, Scala, Scheme, Clojure, SML, OCaml, ...) supportano la nozione di chiusure.

Inoltre, leggo spesso che una lingua X può essere considerata funzionale perché supporta le chiusure.

D'altra parte ci sono linguaggi di programmazione che hanno chiusure ma non considerate funzionali. Il miglior esempio che conosco è Smalltalk: Smalltalk ha blocchi (che nella maggior parte delle implementazioni si comportano come chiusure, vedi ad esempio qui ) ma è considerato un puro linguaggio orientato agli oggetti.

Quindi, mentre le chiusure sono un costrutto molto comune (o addirittura essenziale) nella programmazione funzionale, sono davvero sufficienti per caratterizzare la programmazione funzionale? Se il supporto delle chiusure è sufficiente per considerare un linguaggio funzionale, perché Smalltalk non è considerato funzionale?

O è la nozione di una chiusura ortogonale sia a funzionale che a programmazione orientata agli oggetti?

    
posta Giorgio 27.07.2013 - 21:20
fonte

5 risposte

4

I tratti distintivi dei linguaggi di programmazione funzionale:

  • Funzioni di ordine superiore. (Trasmissione di funzioni come parametri, memorizzazione di funzioni nelle strutture dati, restituzione di funzioni, creazione di funzioni in fase di esecuzione).
  • Incoraggiamento "Trasparenza referenziale".
  • E oserei dire "Pigrizia".
  • Immutabilità

Un linguaggio di programmazione funzionale fa un uso pesante di Strutture dati funzionali .

Avere Chiusure non significa che la lingua sia "Funzionale". Esempi: Java 8 (se verrà aggiunto Lambdas), Groovy e Objective-C,

Modifica:
In base al commento, ho rimosso Memoization dall'elenco precedente.

I linguaggi di programmazione funzionale utilizzano la tecnica di "Memoizzazione" perché è più semplice con il codice funzionale.

    
risposta data 27.07.2013 - 21:46
fonte
3

Le chiusure sono ortogonali a FP. In realtà, tutto ciò che sono è un modo diverso di guardare gli stessi concetti di base degli oggetti . Se ciò che è veramente importante per te è il comportamento, usi una chiusura. Se ciò che è veramente importante sono i dati di stato, usi un oggetto. Oggigiorno molte lingue OO offrono modi per fare entrambe le cose, ma ciò non le rende "lingue funzionali".

    
risposta data 27.07.2013 - 21:37
fonte
3

Le chiusure sono certamente una parte importante della programmazione funzionale. Tuttavia, al giorno d'oggi esistono molte lingue che supportano le chiusure e almeno un certo livello di programmazione funzionale, senza essere "linguaggi funzionali". Gli esempi includono Perl, Python, JavaScript, C #, Java 8, Ruby e altri. Viceversa, le chiusure apparentemente non sono completamente necessarie ai linguaggi funzionali, in quanto i primi linguaggi funzionali hanno preceduto l'invenzione della chiusura. Le prime versioni di Lisp utilizzavano l'ambito dinamico per tutte le variabili, quindi la nozione di "chiusura" non poteva essere espressa: una variabile era legata nell'ambito quando veniva chiamata una funzione, non quando la funzione veniva dichiarata. Detto questo, credo che tutti i moderni linguaggi funzionali fanno offrano chiusure; Non posso certo immaginare la programmazione in un linguaggio funzionale senza di loro!

    
risposta data 27.07.2013 - 23:15
fonte
1

Il concetto di chiusura non è specifico per la programmazione funzionale. Significa semplicemente che hai:

  • Una variabile in un ambito
  • Qualcosa costruito all'interno di questo ambito (in genere una funzione o un oggetto) che utilizza detta variabile
  • Che qualcosa viene passato a qualche parte fuori dall'ambito di origine

L'esempio da manuale è qualcosa del tipo:

function foo() {
    var x = 23;
    var bar = function(y) {
        return x + y;
    };
    return bar;
}

... e diciamo che bar chiude oltre x .

Tuttavia, possiamo fare lo stesso con gli oggetti, in realtà:

function foo() {
    var x = 23;
    var bar = {
        "y": x
    };
    return bar;
}

Il meccanismo è lo stesso, anche se ora non è una funzione che si chiude su x , ma su un oggetto. Di solito non si chiama chiusura, perché a differenza delle chiusure di funzioni, questo tipo di comportamento è "ovvio" per un programmatore imperativo, e l'idea è che stiamo semplicemente facendo riferimento a una variabile locale per mettere un valore in un oggetto - ma quindi, è esattamente ciò che fa una chiusura, solo che l'oggetto può anche essere una funzione.

Ora, per quanto riguarda la programmazione funzionale: il concetto più importante è la funzione . A differenza delle funzioni in linguaggi imperativi, per i quali "routine" o "procedura" è in realtà un nome molto migliore, le funzioni di programmazione funzionale sono concettualmente simili alle funzioni matematiche: mappature dalle cose alle cose. Prendendo questo concetto e utilizzandolo come la primitiva espressiva prima e più importante, gli altri segni distintivi della programmazione funzionale seguono logicamente:

  • Funzioni mappano gli input alle uscite; questo è tutto che fanno. Gli effetti collaterali come la stampa o il mantenimento dello stato mutabile non si adattano a questo modello, quindi i programmatori funzionali tendono ad evitare queste cose. Questo è ciò che la gente chiama la purezza : una funzione pura è una funzione che non ha effetti collaterali. Diverse lingue FP trattano questa materia in modo diverso; all'estremo opposto, c'è Haskell, che non consente affatto alcuna funzione impura, mentre alla fine pragmatica ci sono Lisp, Scheme, JavaScript ecc., che permettono agli effetti collaterali di apparire ovunque e lasciano il loro elusione ai programmatori di boyscout.
  • Anche le funzioni sono cose, quindi ha senso avere funzioni che prendono funzioni come input, o restituiscono funzioni come output, o entrambe. Tali funzioni sono note come funzioni di ordine superiore e il loro uso è onnipresente nella programmazione funzionale. La famosa funzione map è una funzione di ordine superiore: uno dei suoi argomenti è una funzione, che map si applica a ogni elemento nell'altro argomento (che è presumibilmente un elenco di qualche tipo).
  • Le funzioni possono essere espresse in termini di se stessi, a.k.a. ricorsione . Le definizioni ricorsive sono più facili da scrivere in modo puro; non si basano su costrutti di stato mutabile come le variabili del contatore di loop. Per questo motivo, i programmatori funzionali tendono a preferire soluzioni ricorsive rispetto a quelle iterative e i linguaggi di programmazione funzionale forniscono varie ottimizzazioni per evitare i problemi che possono causare la programmazione ricorsiva (overflow dello stack, perdite di memoria, ecc.). Inoltre, tipi comuni di ricorsione sono disponibili in modo generico in ogni linguaggio di programmazione funzionale che vale l'etichetta, i più famosi sono map e reduce (a.k.a. fold ). Con l'aiuto di queste funzioni, un programmatore funzionale può astrarre i dettagli della ricorsione effettiva e pensare in termini di map , reduce e filter diventa una seconda natura ad un certo punto.
  • Il tipo di funzione preferito è la funzione unario , che accetta un solo argomento. Usando chiusure, qualsiasi funzione n-ario può essere riscritta come una funzione unaria che restituisce una funzione (n-1) -aria, e applicando completamente questa logica, qualsiasi funzione n-ario può essere scritta come una catena annidata di funzioni unarie che ritornano il prossimo link della catena. Questo processo è chiamato currying , mentre il concetto di chiamare una funzione con un insieme incompleto di argomenti, che fornisce un'altra funzione che prende il resto degli argomenti, è noto come applicazione di funzione parziale . Come un semplice esempio, se hai una funzione che puoi chiamare in questo modo: foo(a, b, c) , la versione completa sarà chiamata come: foo(a)(b)(c) ; chiamare il foo al curry in questo modo: foo(a)(b) costituisce un'applicazione parziale e produce una funzione che, quando chiamata con c , dà lo stesso risultato della chiamata originale, ad esempio f = foo(a)(b); f(c) .
risposta data 29.07.2013 - 01:05
fonte
0

Per caratterizzare il linguaggio funzionale, una funzione chiave è in corso di elaborazione:

  1. hom (AxB, C) == hom (A, C ^ B)

Questa è una delle funzionalità più importanti disponibili nei linguaggi funzionali, poiché consente di aggiungere un parametro a una funzione. Le lingue che supportano solo il lato sinistro di == non possono essere considerate funzionali.

    
risposta data 28.07.2013 - 12:48
fonte

Leggi altre domande sui tag