Conforme observado por @KarlBielefeldt, a abordagem funcional para esse problema é vê-lo como retornando um novo estado de um estado anterior. As funções em si não contêm nenhuma informação e, portanto, sempre atualizam o estado m para o estado n .
Eu acho que você acha isso ineficiente porque assume que o estado anterior deve ser mantido na memória ao computar o novo estado. Observe que a escolha entre escrever um estado completamente novo ou reescrever o antigo é um detalhe da implementação do ponto de vista de uma linguagem funcional.
Por exemplo, digamos que eu tenha uma lista de um milhão de números inteiros e queira aumentar o décimo em uma unidade. Copiar a lista inteira com um novo número em sua décima posição é um desperdício, você está certo; mas é apenas a maneira conceitual de descrever a operação para o compilador ou intérprete de linguagem. O compilador ou intérprete é livre para pegar a primeira lista e substituir a décima posição.
A vantagem de descrever a operação dessa maneira é que o compilador pode raciocinar sobre a situação em que muitos encadeamentos desejam atualizar a mesma lista em posições diferentes. Se a operação for descrita como "vá para esta posição e sobrescreva o que você encontra", será o programador, e não o compilador, responsável por garantir que as sobrescrições não colidam.
Com tudo isso dito, mesmo em Haskell, existe uma mônada do Estado que ajuda a modelar situações em que "manter o estado" é uma solução mais intuitiva para um problema. Mas observe também alguns problemas que você acha que " inerentemente com estado, como gravar em um banco de dados " têm soluções imutáveis como o Datomic . Isso pode ser surpreendente até você entender que é um conceito, não necessariamente sua realização.