Perché Python non è molto buono per la programmazione funzionale? [chiuso]


324

Ho sempre pensato che la programmazione funzionale possa essere eseguita in Python. Pertanto, sono rimasto sorpreso dal fatto che Python non abbia ricevuto molta menzione in questa domanda e, quando è stato menzionato, normalmente non era molto positivo. Tuttavia, non sono state fornite molte ragioni per questo (sono state menzionate la mancanza di pattern matching e tipi di dati algebrici). Quindi la mia domanda è: perché Python non è molto buono per la programmazione funzionale? Ci sono più ragioni della sua mancanza di pattern matching e tipi di dati algebrici? Oppure questi concetti sono così importanti per la programmazione funzionale che un linguaggio che non li supporta può essere classificato solo come un linguaggio di programmazione funzionale di seconda classe? (Tieni presente che la mia esperienza con la programmazione funzionale è piuttosto limitata.)


2
2018 - Coconut (un linguaggio di programmazione funzionale che si compila in Python) migliora la programmazione funzionale in Python. Vedi anche questa serie di articoli da IBM pagina1 pagina2 pagina3
cssyphus

Risposte:


393

La domanda a cui fai riferimento pone quali lingue promuovono la programmazione sia funzionale che funzionale. Python non promuove la programmazione funzionale anche se funziona abbastanza bene.

Il miglior argomento contro la programmazione funzionale in Python è che i casi d'uso imperativo / OO sono attentamente considerati da Guido, mentre i casi d'uso di programmazione funzionale non lo sono. Quando scrivo Python imperativo, è una delle lingue più belle che conosca. Quando scrivo Python funzionale, diventa brutto e sgradevole come il tuo linguaggio medio che non ha un BDFL .

Il che non vuol dire che sia un male, solo che devi lavorare di più di quanto faresti se passassi a un linguaggio che promuove la programmazione funzionale o passi a scrivere OO Python.

Ecco le cose funzionali che mi mancano in Python:


  • Nessuna corrispondenza del modello e nessuna ricorsione della coda significano che gli algoritmi di base devono essere scritti in modo imperativo. La ricorsione è brutta e lenta in Python.
  • Una piccola libreria di elenchi e nessun dizionario funzionale significa che devi scrivere molte cose da solo.
  • Nessuna sintassi per curry o composizione significa che uno stile privo di punti è pieno di punteggiatura come argomenti che passano esplicitamente.
  • Iteratori anziché gli elenchi pigri significa che devi sapere se vuoi efficienza o persistenza e spargere le chiamate verso listse vuoi persistenza. (Iteratori vengono utilizzati una volta)
  • La semplice sintassi imperativa di Python, insieme al suo semplice parser LL1, significa che una sintassi migliore per le espressioni if ​​e le espressioni lambda è sostanzialmente impossibile. A Guido piace così, e penso che abbia ragione.

5
+1 per la ricorsione della coda mancante - anche se i costrutti in loop l'hanno sostituita, è comunque qualcosa che vale la pena perdere tra Python e Scheme.
nuovo123456,

5
Ottima risposta per completezza e composizione. Purtroppo, come con così tante risposte con un forte background funzionale, ha un uso abusivo della terminologia da parte dell'IMO. Sebbene capisca che non è possibile elaborare ciascun concetto in una risposta, mi chiedo se l'OP (con un background FP limitato ammesso) sia ben informato quando legge termini come "pattern matching", "dizionari funzionali", "curry" o " elenchi pigri ".
ThomasH,

4
Buon punto; Penso che la soluzione sia aggiungere collegamenti. Hai abbastanza rappresentante per modificare la mia risposta? In tal caso, sentiti libero di aggiungere collegamenti ai vari concetti. Inizierò quando avrò tempo dopo.
Nathan Shively-Sanders,

5
Mi rendo conto che ha 5 anni, ma ... sembra che si tratti più delle cose che ti mancano di Haskell , non dei linguaggi funzionali . Ad esempio, la maggior parte dei dialetti e discendenti di ML e Lisp non hanno il curry automatico, rendono lo stile privo di punti eccessivamente prolisso, non hanno liste pigre, ecc. Quindi, se gli iteratori invece di liste pigre rendono Python un brutto linguaggio funzionale, avendo deve rendere CaML un terribile linguaggio funzionale?
Abarnert,

4
@abarnert: Caml ha tutti i punti elenco tranne gli elenchi pigri, che sono disponibili come libreria. Occasionalmente ho usato Caml quando ho scritto questa risposta e attualmente uso F #. Sono entrambi linguaggi funzionali molto carini.
Nathan Shively-Sanders,

