Como eu construo um sistema que possui todos os seguintes itens :
- Usando funções puras com objetos imutáveis.
- Somente passe para os dados de uma função a função necessária, não mais (isto é, nenhum objeto grande de estado do aplicativo)
- Evite ter muitos argumentos para funções.
- Evite ter que construir novos objetos apenas para empacotar e descompactar parâmetros para funções, simplesmente para evitar que muitos parâmetros sejam passados para funções. Se eu vou empacotar vários itens em uma função como um único objeto, quero que esse objeto seja o proprietário desses dados, não algo construído temporariamente
Parece-me que a mônada do Estado infringe a regra nº 2, embora não seja óbvia porque é tecida através da mônada.
Tenho a sensação de que preciso usar as lentes de alguma forma, mas muito pouco está escrito sobre isso para idiomas não funcionais.
fundo
Como exercício, estou convertendo um dos meus aplicativos existentes de um estilo orientado a objetos para um estilo funcional. A primeira coisa que estou tentando fazer é criar o máximo possível do núcleo interno do aplicativo.
Uma coisa que ouvi é que como gerenciar o "Estado" em uma linguagem puramente funcional, e é isso que acredito ser feito pelas mônadas do Estado, é que, logicamente, você chama uma função pura ", passando pelo estado do mundo como está ", e quando a função retornar, ela retornará para você o estado do mundo conforme foi alterado.
Para ilustrar, a maneira como você pode fazer um "olá mundo" de uma maneira puramente funcional é como, você passa em seu programa esse estado da tela e recebe de volta o estado da tela com "olá mundo" impresso. Portanto, tecnicamente, você está fazendo uma chamada para uma função pura e não há efeitos colaterais.
Com base nisso, examinei meu aplicativo e: 1. Primeiro, coloquei todo o estado do meu aplicativo em um único objeto global (GameState). 2. Segundo, tornei o GameState imutável. Você não pode mudar isso. Se você precisar de uma alteração, precisará criar uma nova. Fiz isso adicionando um construtor de cópia, que opcionalmente utiliza um ou mais campos que foram alterados. 3. Para cada aplicativo, passo o GameState como parâmetro. Dentro da função, depois de fazer o que vai fazer, ele cria um novo GameState e o devolve.
Como eu tenho um núcleo funcional puro e um loop externo que alimenta esse GameState no loop principal do fluxo de trabalho do aplicativo.
Minha pergunta:
Agora, meu problema é que, o GameState possui cerca de 15 objetos imutáveis diferentes. Muitas das funções no nível mais baixo operam apenas em alguns desses objetos, como a pontuação. Então, digamos que eu tenho uma função que calcula a pontuação. Hoje, o GameState é passado para essa função, que modifica a pontuação criando um novo GameState com uma nova pontuação.
Algo sobre isso parece errado. A função não precisa da totalidade do GameState. Ele só precisa do objeto Score. Então eu atualizei para passar a Pontuação e retornar apenas a Pontuação.
Isso parecia fazer sentido, então fui além com outras funções. Algumas funções exigiriam que eu passasse 2, 3 ou 4 parâmetros do GameState, mas como usei o padrão por todo o núcleo externo do aplicativo, estou passando cada vez mais o estado do aplicativo. Como, no topo do loop do fluxo de trabalho, eu chamaria um método, que chamaria um método que chamaria um método etc., até o local onde a pontuação é calculada. Isso significa que a pontuação atual é transmitida por todas essas camadas apenas porque uma função na parte inferior calcula a pontuação.
Então agora eu tenho funções com algumas vezes dezenas de parâmetros. Eu poderia colocar esses parâmetros em um objeto para diminuir o número de parâmetros, mas gostaria que essa classe fosse o local principal do estado do aplicativo de estado, em vez de um objeto que é simplesmente construído no momento da chamada, para evitar passar em vários parâmetros e descompacte-os.
Então agora estou me perguntando se o problema que tenho é que minhas funções estão aninhadas muito profundamente. Esse é o resultado de querer ter funções pequenas, então refatoro quando uma função fica grande e a divido em várias funções menores. Mas fazer isso produz uma hierarquia mais profunda, e qualquer coisa passada para as funções internas precisa ser passada para a função externa, mesmo que a função externa não esteja operando diretamente nesses objetos.
Parecia que simplesmente passar no GameState ao longo do caminho evitava esse problema. Mas estou de volta ao problema original de passar mais informações para uma função do que a função precisa.