Immutabilità o mutabilità non sono concetti che hanno senso nella programmazione funzionale.
Il contesto computazionale
Questa è un'ottima domanda che è un seguito interessante (non un duplicato) a un altro recente: qual è la differenza tra assegnazione, valutazione e associazione dei nomi?
Piuttosto, rispondendo alle tue dichiarazioni una per una, sto provando qui a darti una visione strutturata di ciò che è in gioco.
Esistono diversi problemi da considerare per rispondere, tra cui:
Lo stile di programmazione funzionale sembra sciocco perché lo vedi con un occhio imperativo da programmatore. Ma è un paradigma diverso e i tuoi concetti e percezione imperativi sono alieni, fuori posto. I compilatori non hanno tali pregiudizi.
Ma la conclusione finale è che è possibile scrivere programmi in modo puramente funzionale, anche per l'apprendimento automatico, ritenendo che la programmazione funzionale non abbia il concetto di memorizzare dati. Mi sembra di non essere d'accordo su questo punto con altre risposte.
Nella speranza alcuni saranno interessati nonostante la lunghezza di questa risposta.
Paradigmi computazionali
La domanda riguarda la programmazione funzionale (aka programmazione applicativa), un modello specifico di calcolo, il cui rappresentante teorico e più semplice è il calcolo lambda.
Se rimani a un livello teorico, ci sono molti modelli di calcolo: la macchina di Turing (TM), la macchina RAM e altri , il calcolo lambda, la logica combinatoria, la teoria delle funzioni ricorsive, i sistemi semi-Thue, ecc. Il più potente computazionale i modelli sono stati dimostrati equivalenti in termini di ciò a cui possono rivolgersi, e questo è l'essenza della
tesi di Church-Turing .
Un concetto importante è la riduzione reciproca dei modelli, che è la base per stabilire le equivalenze che portano alla tesi Church-Turing. Visto dal punto di vista dei programmatori, ridurre un modello a un altro è praticamente quello che viene solitamente chiamato un compilatore. Se prendi la programmazione logica come modello di calcolo, è molto diversa dal modello fornito dal PC acquistato in un negozio e il compilatore traduce i programmi scritti nel linguaggio di programmazione logica nel modello computazionale rappresentato dal tuo PC (praticamente il computer RAM).
β
In pratica, i linguaggi di programmazione che utilizziamo tendono a mescolare concetti di diverse origini teoriche, provando a farlo in modo che parti selezionate di un programma possano beneficiare delle proprietà di alcuni modelli ove appropriato. Allo stesso modo, le persone che costruiscono sistemi possono scegliere lingue diverse per componenti diversi, per adattare al meglio la lingua all'attività da svolgere.
Quindi, raramente vedi un paradigma di programmazione allo stato puro in un linguaggio di programmazione. I linguaggi di programmazione sono ancora classificati in base al paradigma dominante, ma le proprietà del linguaggio possono essere influenzate quando sono coinvolti concetti di altri paradigmi, spesso confondendo distinzioni e problemi concettuali.
Tipicamente, linguaggi come Haskell e ML o CAML sono considerati funzionali, ma possono consentire comportamenti imperativi ... Altrimenti perché si dovrebbe parlare del " sottoinsieme puramente funzionale "?
Quindi si può affermare che si può fare questo o quello nel mio linguaggio di programmazione funzionale, ma in realtà non risponde a una domanda sulla programmazione funzionale quando si basa su ciò che può essere considerato extra-funzionale.
Le risposte dovrebbero essere più precisamente correlate a un paradigma specifico, senza gli extra.
Cos'è una variabile?
Un altro problema è l'uso della terminologia. In matematica una variabile è un'entità che rappresenta un valore indeterminato in alcuni domini. È usato per vari scopi. Utilizzato in un'equazione, può rappresentare qualsiasi valore tale da verificare l'equazione. Questa visione è utilizzata nella programmazione logica sotto il nome di " variabile logica ", probabilmente perché la variabile nome aveva già un altro significato quando è stata sviluppata la programmazione logica.
Nella programmazione imperativa tradizionale, una variabile è intesa come una sorta di contenitore (o posizione di memoria) che può memorizzare la rappresentazione di un valore e possibilmente far sostituire il suo valore corrente con un altro).
Nella programmazione funzionale, una variabile ha lo stesso scopo che ha in matematica di un segnaposto per un certo valore, ancora da fornire. Nella programmazione imperativa tradizionale questo ruolo è in realtà svolto da costante (da non confondere con i letterali che sono determinati valori espressi con una notazione specifica per quel dominio di valori, come 123, true, ["abdcz", 3.14]).
Le variabili di qualunque tipo, oltre che costanti, possono essere rappresentate da identificatori.
È possibile modificare il valore della variabile imperativa e questa è la base della mutabilità. La variabile funzionale non può.
I linguaggi di programmazione di solito consentono di costruire entità più grandi da quelle più piccole nella lingua.
I linguaggi imperativi consentono a tali costrutti di includere variabili e questo è ciò che ti dà dati mutabili.
Come leggere un programma
Un programma è fondamentalmente una descrizione astratta del tuo algoritmo è un linguaggio, che sia un progetto pragmatico o un linguaggio paradigmaticamente puro.
In linea di principio, puoi prendere ogni affermazione per ciò che dovrebbe significare in modo astratto. Quindi il compilatore lo tradurrà in una forma appropriata per l'esecuzione del computer, ma questo non è il tuo problema in prima approssimazione.
Certo, la realtà è un po 'più dura, ed è spesso bene avere un'idea di ciò che accade in modo da evitare strutture che il compilatore non saprà affrontare per un'esecuzione efficiente. Ma questa è già ottimizzazione ... per la quale i compilatori possono essere molto utili, spesso meglio dei programmatori.
Programmazione funzionale e mutabilità
La mutabilità si basa sull'esistenza di variabili imperative che possono contenere valori, che possono essere modificati in base al compito. Dal momento che questi non esistono nella programmazione funzionale, tutto può essere visto come immutabile.
La programmazione funzionale si occupa esclusivamente di valori.
Le tue prime quattro affermazioni sull'immutabilità sono per lo più corrette, ma descrivono con una visione imperativa qualcosa che non è imperativo. È un po 'come descrivere con i colori in un mondo in cui ognuno è cieco. Stai usando concetti estranei alla programmazione funzionale.
Hai solo valori puri e una matrice di numeri interi è un valore puro. Per ottenere un altro array che differisce solo per un elemento, è necessario utilizzare un valore di array diverso. La modifica di un elemento è solo un concetto che non esiste in questo contesto. Potresti avere una funzione che ha un array e alcuni indici come argomento e restituisce un risultato che è un array quasi identico che differisce solo dove indicato dagli indici. Ma è ancora un valore di array indipendente. Come vengono rappresentati questi valori non è un tuo problema. Forse "condividono" molto nella traduzione imperativa per il computer ... ma quello è il lavoro del compilatore ... e non vuoi nemmeno sapere per quale tipo di architettura della macchina sta compilando.
Tu non copi i valori (non ha senso, è un concetto alieno). Utilizzi solo i valori esistenti nei domini che hai definito nel tuo programma. O li descrivi (come letterali) o sono il risultato dell'applicazione di una funzione ad altri valori. Puoi dare loro un nome (definendo così una costante) per assicurarti che lo stesso valore sia usato in diversi punti del programma. Si noti che l'applicazione della funzione non deve essere percepita come un calcolo ma come il risultato dell'applicazione agli argomenti forniti. Scrivere 5+2
o scrivere 7
equivale allo stesso. Che è coerente con il paragrafo precedente.
Non ci sono variabili imperative. Nessun incarico è possibile. È possibile associare i nomi solo ai valori (per formare costanti), diversamente dalle lingue imperative in cui è possibile associare i nomi a variabili assegnabili.
Non è chiaro se ciò abbia un costo in complessità. Per prima cosa, ti riferisci alla complessità riguarda paradigmi imperativi. Non è definito come tale per la programmazione funzionale, a meno che non si scelga di leggere un programma funzionale come imperativo, che non è l'intento del progettista. In effetti, la visione funzionale ha lo scopo di non preoccuparti di tali problemi e di concentrarti su ciò che viene calcolato. È un po 'come le specifiche rispetto all'implementazione.
Il compilatore deve occuparsi dell'implementazione ed essere abbastanza intelligente da adattare al meglio ciò che deve essere fatto all'hardware che lo farà, qualunque esso sia.
Non sto dicendo che i programmatori non se ne preoccupino mai. Inoltre non sto dicendo che i linguaggi di programmazione e la tecnologia del compilatore siano maturi come potremmo desiderare che siano.
Rispondere alle domande
Non si modifica il valore esistente (concetto alieno), ma si calcolano nuovi valori che differiscono dove desiderato, possibilmente avendo un elemento in più è un elenco.
Il programma può ottenere nuovi dati. Il punto è come lo esprimi nella lingua. Ad esempio, puoi considerare che il programma funziona con un valore specifico, possibilmente di dimensioni illimitate, chiamato flusso di input. È un valore che dovrebbe essere seduto lì (se è già noto completamente o no non è un tuo problema). Quindi hai una funzione che restituisce una coppia composta dal primo elemento del flusso e dal resto del flusso.
Puoi usarlo per costruire reti di componenti comunicanti in un modo puramente applicativo (coroutine)
L'apprendimento automatico è solo un altro problema quando si devono cancellare dati e modificare i valori. Nella programmazione funzionale non lo fai: devi solo calcolare nuovi valori che differiscono in modo appropriato in base ai dati di allenamento. Anche la macchina risultante funzionerà. Ciò di cui ti preoccupi è il tempo di elaborazione e l'efficienza dello spazio. Ma, ancora una volta, questo è un problema diverso, che idealmente dovrebbe essere affrontato dal compilatore.
Osservazioni finali
È abbastanza chiaro, dai commenti o dalle altre risposte, che i linguaggi di programmazione funzionale funzionale non sono puramente funzionali. Ciò riflette il fatto che la nostra tecnologia deve ancora essere migliorata, soprattutto per quanto riguarda la compilazione.
È possibile scrivere in uno stile puramente applicativo? La risposta è nota da circa 40 anni ed è "sì". Lo scopo stesso della semantica denotazionale come appariva negli anni '70 era proprio quello di tradurre (compilare) le lingue in uno stile puramente funzionale, ritenuto meglio compreso matematicamente e quindi considerato un migliore fondo per definire la semantica dei programmi.
L'aspetto interessante è che la struttura di programmazione imperativa, comprese le variabili, può essere tradotta in uno stile funzionale introducendo domini di valori appropriati, come un archivio dati. E nonostante lo stile funzionale, rimane sorprendentemente simile al codice dei compilatori reali scritti in stile imperativo.