102

Guido ha una buona spiegazione di questo qui . Ecco la parte più rilevante:

Non ho mai considerato Python fortemente influenzato dai linguaggi funzionali, indipendentemente da ciò che la gente dice o pensa. Conoscevo molto di più i linguaggi imperativi come C e Algol 68 e sebbene avessi fatto funzioni oggetti di prima classe, non vedevo Python come un linguaggio di programmazione funzionale. Tuttavia, in precedenza, era chiaro che gli utenti volevano fare molto di più con elenchi e funzioni.

...

Vale anche la pena notare che anche se non immaginavo Python come un linguaggio funzionale, l'introduzione di chiusure è stata utile nello sviluppo di molte altre funzionalità di programmazione avanzata. Ad esempio, alcuni aspetti delle nuove classi di stile, decoratori e altre funzionalità moderne si basano su questa capacità.

Infine, anche se nel corso degli anni sono state introdotte numerose funzionalità di programmazione funzionale, a Python mancano ancora alcune funzionalità presenti nei linguaggi di programmazione funzionale "reali". Ad esempio, Python non esegue determinati tipi di ottimizzazioni (ad es. Ricorsione della coda). In generale, poiché la natura estremamente dinamica di Python, è impossibile eseguire il tipo di ottimizzazione in fase di compilazione conosciuta da linguaggi funzionali come Haskell o ML. E va bene.

Ne tiro fuori due cose:

  1. Il creatore del linguaggio non considera davvero Python un linguaggio funzionale. Pertanto, è possibile vedere le funzionalità "funzionali-esque", ma è improbabile che tu veda qualcosa che sia definitivamente funzionale.
  2. La natura dinamica di Python inibisce alcune delle ottimizzazioni che vedi in altri linguaggi funzionali. Certo, Lisp è altrettanto dinamico (se non più dinamico) di Python, quindi questa è solo una spiegazione parziale.

8
Puoi fare l'ottimizzazione delle chiamate in coda in Python. Guido non lo capisce / non lo capisce.
Jules,

26
Sembra ridursi a quel Guido van Rossum che non ama lo stile funzionale.
Svante,

59
Penso che sia più preciso affermare che Guido van Rossum non capisce lo stile funzionale e non capisce perché Python ne abbia bisogno. Devi capire due cose: 1) i linguaggi di programmazione sono in fondo a uno stack tecnologico e influiscono su tutto ciò che è costruito su di essi e 2) proprio come qualsiasi altro software, è più facile aggiungere funzionalità che rimuoverli. Quindi penso che sia una buona qualità per un progettista di lingue essere critico verso questo tipo di richieste.
Jason Baker,

8
"Certo, Lisp è altrettanto dinamico" -> e altrettanto imperativo!
pyon,

6
@Jules, ti dispiace condividere una linea guida per l'uso dell'ottimizzazione delle chiamate in coda in Python? Un puntatore a qualche fonte sarebbe utile.
David Agitato il

52

Lo schema non ha tipi di dati algebrici o pattern matching ma è sicuramente un linguaggio funzionale. Cose fastidiose su Python dal punto di vista della programmazione funzionale:

  1. Lambdas paralizzato. Poiché Lambdas può contenere solo un'espressione e non è possibile eseguire tutte le operazioni con la stessa facilità in un contesto di espressione, ciò significa che le funzioni che è possibile definire "al volo" sono limitate.

  2. Gli if sono dichiarazioni, non espressioni. Questo significa, tra l'altro, che non puoi avere un lambda con un If al suo interno. (Questo è risolto dai ternari in Python 2.5, ma sembra brutto.)

  3. Guido minaccia di rimuovere mappe, filtri e riduzioni di tanto in tanto

D'altra parte, Python ha chiusure lessicali, Lambdas e comprensioni di elenchi (che sono in realtà un concetto "funzionale" indipendentemente dal fatto che Guido lo ammetta o meno). Faccio molta programmazione in "stile funzionale" in Python, ma difficilmente direi che sia l'ideale.


3
Mappa, filtro e riduzione non sono realmente necessari in Python. Devo ancora vedere un pezzo di codice che è stato semplificato molto usandoli. Inoltre, chiamare le funzioni in Python può essere costoso, quindi di solito è meglio usare solo una comprensione elenco / generatore o un ciclo for comunque.
Jason Baker,

