Os fechamentos com efeitos colaterais são considerados "estilo funcional"?


9

Muitas linguagens de programação modernas suportam algum conceito de fechamento , isto é, um pedaço de código (um bloco ou uma função) que

  1. Pode ser tratado como um valor e, portanto, armazenado em uma variável, passado para diferentes partes do código, definido em uma parte de um programa e invocado em uma parte totalmente diferente do mesmo programa.
  2. Pode capturar variáveis ​​do contexto em que está definido e acessá-las quando for posteriormente invocada (possivelmente em um contexto totalmente diferente).

Aqui está um exemplo de encerramento escrito em Scala:

def filterList(xs: List[Int], lowerBound: Int): List[Int] =
  xs.filter(x => x >= lowerBound)

O literal da função x => x >= lowerBoundcontém a variável livre lowerBound, que é fechada (vinculada) pelo argumento da função filterListque tem o mesmo nome. O fechamento é passado para o método da biblioteca filter, que pode ser chamado repetidamente como uma função normal.

Eu tenho lido muitas perguntas e respostas neste site e, até onde eu entendi, o termo encerramento é frequentemente associado automaticamente à programação funcional e ao estilo de programação funcional.

A definição de programação de funções na wikipedia diz:

Na ciência da computação, a programação funcional é um paradigma de programação que trata a computação como a avaliação de funções matemáticas e evita dados de estado e mutáveis. Ele enfatiza a aplicação de funções, em contraste com o estilo de programação imperativa, que enfatiza as mudanças de estado.

e mais adiante

[...] no código funcional, o valor de saída de uma função depende apenas dos argumentos que são introduzidos na função [...]. A eliminação dos efeitos colaterais pode facilitar a compreensão e a previsão do comportamento de um programa, que é uma das principais motivações para o desenvolvimento da programação funcional.

Por outro lado, muitas construções de fechamento fornecidas pelas linguagens de programação permitem que um fechamento capture variáveis ​​não locais e as altere quando o fechamento é invocado, produzindo um efeito colateral no ambiente em que foram definidas.

Nesse caso, os fechamentos implementam a primeira ideia de programação funcional (funções são entidades de primeira classe que podem ser movidas como outros valores), mas negligenciam a segunda ideia (evitando efeitos colaterais).

Esse uso de fechamentos com efeitos colaterais é considerado estilo funcional ou os fechamentos são considerados uma construção mais geral que pode ser usada tanto para um estilo de programação funcional quanto para um não-funcional? Existe alguma literatura sobre esse tópico?

NOTA IMPORTANTE

Não estou questionando a utilidade dos efeitos colaterais ou de ter encerramentos com efeitos colaterais. Além disso, não estou interessado em discutir as vantagens / desvantagens de fechamentos com ou sem efeitos colaterais.

Estou interessado apenas em saber se o uso de tais fechamentos ainda é considerado estilo funcional pelo proponente da programação funcional ou se, pelo contrário, seu uso é desencorajado ao usar um estilo funcional.


3
Em um estilo / linguagem puramente funcional, os efeitos colaterais são impossíveis ... então eu acho que seria uma questão de: em que nível de pureza alguém considera o código funcional?
9118 Steven Evers

@SnOrfus: A 100%?
Giorgio

11
Os efeitos colaterais são impossíveis em uma linguagem puramente funcional, portanto, isso deve responder à sua pergunta.
Steven Evers

@SnOrfus: Em muitas linguagens, os fechamentos são considerados uma construção de programação funcional, então fiquei um pouco confuso. Parece-me que (1) seu uso é mais geral do que apenas fazer FP, e (2) o uso de fechamentos não garante automaticamente que alguém esteja usando um estilo funcional.
Giorgio

O downvoter pode deixar uma nota sobre como melhorar essa pergunta?
Giorgio

Respostas:


7

Não; a definição de paradigma funcional é sobre falta de estado e implicitamente falta de efeitos colaterais. Não se trata de funções de alta ordem, fechamentos, manipulação de lista suportada por idioma ou outros recursos de idioma ...

O nome da programação funcional vem da noção matemática de funções - chamadas repetidas na mesma entrada sempre fornecem a mesma saída - função nullipotent. Isso só pode ser alcançado se os dados forem imutáveis . Para facilitar o desenvolvimento, as funções se tornaram mutáveis ​​(funções mudam, os dados ainda são imutáveis) e, portanto, a noção de funções de ordem superior (funcionais em matemática, como derivadas, por exemplo) - uma função que assume como entrada outra função. Para a possibilidade de funções serem passadas como argumentos, foram adotadas funções de primeira classe ; após estes, para aumentar ainda mais a produtividade, surgiram fechamentos .

Esta é, obviamente, uma visão muito simplificada.


3
Funções de ordem superior não têm absolutamente nada a ver com mutabilidade, nem funções de primeira classe. Posso repassar funções e construir outras funções a partir delas em Haskell com grande facilidade, sem ter nenhum estado mutável nem efeitos colaterais.
Tdammers

@ Tdammers eu provavelmente não expressei muito claramente; quando eu disse que as funções se tornaram mutáveis, não me referi à mutabilidade dos dados, mas ao fato de que o comportamento de uma função pode ser alterado (por uma função de alta ordem).
M3th0dman

Uma função de ordem superior não precisa alterar uma função existente; a maioria dos exemplos de livros combina funções existentes em novas funções sem modificá-las - pegue map, por exemplo, o que aceita uma função, a aplica a uma lista e retorna uma lista dos resultados. mapnão modifica nenhum de seus argumentos, não altera o comportamento da função usada como argumento, mas é definitivamente uma função de ordem superior - se você a aplicar parcialmente, com apenas o parâmetro function, você construiu um nova função que opera em uma lista, mas ainda não ocorreu nenhuma mutação.
Tdammers

