Pergunta

Eu preciso de ajuda para a minha cabeça em torno da diferença entre o meu atual noção OOP de Estado, e da forma como seria feito em uma linguagem funcional como Haskell ou Clojure.

Para usar um exemplo banal, digamos que estamos lidando com objetos de conta bancária simplificada / estruturas / whatever. Em uma linguagem OOP, eu teria alguma classe mantendo uma referência a um BankAccount, que teria variáveis ??de instância para coisas como taxa de juros e métodos como setInterestRate () que mudam o estado do objeto e geralmente retornar nada. Em digamos Clojure, eu teria um struct-conta bancária (a hashmap glorificado) e funções especiais que levam um parâmetro de conta bancária e outras informações, e retornar uma nova estrutura. Então, ao invés de mudar o estado do objeto original, agora tenho um novo que estão sendo devolvidos, com as modificações desejadas.

Então ... o que eu faço com ele? Overwrite qualquer que seja variável foi referenciando a conta bancária de idade? Se assim for, isso tem vantagens sobre a abordagem OOP de mudança de estado? No final, em ambos os casos, parece um tem uma variável que faz referência ao objeto com as mudanças necessárias. Retardado como eu sou, eu só tenho um vago conceito do que está acontecendo.

Espero que fazia sentido, obrigado por qualquer ajuda!

Foi útil?

Solução

Em um estilo funcional pura, você nunca vai substituir qualquer variável.

Uma analogia seria a de espaço-tempo na física. Se você considerar o mundo como 3d, então os objetos não têm posições fixas - eles se movem ao longo do tempo. Para trazer a matemática para suportar sobre o mundo físico, portanto, adicionar uma dimensão de tempo e considerar os valores de várias propriedades em determinadas épocas. Ao fazê-lo, fizemos os objetos de nosso estudo em constantes. Da mesma forma, na programação, há uma simplicidade conceitual para ser tido por trabalhar com valores imutáveis. Objetos com uma identidade no mundo real pode ser modelado como uma sequência de valores imutáveis ??(os estados do objeto, por vezes aumentando) em vez de um único valor que alterações.

É claro que os detalhes de como associar a seqüência de valores para uma "identidade de objeto" pode ser um pouco peludo. Haskell tem Monads que permitem estado do modelo. Funcional programação Reactive é uma tentativa mais literal na modelagem de objetos no mundo, com atualizações funcionais puros, que eu acho que é uma direção muito promissor para a programação.

I notarão que Clojure, ao contrário de Haskell, não é puro, e você pode atualizar variáveis ??como você sugeriu. Se você estiver atualizando apenas alguns variáveis ??em um nível alto, você ainda vai provavelmente desfrutar de muitos dos benefícios simplicidade conceitual de programação funcional.

Outras dicas

Provavelmente no mundo OO você tem um loop e está modificando estas contas bancárias e outra vez em resposta às solicitações. Vamos supor que você tem toda uma carteira de contas e estes têm digitar Portfolio. Então, em Haskell você iria escrever uma função pura

updatePortfolio :: Request -> Portfolio -> Portfolio

E o seu loop principal pode ler as solicitações de entrada padrão e manter a sua carteira até à data. (O exemplo não é muito útil a menos que você pode escrever a carteira bem, mas é mais simples.)

readRequest :: IO Request  -- an action that, when performed, reads a Request with side effects

main :: Portfolio -> IO ()  -- a completely useless program that updates a Portfolio in response to a stream of Requests

main portfolio = do req <- readRequest
                    main (updatePortfolio req)

e agora eu espero que você veja o que aconteceu ao seu estado mutável: em um programa funcional típico, afirmam que mudanças é passado como um parâmetro para uma função. Quando o changess estado, você fazer uma nova chamada de função. A chamada está em posição de cauda (você pode olhar para cima 'chamada de cauda adequada') e por isso não utilizar quaisquer recursos adicionais, e de fato quando o compilador gera assembly de código gera um loop, e vai manter o ponteiro para o em constante mudança Portfolio num registo.

Este é um exemplo muito brinquedo, mas espero que lhe dá um pouco do sabor de uma linguagem funcional.

Então ... o que eu faço com ele? Overwrite qualquer que seja variável foi referenciando a conta bancária de idade?

Sim

Em caso afirmativo, que tem vantagens sobre a abordagem OOP de mudança de estado?

Digamos que o cálculo de qualquer ação que você faz no que struct leva muito tempo e algo acontece no meio do caminho e você precisa voltar para a estrutura original ou o cálculo levantou um erro. Com a interpretação que você apresentou para mim de OO (usando uma referência, porque você pode ter uma linguagem OO imutável) que os dados poderiam ser desconhecido do --é corrupto a menos que suficiente informação foi dada a partir da chamada de função falhou, e permite sugerir que falhou seriamente. Em uma abordagem funcional você sabe com certeza que a sua estrutura de dados original está correta --because você inicialmente feito uma cópia.

Estender este cenário em aplicações multi-threaded. Nós podemos garantir que ninguém mais está usando a estrutura de dados que são uma vez que todos nós temos nossa própria versão do mesmo.

Além disso, podemos economizar espaço usando dados da outra estrutura que estamos copiando. Um exemplo clássico é quando a adição de um elemento para a cabeça de uma lista. Se tivermos um ponteiro para o segundo elemento, e um ponteiro para o primeiro elemento se pode fazer referência a ambas as listas com apenas o tamanho do primeiro (ver abaixo). Sem imutabilidade não podemos garantir isso.

        b__
           |  
a -> [6|] -+-> [5|] -> [4|] -> [3|] -> [2|] -> [1|x]

Olhe para Haskell, que é uma pura funcional linguagem não tem re-atribuição que seja, assim como há outros efeitos colaterais: a fim de fazer IO, na IO mônada construí-la na verdade substitui o RealWorld com um nova instância do mundo que tem, por exemplo, o novo texto exibido no console.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top