2
Questo è esattamente ciò che Nathan Sanders dice di seguito: "Python non promuove la programmazione funzionale anche se funziona abbastanza bene". Se Guido volesse che Python diventasse un linguaggio funzionale, avrebbe fatto funzionare l'implementazione abbastanza bene da usare le funzioni usa e getta e avrebbe stappato Lambdas nella misura in cui si potesse effettivamente usare mappa / filtro / riduzione in modi utili. D'altra parte, le persone funzionali stanno iniziando a svegliarsi con la bellezza delle comprensioni delle liste. Speriamo che non dovremo scegliere l'uno o l'altro.
Jacob B

7
mappa e filtro sono banalmente sostituiti da una comprensione dell'elenco. ridurre - quasi sempre - è così inefficiente che dovrebbe essere sostituito con una funzione di generatore.
S.Lott

13
@ S.Lott come sostituisci riduci con un generatore?
Antimonio,

17
La comprensione dell'elenco di @JacobB era disponibile in linguaggi funzionali circa 15 anni prima dell'invenzione di Python e 25 anni prima che Python acquisisse un'implementazione della funzione. L'idea che Python abbia influenzato la loro diffusione, o che fp abbia imparato questo da Python, o anche semplicemente che la sua popolarità nel mondo fp dopo le date dell'implementazione di Python, è semplicemente sbagliata. L'implementazione di Python è stata presa direttamente da Haskell. Forse ti ho frainteso e non è quello che volevi dire, ma sono perplesso da "le persone funzionali stanno iniziando a svegliarsi con la bellezza delle comprensioni".
itsbruce,

23

Non definirei mai Python "funzionale" ma ogni volta che programma in Python il codice finisce inevitabilmente per essere quasi puramente funzionale.

Certo, ciò è dovuto principalmente alla comprensione dell'elenco estremamente piacevole. Quindi non suggerirei necessariamente Python come linguaggio di programmazione funzionale, ma suggerirei la programmazione funzionale per chiunque usi Python.


17

Consentitemi di dimostrare con un pezzo di codice tratto da una risposta a una domanda "funzionale" di Python su SO

Pitone:

def grandKids(generation, kidsFunc, val):
  layer = [val]
  for i in xrange(generation):
    layer = itertools.chain.from_iterable(itertools.imap(kidsFunc, layer))
  return layer

Haskell:

grandKids generation kidsFunc val =
  iterate (concatMap kidsFunc) [val] !! generation

La differenza principale è che libreria standard di Haskell ha funzioni utili per la programmazione funzionale: in questo caso iterate, concate(!!)


7
Ecco una sostituzione di una linea per grandKids()il corpo con le espressioni del generatore: return reduce(lambda a, v: concat((x for x in kidsFunc(v)) for v in a), xrange(generation), [val]).
Lloeki,

6
Ed eccone uno che non ha nemmeno bisogno concat:return reduce(lambda a, v: (x for v in a for x in kidsFunc(v)), xrange(generation), [val])
Lloeki,

9
@Lloeki: iterare semplificherebbe significativamente quel codice e (x for v in a for x in kidsFunc (v)) è molto più chiaro come concatMap (kidsFunc). La mancanza di simpatici builtin di ordine superiore di Python rende criptico e dettagliato un codice equivalente rispetto a Haskell.
Phob,

2
concat può essere sostituito daitertools.chain.from_iterable
Antimonio

@Antimonio: buono a sapersi. thx
yairchu,

14

Una cosa veramente importante per questa domanda (e le risposte) è la seguente: che diavolo è la programmazione funzionale e quali sono le sue proprietà più importanti. Proverò a darmi il mio punto di vista:

La programmazione funzionale è molto simile alla scrittura di matematica su una lavagna. Quando si scrivono equazioni su una lavagna, non si pensa a un ordine di esecuzione. Non c'è (in genere) nessuna mutazione. Non torni il giorno dopo e non lo guardi, e quando esegui di nuovo i calcoli, ottieni un risultato diverso (o potresti, se hai bevuto del caffè fresco :)). Fondamentalmente, quello che è alla lavagna è lì, e la risposta era già lì quando hai iniziato a scrivere le cose, non hai ancora capito di cosa si tratta ancora.

La programmazione funzionale è molto simile; non cambi le cose, devi solo valutare l'equazione (o in questo caso "programma") e capire qual è la risposta. Il programma è ancora lì, non modificato. Lo stesso con i dati.

