Um vídeo do YouTube que eu estava assistindo explicou as diferenças entre programação imperativa e funcional, demonstrando como os números de 1
para 10
são resumidos em Java e em Haskell, respectivamente.
Em Java, você deve declarar explicitamente cada etapa e atribuir o resultado de cada etapa a uma variável - algo como o seguinte
int total = 0;
for (int i = 1; i <= 10; i++){
total = total + i;
}
return total;
Em Haskell, você pode simplesmente dizer:
sum(1..10)
Minha pergunta é: obviamente há algo acontecendo no fundo de uma linguagem funcional, e que algo deve ser algum tipo de processo imperativo. Parece que as linguagens funcionais são realmente apenas um tipo de API de idioma imperativo. Por exemplo, eu posso criar parte de uma linguagem funcional definindo um método sum(int start, int end)
em Java. Eu realmente criei um novo tipo de linguagem ali, ou acabei de definir um conjunto de chamadas de método Imperative que ocultam instruções imperativas de você?
Espero que esteja claro o que estou lutando para entender.
fonte
Respostas:
Se descascarmos o açúcar sintático na frente e a geração de código na parte de trás e compararmos o que acontece entre a conversão de código fonte e código em execução para linguagens imperativas , como C ou Java, com linguagens funcionais como ML ou OCaml, geralmente encontraremos as seguintes diferenças em quê, por que e como.
Mutável vs. imutável
Com a programação funcional, tende-se a usar valores imutáveis, o que significa que não precisamos nos preocupar se um valor mudar por um meio externo à nossa função atual. Quando usado corretamente, remove todos os problemas relacionados aos efeitos colaterais .
Foco: Dados versus Função.
Quando se pensa em codificar como imperativo, primeiro se pensa em estruturas de dados e depois em quais métodos eles precisam. Ao trabalhar com programação funcional, primeiro se pensa em quais funções são necessárias e depois cria os tipos de dados necessários. A maioria dos tipos de dados é de lista lenta (think stream ou lista infinita) ou uniões discriminadas . Quando a união discriminada é recursiva, você cria instantaneamente uma árvore ou gráfico sem precisar escrever todo o código ambulante.
Genéricos / Polimorfismo paramétrico
Este é interessante e, se os meus fatos estiverem corretos, foi inventado com programação funcional e depois transplantado para programação imperativa. Então, se você gosta de genéricos, agradeça aos designers de linguagem funcional.
Transparência referencial / paralelização
Devido à transparência referencial, o código funcional pode ser transportado com mais facilidade para a computação paralela .
Função de ordem superior / composicionalidade
Como as funções podem criar novas funções e retornar funções, a criação de novas funções é baseada em outras funções e é tão fácil quanto criar novas expressões em vez de escrever novos métodos inteiros. Isso leva a morfismos que são muito úteis se o problema que você está resolvendo pode ser superado com a matemática. Fazer transformações de conjunto, como SQL e atualizações, é muito mais fácil com a programação funcional. Como observou a Wandering Logic , é aqui que as linguagens de programação funcional se destacam.
Digitação: Estática versus inferência .
Como os tipos são inferidos em vez de serem definidos pelo programador durante a gravação, mais verificações podem ser feitas para garantir a correção do código e, muitas vezes, as funções serão tornadas genéricas em vez de serem de um tipo definido.
Correspondência de padrão vs instrução switch
Ao combinar correspondência com uniões discriminadas, sua correspondência é verificada para garantir que você tenha coberto todos os resultados. Quantas vezes você teve um erro em tempo de execução porque perdeu um caso com uma instrução switch.
fonte
Uma linguagem de programação funcional é notável pelo que proíbe . Proíbe modificar uma variável ou estrutura de dados existente. Você pode programar em um "estilo funcional" em algumas linguagens de programação imperativas, mas a linguagem não o protegerá contra a modificação acidental de uma variável ou estrutura de dados existente. Por exemplo, aqui está uma versão recursiva e funcional do
sum
Java:Onde as linguagens de programação funcional se destacam é que todas elas têm funções "de primeira classe". Ou seja: uma função pode ser usada como um valor, assim como um número inteiro ou uma estrutura de dados. Algumas linguagens imperativas também têm funções de primeira classe (principalmente C e C ++), mas em Java você precisa falsificá-lo com um objeto com um único método (geralmente chamado
apply()
ou algo assim). As funções de primeira classe permitem generalizar asum
função para um função que reduz qualquer operador:Se eu estivesse escrevendo em uma linguagem de programação funcional, saberia que toda função é realmente uma função (ela retorna o mesmo valor toda vez que é chamada com os mesmos parâmetros), então eu saberia isso em:
total1
etotal2
têm o mesmo valor (por exemplo, eu poderia otimizar a segunda chamada parasum
), enquanto que em uma linguagem de programação imperativa não tenho idéia seReallyStrangeOperator(1,2)
retorna o mesmo valor sempre ou não.fonte