As linguagens de programação funcionais não permitem efeitos colaterais?

10

Segundo a Wikipedia, linguagens de programação funcional , declarativas, eles não permitem efeitos colaterais. A programação declarativa em geral, tenta minimizar ou eliminar os efeitos colaterais.

Além disso, de acordo com a Wikipedia, um efeito colateral está relacionado a alterações de estado. Portanto, as linguagens de programação funcional, nesse sentido, na verdade eliminam os efeitos colaterais, pois não salvam nenhum estado.

Mas, além disso, um efeito colateral tem outra definição. Efeito colateral

tem uma interação observável com suas funções de chamada ou com o mundo exterior, além de retornar um valor. Por exemplo, uma função específica pode modificar uma variável global ou variável estática, modificar um de seus argumentos, gerar uma exceção, gravar dados em um monitor ou arquivo, ler dados ou chamar outras funções com efeito colateral.

Nesse sentido, as linguagens de programação funcional permitem efeitos colaterais, pois existem inúmeros exemplos de funções que afetam seu mundo exterior, chamando outras funções, levantando exceções, escrevendo em arquivos etc.

Então, finalmente, as linguagens de programação funcional permitem efeitos colaterais ou não?

Ou, não entendo o que qualifica como "efeito colateral", portanto, as linguagens imperativas permitem e as declarativas não. De acordo com o exposto acima e o que recebo, nenhum idioma elimina os efeitos colaterais, então, ou estou perdendo algo sobre os efeitos colaterais, ou a definição da Wikipedia é incorretamente ampla.

codebot
fonte

Respostas:

26

A programação funcional inclui muitas técnicas diferentes. Algumas técnicas são boas com efeitos colaterais. Mas um aspecto importante é o raciocínio equacional : se eu chamo uma função com o mesmo valor, sempre obtenho o mesmo resultado. Portanto, posso substituir uma chamada de função pelo valor de retorno e obter um comportamento equivalente. Isso facilita o raciocínio sobre o programa, especialmente durante a depuração.

Caso a função tenha efeitos colaterais, isso não se aplica. O valor de retorno não é equivalente à chamada da função, porque o valor de retorno não contém os efeitos colaterais.

A solução é parar de usar efeitos colaterais e codificá-los no valor de retorno . Idiomas diferentes têm sistemas de efeitos diferentes. Por exemplo, Haskell usa mônadas para codificar certos efeitos, como IO ou mutação de estado. As linguagens C / C ++ / Rust possuem um sistema de tipos que pode impedir a mutação de alguns valores.

Em uma linguagem imperativa, uma print("foo")função imprimirá algo e não retornará nada. Em uma linguagem funcional pura como Haskell, uma printfunção também pega um objeto que representa o estado do mundo exterior e retorna um novo objeto que representa o estado depois de executar essa saída. Algo parecido com newState = print "foo" oldState. Eu posso criar quantos estados novos quiser do estado antigo. No entanto, apenas um será usado pela função principal. Então, preciso sequenciar os estados de várias ações, encadeando as funções. Para imprimir foo bar, posso dizer algo como print "bar" (print "foo" originalState).

Se um estado de saída não for usado, o Haskell não executará as ações anteriores àquele estado, porque é uma linguagem lenta. Por outro lado, essa preguiça só é possível porque todos os efeitos são explicitamente codificados como valores de retorno.

Observe que Haskell é a única linguagem funcional comumente usada que usa essa rota. Outras linguagens funcionais incl. a família Lisp, a família ML e as linguagens funcionais mais recentes, como Scala, desencorajam, mas permitem efeitos colaterais ainda - elas podem ser chamadas de linguagens funcionais imperativas.

Usar efeitos colaterais para E / S provavelmente é bom. Freqüentemente, a E / S (exceto a criação de log) é feita apenas no limite externo do seu sistema. Nenhuma comunicação externa acontece dentro da sua lógica de negócios. Em seguida, é possível escrever o núcleo do seu software em um estilo puro, enquanto ainda executa E / S impuras em um shell externo. Isso também significa que o núcleo pode ser sem estado.

