I linguaggi funzionali, per definizione, non dovrebbero mantenere variabili di stato. Perché, allora, Haskell, Clojure e altri forniscono implementazioni di memoria transazionale software (STM)? C'è un conflitto tra due approcci?
Non c'è niente di sbagliato in un linguaggio funzionale che mantiene lo stato mutabile. Anche i linguaggi funzionali "puri" come Haskell devono mantenere lo stato per interagire con il mondo reale. I linguaggi funzionali "impuri" come Clojure consentono effetti collaterali che possono includere lo stato di mutamento.
Il punto principale è che le lingue funzionali scoraggiano lo stato mutabile a meno che non ne abbia davvero bisogno . Lo stile generale è quello di programmare utilizzando funzioni pure e dati immutabili e interagire solo con lo stato mutabile "impuro" nelle parti specifiche del codice che lo richiedono. In questo modo, puoi mantenere il resto della tua base di codice "puro".
Penso che ci siano diversi motivi per cui STM è più comune nei linguaggi funzionali:
Personalmente mi piace l'approccio di Clojure che consente la mutabilità, ma solo nel contesto di "riferimenti gestiti" strettamente controllati che possono partecipare alle transazioni STM. Tutto il resto nella lingua è "puramente funzionale".
;; define two accounts as managed references
(def account-a (ref 100))
(def account-b (ref 100))
;; define a transactional "transfer" function
(defn transfer [ref-1 ref-2 amount]
(dosync
(if (>= @ref-1 amount)
(do
(alter ref-1 - amount)
(alter ref-2 + amount))
(throw (Error. "Insufficient balance!")))))
;; make a stranfer
(transfer account-a account-b 75)
;; inspect the accounts
@account-a
=> 25
@account-b
=> 175
Nota che il codice precedente è completamente transazionale e atomico: un osservatore esterno che legge i due saldi di un'altra transazione vedrà sempre uno stato atomico coerente, cioè i due saldi saranno sempre pari a 200. Con la concorrenza basata sui blocchi, questo è un sorprendentemente difficile da risolvere in un sistema complesso con molte entità transazionali.
Per alcuni chiarimenti extra, Rich Hickey fa un eccellente lavoro di spiegazione dello STM di Clojure in questo video
Functional languages, by definition, should not maintain state variables
La tua definizione è sbagliata. La lingua che non può mantenere lo stato semplicemente non può essere utilizzata.
La differenza tra linguaggi funzionali e imperativi non è che uno di essi abbia uno stato e l'altro no. È in un modo che mantengono lo stato.
Le lingue imperative si sono diffuse in tutto il programma.
Le lingue funzionali isolano e mantengono lo stato esplicitamente tramite le firme dei tipi. E questo è il motivo per cui forniscono meccanismi sofisticati di gestione dello stato come STM.
A volte un programma richiede uno stato mutabile (ad esempio, i contenuti del database per un'app Web) e sarebbe bello poterlo utilizzare senza perdere benefits di programmazione funzionale. Nei linguaggi non funzionali, lo stato mutabile permea tutto. Se lo rendi esplicito con qualche tipo di API speciale , puoi limitarlo a una piccola area identificabile mentre tutto altro rimane puramente funzionale. I vantaggi di FP comprendono un debugging più semplice, test unitari ripetibili, concorrenza indolore e cordialità multicore / GPU.
Leggi altre domande sui tag functional-programming haskell clojure stm