Vorrei classificare quanto segue come le caratteristiche più importanti della programmazione funzionale: a) trasparenza referenziale - se si valuta la stessa affermazione in un altro momento e luogo, ma con gli stessi valori variabili, significherà comunque lo stesso. b) nessun effetto collaterale - non importa per quanto tempo fissi la lavagna, l'equazione che un altro ragazzo sta guardando un'altra lavagna non cambierà accidentalmente. c) anche le funzioni sono valori. che possono essere passati in giro e applicati con o verso altre variabili. d) composizione della funzione, puoi fare h = g · f e quindi definire una nuova funzione h (..) che equivale a chiamare g (f (..)).

Questa lista è nel mio ordine prioritario, quindi la trasparenza referenziale è la più importante, seguita da nessun effetto collaterale.

Ora, se passi attraverso Python e controlli quanto bene il linguaggio e le librerie supportano e garantiscono questi aspetti, allora sei sulla buona strada per rispondere alla tua domanda.


2
Le funzioni sono di prima classe in Python.
Carl Smith,

@CarlSmith Quello, ma lascia 3/4 che Python non ha. : - \
arya

1
Non penso che Python sia un buon linguaggio per la programmazione funzionale. Non sono nemmeno sicuro ora cosa intendo con quel commento per essere onesto. Non sembra rilevante per la risposta. Lo eliminerei, ma il tuo commento sarebbe fuori contesto.
Carl Smith,

1
Trasparenza referenziale e immutabilità non sono in realtà caratteristiche del linguaggio. Sì, alcune lingue (Haskell) le enfatizzano e rendono difficile non averle, ma puoi fare una funzione referenzialmente trasparente o un oggetto immutabile in qualsiasi lingua. Devi solo aggirare la libreria standard, che spesso li violerà.
Kevin,

Inoltre, Python supporta sia il curry che la composizione, ma non a livello di lingua, è invece nella libreria standard.
Kevin,

10

Python è quasi un linguaggio funzionale. È "lite funzionale".

Ha funzionalità extra, quindi non è abbastanza puro per alcuni.

Manca anche alcune funzionalità, quindi non è abbastanza completo per alcune.

Le funzioni mancanti sono relativamente facili da scrivere. Dai un'occhiata a post come questo su FP in Python.


2
Per la maggior parte, sono d'accordo con questo post. Ma non posso essere d'accordo nel dire che Python è un linguaggio funzionale. Incoraggia moltissimo la programmazione imperativa, ed è difficile non farlo con le "funzionalità extra" che menzioni. Penso che sia meglio riferirsi a Python come "funzionale lite" come hai fatto nell'altro post. :-)
Jason Baker,

8
-1 Siamo spiacenti, no. Voglio dire, no, no. I linguaggi funzionali forniscono costrutti che facilitano il ragionamento formale: induttivo (ML), equazionale (Haskell). Le chiusure e le funzioni anonime sono solo zucchero sintattico per il modello di strategia.
pyon

8

Un altro motivo non menzionato sopra è che molte funzioni e metodi incorporati di tipi incorporati modificano un oggetto ma non restituiscono l'oggetto modificato. Se tali oggetti modificati fossero restituiti, ciò renderebbe il codice funzionale più pulito e più conciso. Ad esempio, se some_list.append (some_object) restituisce some_list con some_object aggiunto.


4

Oltre ad altre risposte, un motivo per cui Python e la maggior parte degli altri linguaggi multi-paradigma non sono adatti alla vera programmazione funzionale è perché i loro compilatori / macchine virtuali / runtime non supportano l'ottimizzazione funzionale. Questo tipo di ottimizzazione è ottenuta dal compilatore che comprende le regole matematiche. Ad esempio, molti linguaggi di programmazione supportano una mapfunzione o un metodo. Questa è una funzione abbastanza standard che accetta una funzione come argomento e un iterabile come secondo argomento quindi applica quella funzione a ciascun elemento nell'iterabile.

Comunque risulta che map( foo() , x ) * map( foo(), y )è lo stesso di map( foo(), x * y ). Il secondo caso è in realtà più veloce del primo perché il primo esegue due copie in cui il secondo esegue uno.

I linguaggi funzionali migliori riconoscono queste relazioni matematicamente basate ed eseguono automaticamente l'ottimizzazione. Le lingue che non sono dedicate al paradigma funzionale probabilmente non verranno ottimizzate.


map( foo() , x ) * map( foo(), y ) == map( foo(), x * y )non è vero per tutte le funzioni. Ad esempio, considerare il caso quando foocalcola una derivata.
Eli Korvigo,

1
Penso che intendesse +invece di *.
user1747134

Stai assumendo foo () è lineare?
Juan Isaza,

quella proprietà NON è vera per foo (x) = x + 1. As (x + 1) * (y + 1)! = X * y + 1.
juan Isaza
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.