Pamiętaj, że jeśli to Ci odpowiada, ale w językach funkcjonalnych, takich jak Standard ML, domyślnie wszystko jest niezmienne. Mutacja jest wspierana przez ogólny ref
typ erence. Więc int
zmienna jest niezmienna, a ref int
zmienna jest zmienny pojemnik na int
s. Zasadniczo zmienne są rzeczywistymi zmiennymi w sensie matematycznym (nieznana, ale stała wartość), a ref
s są „zmiennymi” w sensie programowania imperatywnego - komórką pamięci, z której można zapisywać i odczytywać. (Lubię nazywać je przypisywalnymi ).
Myślę, że problem const
jest dwojaki. Po pierwsze, C ++ nie ma funkcji wyrzucania elementów bezużytecznych, co jest konieczne, aby mieć nietrywialne trwałe struktury danych . const
musi być głęboki, aby mieć jakikolwiek sens, jednak posiadanie w pełni niezmiennych wartości w C ++ jest niepraktyczne.
Po drugie, w C ++ musisz się zdecydować, const
a nie zrezygnować. Ale kiedy zapomnisz o const
czymś, a później to naprawisz, skończysz w sytuacji „zatruć const”, o której mowa w odpowiedzi @ RobY, gdzie const
zmiana będzie kaskadowo w całym kodzie. Gdyby const
było to ustawienie domyślne, nie zobaczyłbyś, że aplikujesz z const
mocą wsteczną. Dodatkowo konieczność dodawania const
wszędzie powoduje znaczny szum w kodzie.
Podejrzewam, że późniejsze języki głównego nurtu (np. Java) były silnie ukształtowane przez sukces i sposób myślenia C i C ++. W tym przypadku nawet w przypadku wyrzucania elementów bezużytecznych większość interfejsów API do zbierania języków zakłada zmienne struktury danych. Fakt, że wszystko jest zmienne i niezmienność jest postrzegany jako przypadek narożny, mówi wiele o imperatywnym sposobie myślenia za popularnymi językami.
EDYCJA : Po przemyśleniu komentarza Greenoldmana zrozumiałem, że const
nie chodzi bezpośrednio o niezmienność danych; const
koduje w typie metody, czy ma ona skutki uboczne dla instancji.
Możliwe jest użycie mutacji, aby osiągnąć referencyjnie przejrzyste zachowanie. Załóżmy, że masz funkcję, która wywoływana sukcesywnie zwraca różne wartości - na przykład funkcję, która odczytuje pojedynczy znak stdin
. Możemy użyć pamięci podręcznej / zapamiętać wyniki tej funkcji, aby uzyskać referencyjnie przejrzysty strumień wartości. Strumień byłby połączoną listą, której węzły wywołują funkcję przy pierwszej próbie odzyskania ich wartości, ale następnie buforują wynik. Więc jeśli stdin
constains Hello, world!
, po raz pierwszy spróbować pobrać wartość pierwszego węzła, to będzie czytać jedną char
i powrotu H
. Następnie będzie nadal wracał H
bez dalszych wezwań do przeczytania char
. Podobnie drugi węzeł odczytałby char
zstdin
przy pierwszej próbie odzyskania jego wartości, tym razem zwracamy e
i buforujemy ten wynik.
Interesującą rzeczą jest to, że zamieniłeś proces z natury stanowy w obiekt, który na pozór jest bezpaństwowy. Aby to osiągnąć, konieczna była jednak mutacja stanu wewnętrznego obiektu (poprzez buforowanie wyników) - mutacja była łagodnym efektem . Nie da się uczynić naszego, CharStream
const
nawet jeśli strumień zachowuje się jak niezmienna wartość. Teraz wyobraź sobie, że istnieje Stream
interfejs z const
metodami i oczekują tego wszystkie twoje funkcje const Streams
. Twój CharStream
nie może implementować interfejs!
( EDYCJA 2: Najwyraźniej istnieje słowo kluczowe C ++ mutable
, które pozwala nam oszukiwać i tworzyćCharStream
const
. Jednak ta luka niszczy const
gwarancje - teraz naprawdę nie możesz być pewien, że coś nie zmutuje za pomocą swoich const
metod. Przypuszczam, że to nie tak źle, ponieważ musisz wyraźnie poprosić o lukę, ale nadal całkowicie polegasz na systemie honoru).
Po drugie, załóżmy, że masz funkcje wyższego rzędu - możesz przekazać funkcje jako argumenty do innych funkcji. const
ness jest częścią sygnatury funkcji, więc nie można przekazywać const
niefunkcji jako argumentów funkcji, które oczekują const
funkcji. Ślepe egzekwowanie const
tutaj doprowadziłoby do utraty ogólności.
Wreszcie, manipulowanie const
obiektem nie gwarantuje, że nie mutuje on jakiegoś zewnętrznego (statycznego lub globalnego) stanu za twoimi plecami, więc const
gwarancje nie są tak silne, jak się początkowo wydają.
Nie jest dla mnie jasne, czy kodowanie obecności lub braku efektów ubocznych w systemie typów jest ogólnie dobrą rzeczą.