É difícil definir exatamente o que é uma "linguagem funcional" - fora dos idiomas que você listou, apenas Haskell é puramente funcional (todos os outros adotam algum tipo de abordagem híbrida). Porém, existem certos recursos de linguagem que são muito úteis para a programação funcional, e Ruby e Python não têm o suficiente para serem ambientes muito bons para FP. Aqui está minha lista de verificação pessoal, em ordem de importância:
- Funções e fechamentos de primeira classe (Ruby, Python e todos os outros que você listou possuem isso).
- Otimização de chamada de cauda garantida (Erlang, Haskell, Scala e Scheme têm isso, mas não Python, Ruby ou Clojure (ainda)).
- Suporte à imutabilidade na linguagem e nas bibliotecas padrão (essa é uma das mais importantes que todas as "linguagens funcionais" listadas (exceto o Scheme), mas Ruby e Python não).
- Suporte em nível de idioma para funções referencialmente transparentes (ou puras) (até onde eu sei, apenas Haskell o possui atualmente).
A necessidade de (1) deve ser óbvia - funções de ordem superior são extremamente difíceis sem funções de primeira classe. Quando as pessoas falam que Ruby e Python são boas linguagens para FP, geralmente estão falando sobre isso. No entanto, esse recurso específico é necessário, mas não suficiente, para tornar uma linguagem boa para o FP.
(2) tem sido uma necessidade tradicional para o PF desde que o esquema foi inventado. Sem o TCO, é impossível programar com recursão profunda, que é uma das pedras angulares do FP, porque você sobrecarrega a pilha. A única linguagem "funcional" (por definição popular) que não possui isso é o Clojure (devido às limitações da JVM), mas o Clojure possui uma variedade de hacks para simular o TCO. (Para sua informação, o Ruby TCO é específico da implementação , mas o Python especificamente não o suporta .) O motivo pelo qual o TCO deve ser garantido é que, se for específico da implementação, as funções recursivas profundas serão interrompidas com algumas implementações, portanto você não pode realmente use-os de todo.
(3) é outra coisa importante que as linguagens funcionais modernas (especialmente Haskell, Erlang, Clojure e Scala) têm que Ruby e Python não têm. Sem entrar em muitos detalhes, a imutabilidade garantida elimina classes inteiras de bugs, especialmente em situações simultâneas, e permite coisas legais, como estruturas de dados persistentes . É muito difícil tirar proveito desses benefícios sem o suporte no nível do idioma.
(4) é, para mim, a coisa mais interessante sobre linguagens puramente funcionais (em oposição às linguagens híbridas). Considere a seguinte função Ruby extremamente simples:
def add(a, b)
a + b
end
Parece uma função pura, mas, devido à sobrecarga do operador, pode alterar o parâmetro ou causar efeitos colaterais, como a impressão no console. É improvável que alguém sobrecarregue o +
operador para ter um efeito colateral, mas o idioma não oferece garantias. (O mesmo se aplica ao Python, embora talvez não com este exemplo específico.)
Em uma linguagem puramente funcional, por outro lado, existem garantias no nível da linguagem de que as funções são referencialmente transparentes. Isso tem inúmeras vantagens: funções puras podem ser facilmente memorizadas; eles podem ser facilmente testados sem depender de qualquer tipo de estado global; e os valores dentro da função podem ser avaliados preguiçosamente ou em paralelo, sem se preocupar com problemas de simultaneidade. Haskell tira proveito disso, mas eu não sei o suficiente sobre outras linguagens funcionais para saber se elas o fazem.
Tudo isso dito, é possível usar técnicas de FP em quase qualquer linguagem (até Java). Por exemplo, o MapReduce do Google é inspirado em idéias funcionais, mas até onde eu sei, eles não usam nenhuma linguagem "funcional" para seus grandes projetos (acho que eles usam principalmente C ++, Java e Python).