Você tem estado ao associar valores (números, seqüências de caracteres, estruturas de dados complexas) a uma identidade e um ponto no tempo.
Por exemplo, o número 10 por si só não representa nenhum estado: é apenas um número bem definido e sempre será ele mesmo: o número natural 10. Como outro exemplo, a cadeia "HELLO" é uma sequência de cinco caracteres e é completamente descrito pelos caracteres que contém e pela sequência em que aparecem. Daqui a cinco milhões de anos, a string "HELLO" ainda será a string "HELLO": um valor puro.
Para ter estado, você deve considerar um mundo em que esses valores puros estejam associados a algum tipo de entidade que possui uma identidade . Identidade é uma idéia primitiva: significa que você pode distinguir duas coisas, independentemente de outras propriedades que possam ter. Por exemplo, dois carros do mesmo modelo, mesma cor, ... são dois carros diferentes.
Dadas essas coisas com identidade, você pode anexar propriedades a elas, descritas por valores puros. Por exemplo, meu carro tem a propriedade de ser azul. Você pode descrever esse fato associando o par
("colour", "blue")
para o meu carro. O par ("cor", "azul") é um valor puro que descreve o estado desse carro em particular.
O estado não está associado apenas a uma entidade específica, mas também a um momento específico. Então, você pode dizer que hoje, meu carro tem estado
("colour", "blue")
Amanhã vou tê-lo repintado em preto e o novo estado será
("colour", "black")
Observe que o estado de uma entidade pode mudar, mas sua identidade não muda por definição. Bem, desde que a entidade exista, é claro: um carro pode ser criado e destruído, mas manterá sua identidade por toda a vida. Não faz sentido falar sobre a identidade de algo que ainda não existe.
Se os valores das propriedades anexadas a uma determinada entidade mudarem com o tempo, você diz que o estado dessa entidade é mutável . Caso contrário, você diz que o estado é imutável .
A implementação mais comum é armazenar o estado de uma entidade em algum tipo de variável (variáveis globais, variáveis de membros de objetos), ou seja, armazenar o instantâneo atual de um estado. O estado mutável é implementado usando a atribuição: cada operação de atribuição substitui o instantâneo anterior por um novo. Esta solução normalmente usa locais de memória para armazenar o instantâneo atual. Substituir um local de memória é uma operação destrutiva que substitui um instantâneo por um novo. ( Aqui você encontra uma conversa interessante sobre essa abordagem de programação orientada a locais .)
Uma alternativa é visualizar os estados subseqüentes (histórico) de uma entidade como um fluxo (possivelmente sequência infinita) de valores, consulte, por exemplo, o Capítulo 3 do SICP . Nesse caso, cada instantâneo é armazenado em um local de memória diferente e o programa pode examinar diferentes instantâneos ao mesmo tempo. As capturas instantâneas não utilizadas podem ser coletadas como lixo quando não são mais necessárias.
Vantagens / desvantagens das duas abordagens
- A abordagem 1 consome menos memória e permite construir um novo instantâneo com mais eficiência, pois não envolve cópia.
- A abordagem 1 empurra implicitamente o novo estado para todas as partes de um programa com uma referência a ele; a abordagem 2 precisaria de algum mecanismo para enviar um instantâneo a seus observadores, por exemplo, na forma de um evento.
- A abordagem 2 pode ajudar a evitar erros de estado inconsistentes (por exemplo, atualizações parciais de estado): definindo uma função explícita que produz um novo estado a partir de um antigo, é mais fácil distinguir entre os instantâneos produzidos em diferentes momentos no tempo.
- A abordagem 2 é mais modular, pois permite produzir facilmente visualizações no estado que são independentes do próprio estado, por exemplo, usando funções de ordem superior, como
map
e filter
.