Nella programmazione funzionale, le variabili locali mutabili senza effetti collaterali sono ancora considerate "cattive pratiche"?

21

Le variabili locali mutabili sono in una funzione che vengono utilizzate solo internamente (ad esempio la funzione non ha effetti collaterali, almeno non intenzionalmente) ancora considerate "non funzionali"?

es. nel controllo dello stile del corso "Programmazione funzionale con Scala" considera qualsiasi var di utilizzo errato

La mia domanda, se la funzione non ha effetti collaterali, sta scrivendo un codice di stile imperativo ancora scoraggiato?

es. invece di usare la ricorsione di coda con il pattern dell'accumulatore, cosa c'è di sbagliato nel fare un loop locale per il loop e creare un local % mutabile% co_de e aggiungerlo, purché l'input non sia cambiato?

Se la risposta è "sì, sono sempre scoraggiati, anche se non ci sono effetti collaterali", qual è la ragione?

    
posta Eran Medan 25.04.2013 - 17:36
fonte

6 risposte

16

L'unica cosa che è inequivocabilmente una cattiva pratica qui è affermare che qualcosa è una funzione pura quando non lo è.

Se le variabili mutabili vengono utilizzate in un modo che è veramente e completamente autosufficiente, la funzione è esternamente pura e tutti sono felici. Haskell infatti lo supporta esplicitamente , con il sistema di tipi garantisce anche che i riferimenti mutabili non possano essere utilizzati al di fuori della funzione che li crea.

Detto questo, penso che parlare di "effetti collaterali" non sia il modo migliore per guardarlo (ed è il motivo per cui ho detto "puro" sopra). Qualunque cosa crei una dipendenza tra la funzione e lo stato esterno rende le cose più difficili da ragionare, e ciò include cose come conoscere l'ora corrente o usare lo stato mutabile nascosto in un modo non thread-safe.

    
risposta data 25.04.2013 - 17:54
fonte
14

Il problema non è la mutevolezza in sé, è una mancanza di trasparenza referenziale.

Una cosa referenzialmente trasparente e un riferimento ad essa devono sempre essere uguali, quindi una funzione referenzialmente trasparente restituirà sempre gli stessi risultati per un dato insieme di input e una "variabile" referenzialmente trasparente è in realtà un valore piuttosto che una variabile , dal momento che non può cambiare. Puoi creare una funzione referentially trasparente che contiene una variabile mutabile; non è un problema Potrebbe essere più difficile garantire che la funzione sia comunque referenzialmente trasparente, a seconda di cosa stai facendo.

C'è un caso in cui posso pensare a dove la mutabilità deve essere usata per fare qualcosa che sia molto funzionale: la memoizzazione. Memoization sta memorizzando i valori nella cache da una funzione, quindi non devono essere ricalcolati; è referenzialmente trasparente, ma usa la mutazione.

Ma in generale la trasparenza referenziale e l'immutabilità vanno insieme, a parte una variabile mutabile locale in una funzione referenzialmente trasparente e la memoizzazione, non sono sicuro che ci siano altri esempi in cui questo non sia il caso.

    
risposta data 25.04.2013 - 19:30
fonte
8

Non è molto bello farla diventare "buona pratica" e "cattiva pratica". Scala supporta valori mutabili perché risolvono determinati problemi molto meglio dei valori immutabili, cioè quelli che sono di natura iterativa.

Per prospettiva, sono abbastanza sicuro che tramite CanBuildFrom quasi tutte le strutture immutabili fornite da scala eseguono una sorta di mutazione internamente. Il punto è che ciò che espone è immutabile. Mantenere il maggior numero possibile di valori immutabili aiuta a rendere il programma più facile da ragionare e meno incline agli errori .

Questo non significa che devi necessariamente evitare internamente le strutture e i valori mutabili quando hai un problema più adatto alla mutevolezza.

Con questo in mente, molti problemi che in genere richiedono variabili mutabili (come il looping) possono essere risolti meglio con molte delle funzioni di ordine superiore che linguaggi come Scala forniscono (map / filter / fold). Sii consapevole di quelli.

    
risposta data 25.04.2013 - 17:44
fonte
3

Oltre ai potenziali problemi relativi alla sicurezza dei thread, in genere si perde anche molta sicurezza del tipo. I loop imperativi hanno un tipo di ritorno di Unit e possono assumere praticamente qualsiasi espressione per gli input. Le funzioni di ordine superiore e persino la ricorsione hanno semantica e tipi molto più precisi.

Hai anche molte più opzioni per l'elaborazione dei contenitori funzionali rispetto ai loop imperativi. Con imperativo, hai fondamentalmente for , while e variazioni minori su quei due come do...while e foreach .

In funzionale, hai aggregato, conteggio, filtro, trova, flatMap, fold, groupBy, lastIndexWhere, map, maxBy, minBy, partition, scan, sortBy, sortWith, span e takeWhile, solo per citarne alcuni più comuni quelli della libreria standard di Scala. Quando ti abitui ad averli disponibili, i cicli imperativi for sembrano troppo semplici in confronto.

L'unica vera ragione per usare la mutabilità locale è molto occasionalmente per le prestazioni.

    
risposta data 15.02.2017 - 10:38
fonte
2

Direi che è per lo più ok. Inoltre, la generazione di strutture in questo modo potrebbe essere un buon modo per migliorare le prestazioni in alcuni casi. Clojure ha risolto questo problema fornendo strutture dati transitorie .

L'idea di base è di consentire le mutazioni locali in un ambito limitato e quindi di bloccare la struttura prima di restituirla. In questo modo, il tuo utente può ancora ragionare sul tuo codice come se fosse puro, ma sei in grado di eseguire trasformazioni sul posto quando è necessario.

Come dice il link:

If a tree falls in the woods, does it make a sound? If a pure function mutates some local data in order to produce an immutable return value, is that ok?

    
risposta data 25.04.2013 - 18:30
fonte
2

Non avere variabili mutabili locali ha un vantaggio: rende la funzione più amichevole nei confronti dei thread.

Sono stato masterizzato da tale variabile locale (non nel mio codice, né ho avuto la fonte) causando un danneggiamento dei dati a bassa probabilità. La sicurezza del filo non è stata menzionata in un modo o nell'altro, non c'era stato che persistesse attraverso le chiamate e non ci fossero effetti collaterali. Non mi è venuto in mente che potrebbe non essere thread-safe, inseguire un 1 su 100.000 la corruzione dei dati casuali è un dolore reale.

    
risposta data 26.04.2013 - 05:52
fonte

Leggi altre domande sui tag