Em uma resposta a esta pergunta (escrita por Pete), existem algumas considerações sobre POO versus PF. Em particular, sugere-se que as linguagens FP não sejam muito adequadas para modelar objetos (persistentes) que tenham uma identidade e um estado mutável.
Eu queria saber se isso é verdade ou, em outras palavras, como modelar objetos em uma linguagem de programação funcional. Pelo meu conhecimento básico de Haskell, pensei que alguém poderia usar mônadas de alguma forma, mas realmente não sei o suficiente sobre esse tópico para encontrar uma resposta clara.
Então, como as entidades com uma identidade e um estado persistente mutável normalmente são modeladas em uma linguagem funcional?
Aqui estão alguns detalhes adicionais para esclarecer o que tenho em mente. Pegue um aplicativo Java típico no qual eu possa (1) ler um registro de uma tabela de banco de dados em um objeto Java, (2) modificar o objeto de maneiras diferentes, (3) salvar o objeto modificado no banco de dados.
Como isso seria implementado, por exemplo, em Haskell? Inicialmente, eu lia o registro em um valor de registro (definido por uma definição de dados), realizava diferentes transformações aplicando funções a esse valor inicial (cada valor intermediário é uma nova cópia modificada do registro original) e depois escrevia o valor final do registro para o banco de dados.
Isso é tudo o que existe? Como posso garantir que, a cada momento, apenas uma cópia do registro seja válida / acessível? Não se deseja que diferentes valores imutáveis representem diferentes instantâneos do mesmo objeto para serem acessíveis ao mesmo tempo.
Respostas:
A maneira usual de realizar mudanças de estado em uma linguagem pura como Haskell é modelá-las como funções que pegam o estado antigo e retornam uma versão modificada. Mesmo para objetos complexos, isso é eficiente devido à preguiçosa estratégia de avaliação de Haskell - mesmo que você esteja criando sintaticamente um novo objeto, ele não é copiado na sua totalidade; cada campo é avaliado apenas quando necessário.
Se você tiver mais do que algumas mudanças de estado local, as coisas podem se tornar desajeitadas, e é aí que as mônadas entram. O paradigma da mônada pode ser usado para encapsular um estado e suas mudanças; o exemplo do livro é a
State
mônada que vem com uma instalação padrão do Haskell. Note, no entanto, que uma mônada não é nada de especial: é apenas um tipo de dados que expõe dois métodos (>>=
ereturn
) e atende a algumas expectativas (as 'leis da mônada'). Sob o capô, a mônada do Estado faz exatamente o mesmo: pegue o estado antigo e retorne um estado modificado; somente a sintaxe é melhor.fonte
Eu não sou desenvolvedor de linguagem funcional, então, por favor, me atire em chamas se eu estiver errado, mas se estiver certo, pode ser uma analogia interessante.
Me disseram uma vez que o Excel é essencialmente uma linguagem funcional. Não estou falando sobre VBA e assim por diante, estou falando sobre o que acontece na planilha.
Portanto, você tem entradas, que podem ser tabulares, células individuais ou intervalos nomeados, etc., e através de uma cadeia de operações, você acaba com um resultado ou com muitos resultados. Mude uma entrada e ela flui imediatamente.
Essa analogia mantém a água?
fonte
Presumo que você esteja falando sobre a característica apátrida da linguagem funcional pura.
Você toma o estado inicial como uma entrada e retorna o estado final como uma saída. Nada fundamentalmente diferente do que você faz em uma linguagem com uma noção de estado, exceto que você é mais explícito a respeito e pode ser tedioso e menos eficiente (*).
Observe que você não precisa necessariamente modificar todos os locais que referenciam seu objeto: eles podem conter um token e, em seguida, basta modificar a estrutura de dados que mapeia os tokens para o estado atual. Até então, você está implementando um sistema completo do estado usando uma linguagem sem estado e recebe de volta os problemas dos idiomas completos do estado.
(*) Por exemplo, procurei - e não encontrei - a estrutura de dados que me permite retornar em O (1) uma cópia modificada de uma estrutura de dados indexável por números inteiros consecutivos em O (1) também.
fonte
[(Int, v)]
permitirá que você substitua o valor em qualquer índice em tempo constante, simplesmente aceitando o novo valor. Desvantagens: o valor antigo ainda está usando RAM e a pesquisa é linear.Em resumo, cada estado de uma entidade é sua própria entidade. Portanto, na sua lógica de negócios clássica de "pedido", há uma
Order
entidade e umaOrderVersion
entidade. Ambos são imutáveis. A lógica que adiciona um item de linha pega a versão antiga e a novaOrderLineItemVersion
como entrada e retorna uma novaOrderVersion
entidade.Isso facilita algumas coisas (principalmente a funcionalidade "desfazer"), mas algumas são mais difíceis.
fonte