È difficile definire esattamente che cosa sia un "linguaggio funzionale": fuori dalle lingue che hai elencato, solo Haskell è puramente funzionale (tutti gli altri adottano una sorta di approccio ibrido). Ci sono alcune caratteristiche del linguaggio che sono molto utili per la programmazione funzionale, però, e Ruby e Python non ne hanno abbastanza per essere ambienti molto buoni per FP. Ecco la mia lista di controllo personale, in ordine di importanza:
-
Funzioni di prima classe e chiusure (Ruby, Python e tutti gli altri che hai elencato hanno questo).
- Garantito ottimizzazione della chiamata di coda (Erlang, Haskell, Scala e Scheme hanno questo, ma non Python, Ruby, o Clojure (ancora)).
- Supporto per immutabilità nella lingua e nelle librerie standard (questo è un grande che tutti i "linguaggi funzionali" hai elencato (tranne Scheme) ma Ruby e Python no).
- Supporto a livello di lingua per le funzioni (o pure) (per quanto ne so, solo Haskell ha questo al momento).
La necessità di (1) dovrebbe essere ovvia - le funzioni di ordine superiore sono estremamente difficili senza funzioni di prima classe. Quando le persone parlano di Ruby e Python che sono buone lingue per FP, di solito parlano di questo. Tuttavia, questa particolare caratteristica è necessaria ma non sufficiente per rendere un linguaggio valido per FP.
(2) è stata una necessità tradizionale per FP fin da quando Scheme è stato inventato. Senza TCO, è impossibile programmare con ricorsione profonda, che è uno dei capisaldi di FP, perché si ottengono overflow dello stack. L'unico linguaggio "funzionale" (per definizione popolare) che non ha questo è Clojure (a causa delle limitazioni della JVM), ma Clojure ha una varietà di hack per simulare il TCO. (FYI, Il TCO di Ruby è specifico per l'implementazione , ma Python specificamente non lo supporta .) La ragione per cui il TCO deve essere garantito è che, se le funzioni ricorsive profonde specifiche dell'implementazione si interromperanno con alcune implementazioni, quindi non puoi davvero usarle affatto.
(3) è un'altra cosa importante che i linguaggi funzionali moderni (specialmente Haskell, Erlang, Clojure e Scala) hanno che Ruby e Python non lo fanno. Senza entrare troppo nel dettaglio, l'immutabilità garantita elimina intere classi di bug, soprattutto in situazioni concorrenti, e consente di ordinare cose carine come strutture dati persistenti . È molto difficile sfruttare questi vantaggi senza il supporto a livello di lingua.
(4) è, per me, la cosa più interessante dei linguaggi puramente funzionali (al contrario dei linguaggi ibridi). Considera la seguente estremamente semplice funzione Ruby:
def add(a, b)
a + b
end
Sembra una pura funzione, ma a causa dell'overloading dell'operatore, potrebbe mutare i parametri o causare effetti collaterali come la stampa sulla console. È improbabile che qualcuno sovraccarichi l'operatore +
per avere un effetto collaterale, ma il linguaggio non fornisce garanzie. (Lo stesso vale per Python, anche se forse non con questo specifico esempio.)
In un linguaggio puramente funzionale, d'altra parte, ci sono garanzie a livello linguistico che le funzioni sono referenzialmente trasparenti. Questo ha numerosi vantaggi: le funzioni pure possono essere facilmente memorizzate; possono essere facilmente testati senza fare affidamento su alcun tipo di stato globale; e i valori all'interno della funzione possono essere valutati pigramente o in parallelo senza preoccuparsi di problemi di concorrenza. Haskell ne approfitta pienamente, ma non ne so abbastanza di altri linguaggi funzionali per sapere se lo fanno.
Tutto ciò detto, è possibile utilizzare le tecniche FP in quasi tutti i linguaggi (anche Java). Ad esempio, il MapReduce di Google è ispirato a idee funzionali, ma per quanto ne so non usano alcun "funzionale" lingue per i loro grandi progetti (penso che utilizzino principalmente C ++, Java e Python).