Linguagens funcionais, por definição, não devem manter variáveis de estado. Por que, então, Haskell, Clojure e outros fornecem implementações de memória transacional de software (STM)? Existe um conflito entre duas abordagens?
Linguagens funcionais, por definição, não devem manter variáveis de estado. Por que, então, Haskell, Clojure e outros fornecem implementações de memória transacional de software (STM)? Existe um conflito entre duas abordagens?
Respostas:
Não há nada de errado em uma linguagem funcional manter um estado mutável. Mesmo linguagens funcionais "puras", como Haskell, precisam manter o estado para interagir com o mundo real. Linguagens funcionais "impuras" como o Clojure permitem efeitos colaterais que podem incluir um estado de mutação.
O ponto principal é que as linguagens funcionais desencorajam o estado mutável, a menos que você realmente precise . O estilo geral é programar usando funções puras e dados imutáveis e interagir apenas com o estado mutável "impuro" nas partes específicas do seu código que o exigem. Dessa forma, você pode manter o restante da sua base de código "pura".
Eu acho que há várias razões pelas quais o STM é mais comum em linguagens funcionais:
Pessoalmente, gosto da abordagem de Clojure de permitir a mutabilidade, mas apenas no contexto de "referências gerenciadas" estritamente controladas que podem participar de transações do STM. Todo o resto da linguagem é "puramente funcional".
;; define two accounts as managed references
(def account-a (ref 100))
(def account-b (ref 100))
;; define a transactional "transfer" function
(defn transfer [ref-1 ref-2 amount]
(dosync
(if (>= @ref-1 amount)
(do
(alter ref-1 - amount)
(alter ref-2 + amount))
(throw (Error. "Insufficient balance!")))))
;; make a stranfer
(transfer account-a account-b 75)
;; inspect the accounts
@account-a
=> 25
@account-b
=> 175
Observe que o código acima é totalmente transacional e atômico - um observador externo que lê os dois saldos em outra transação sempre verá um estado atômico consistente, ou seja, os dois saldos sempre somam 200. Com a concorrência baseada em bloqueio, esse é um problema surpreendentemente difícil resolver em um grande sistema complexo com muitas entidades transacionais.
Para uma iluminação extra, Rich Hickey faz um excelente trabalho ao explicar o STM de Clojure neste vídeo
Linguagens funcionais, por definição, não devem manter variáveis de estado
Sua definição está errada. Linguagem que não pode manter o estado simplesmente não pode ser usada.
A diferença entre linguagens funcionais e imperativas não é que uma delas tenha estado e a outra não. É de certa forma que eles mantêm o estado.
Linguagens imperativas têm estado espalhado por todo o programa.
Linguagens funcionais isolam e mantêm o estado explicitamente por meio de assinaturas de tipo. E é por isso que eles fornecem mecanismos sofisticados de gerenciamento de estado, como o STM.
Às vezes, um programa requer um estado mutável (por exemplo, conteúdo de banco de dados para um aplicativo Web) e seria ótimo poder usá-lo sem perder os benefícios da programação funcional. Em linguagens não funcionais, o estado mutável permeia tudo. Se você o tornar explícito com algum tipo de API especial , poderá confiná-lo a uma pequena região identificável, enquanto todo o resto permanecerá puramente funcional. Os benefícios do FP incluem depuração mais fácil, teste de unidade repetível, simultaneidade indolor e compatibilidade com vários núcleos / GPU.