@ Tdammers: Exatamente: a mutabilidade é usada apenas em linguagens imperativas ou multiparadigmas. Embora possam ter o conceito de função (ou método) de ordem superior e de fechamento, eles renunciam à imutabilidade. Isso pode ser útil (não estou dizendo que não devamos fazê-lo), mas minha pergunta é se você ainda pode chamar isso de funcional. O que significaria que, em geral, um fechamento não é um conceito estritamente funcional.
Giorgio

@Giorgio: a maioria das línguas clássicas FP fazer tem mutabilidade; Haskell é o único que eu consigo pensar de cima para baixo que não permite mutabilidade. Ainda assim, evitar estado mutável é um valor importante no FP.
tdammers

9

Não. "Estilo funcional" implica programação sem efeitos colaterais.

Para ver o porquê, dê uma olhada na entrada do blog de Eric Lippert sobre o ForEach<T>método de extensão e por que a Microsoft não incluiu um método de sequência como esse no Linq :

Sou filosoficamente contrário a fornecer esse método, por duas razões.

A primeira razão é que isso viola os princípios de programação funcional nos quais todos os outros operadores de sequência se baseiam. Claramente, o único objetivo de uma chamada para esse método é causar efeitos colaterais. O objetivo de uma expressão é calcular um valor, não causar um efeito colateral. O objetivo de uma declaração é causar um efeito colateral. O site de chamada dessa coisa seria muito parecido com uma expressão (embora, reconhecidamente, uma vez que o método retorne vazio, a expressão só possa ser usada em um contexto de "expressão de declaração"). crie o único operador de sequência que é útil apenas para seus efeitos colaterais.

A segunda razão é que isso adiciona zero novo poder representacional à linguagem. Isso permite reescrever esse código perfeitamente claro:

foreach(Foo foo in foos){ statement involving foo; }

neste código:

foos.ForEach((Foo foo)=>{ statement involving foo; });

que usa quase exatamente os mesmos caracteres em ordem ligeiramente diferente. E, no entanto, a segunda versão é mais difícil de entender, mais difícil de depurar e introduz a semântica de fechamento, alterando potencialmente a vida útil do objeto de maneiras sutis.


11
Embora, eu concorde com ele ... seu argumento enfraquece um pouco ao considerar ParallelQuery<T>.ForAll(...). A implementação de um tal IEnumerable<T>.ForEach(...)é extremamente útil para depurar ForAlldeclarações (substitua o ForAllcom ForEache remover o AsParallel()e você pode muito mais facilmente percorrer / depurá-lo)
Steven Evers

Claramente Joe Duffy tem idéias diferentes. : D
Robert Harvey

O Scala também não impõe pureza: você pode passar um fechamento não puro para uma função de alta ordem. Minha impressão é que a ideia de fechamento não é específica da programação funcional, mas é uma ideia mais geral.
Giorgio

5
@Giorgio: Os fechamentos não precisam ser puros para serem considerados fechamentos. Eles precisam ser puros, no entanto, para serem considerados "estilo funcional".
9788 Robert

3
@Giorgio: É realmente útil poder fechar um estado mutável e efeitos colaterais e passá-lo para outra função. É apenas contrário aos objetivos da programação funcional. Eu acho que muita confusão vem do suporte elegante à linguagem para lambdas, sendo comum em linguagens funcionais.
precisa saber é o seguinte

0

A programação funcional leva as funções de primeira classe ao próximo nível conceitual, com certeza, mas declarar funções anônimas ou passar funções para outras funções não é necessariamente uma coisa de programação funcional. Em C, tudo era um número inteiro. Um número, um ponteiro para dados, um ponteiro para uma função ... tudo apenas ints. Você poderia passar ponteiros de função para outras funções, fazer listas de ponteiros de função ... Heck, se você trabalha em linguagem assembly, funções são realmente apenas endereços na memória onde blocos de instruções da máquina são armazenados. Dar um nome a uma função é uma sobrecarga extra para as pessoas que precisam de um compilador para escrever código. Portanto, as funções eram "de primeira classe", nesse sentido, em uma linguagem completamente não funcional.

Se a única coisa que você fizer é calcular fórmulas matemáticas em um REPL, poderá ser funcionalmente puro com o seu idioma. Mas a maioria dos programas de negócios tem efeitos colaterais. Perder dinheiro enquanto aguarda a conclusão de um programa de longa duração é um efeito colateral. Executar qualquer ação externa: gravar em um arquivo, atualizar um banco de dados, registrar eventos em ordem, etc. requer uma mudança de estado. Poderíamos debater se o estado é realmente alterado se você encapsular essas ações em invólucros imutáveis ​​que eliminam os efeitos colaterais, para que seu código não precise se preocupar com eles. Mas é como discutir se uma árvore faz barulho se cair na floresta sem ninguém lá para ouvi-la. O fato é que a árvore começou na vertical e acabou no chão. O estado é alterado quando as coisas são feitas, mesmo que apenas para relatar que as coisas foram feitas.

Portanto, ficamos com uma escala de pureza funcional, não em preto e branco, mas em tons de cinza. E nessa escala, quanto menos efeitos colaterais, menos mutabilidade, melhor (mais funcional).

Se você absolutamente exigir um efeito colateral ou um estado mutável no seu código funcional, você se esforçará para encapsular o código do resto do seu programa da melhor maneira possível. Usar um fechamento (ou qualquer outra coisa) para injetar efeitos colaterais ou estados mutáveis ​​em funções de outra forma puras é a antítese da programação funcional. A única exceção pode ser se o fechamento for a maneira mais eficaz de encapsular os efeitos colaterais do código para o qual ele é passado. Ainda não é "programação funcional", mas pode ser o armário que você pode obter em determinadas situações.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.