A apatridia possui várias vantagens práticas, como maior razoabilidade e escalabilidade. Isso é muito popular para back-end de aplicativos da web. Qualquer estado é mantido fora, em um banco de dados compartilhado. Isso facilita o balanceamento de carga: não preciso colar sessões em um servidor específico. E se eu precisar de mais servidores? Basta adicionar outro, porque ele está usando o mesmo banco de dados. E se um servidor travar? Posso refazer qualquer solicitação pendente em outro servidor. Claro, ainda há estado - no banco de dados. Mas eu o expliquei e extraí, e poderia usar uma abordagem funcional pura internamente, se eu quisesse.

amon
fonte
Obrigado pela resposta detalhada. O que eu mantenho como conclusão é que os efeitos colaterais não afetam o valor da função, devido ao raciocínio equacional, é por isso que "linguagens funcionais não permitem / minimizam efeitos colaterais". Os efeitos incorporados nos valores das funções afetam e alteram o estado que é salvo - ou salvo fora do núcleo do programa. Além disso, a E / S acontece no limite externo da lógica de negócios.
Codebot
3
@ codebot, não exatamente, na minha opinião. Se implementado corretamente, os efeitos colaterais na programação funcional devem refletir o tipo de retorno da função. Por exemplo, se uma função falhar (se um arquivo específico não existir ou uma conexão com o banco de dados não puder ser estabelecida), o tipo de retorno da função deverá encapsular a falha, em vez de fazer com que a função gere uma exceção. Dê uma olhada em Programação Orientada a Ferrovia para obter um exemplo ( fsharpforfunandprofit.com/posts/recipe-part2 ).
Aaron M. Eshbach
"... eles poderiam ser chamados de linguagens imperativas-funcionais.": Simon Peyton Jones escreveu "... Haskell é a melhor linguagem de programação imperativa do mundo".
Giorgio
5

Nenhuma linguagem de programação elimina efeitos colaterais. Eu acho que é melhor dizer que linguagens declarativas contêm efeitos colaterais, enquanto linguagens imperativas não. No entanto, não tenho tanta certeza de que alguma dessas conversas sobre efeitos colaterais chegue à diferença fundamental entre os dois tipos de idiomas e que realmente pareça o que você está procurando.

Eu acho que ajuda a ilustrar a diferença com um exemplo.

a = b + c

A linha de código acima pode ser escrita em praticamente qualquer idioma. Como podemos determinar se estamos usando uma linguagem imperativa ou declarativa? Como as propriedades dessa linha de código são diferentes nas duas classes de linguagem?

Em uma linguagem imperativa (C, Java, Javascript etc.), essa linha de código representa apenas uma etapa do processo. Não nos diz nada sobre a natureza fundamental de qualquer um dos valores. Ele nos diz que, no momento seguinte a esta linha de código (mas antes da próxima linha), aserá igual a bmais, cmas não nos diz nada ano sentido mais amplo.

Em uma linguagem declarativa (Haskell, Scheme, Excel, etc.), essa linha de código diz muito mais. Ele estabelece uma relação invariável entre ae os outros dois objetos, de modo que sempre será o caso aigual a bmais c. Note, que incluiu Excel na lista de linguagens declarativas porque, mesmo se bou caltera o valor, o fato de que ainda permanecem que aserá igual à sua soma.

A meu ver , isso não é efeitos colaterais ou estado, é o que diferencia os dois tipos de idiomas. Em uma linguagem imperativa, qualquer linha de código específica não diz nada sobre o significado geral das variáveis ​​em questão. Em outras palavras, a = b + csignifica apenas que, por um breve momento, aigualou a soma de be c.

Enquanto isso, em linguagens declarativas, toda linha de código estabelece uma verdade fundamental que existirá durante toda a vida útil do programa. Nessas linguagens, a = b + cinforma que, não importa o que aconteça em qualquer outra linha de código a, sempre será igual à soma de be c.

Daniel T.
fonte