Se pudermos fazer programação funcional com Python, precisamos de uma linguagem de programação funcional específica? [fechadas]

22

Usando geradores e lambda, podemos fazer programação funcional com Python. Você também pode conseguir a mesma coisa com Ruby.

Portanto, a pergunta é: por que precisamos de linguagens de programação funcional específicas, como Erlang, Haskell e Scheme? Existe algo diferente que essas linguagens de programação funcional específicas fornecem? Por que não podemos simplesmente usar o Python para programação funcional?

Joshua Partogi
fonte
30
todos eles existiam antes mesmo de o python ser criado #
Mahmoud Hossam
51
Um Ford Pinto é um carro. Por que precisamos de carros velozes específicos como Ferraris?
Martin York
11
Usando classes e modelos, podemos fazer qualquer coisa OO em C ++. Por que Java e Python foram criados? O que eles adicionam?
9000
19
Todas as linguagens de programação (sem algumas linguagens de pesquisa puramente acadêmicas) são equivalentes a Turing; portanto, o que você pode fazer na linguagem A pode fazer em qualquer outra linguagem. Portanto, seguindo essa linha de pensamento, precisamos apenas de um idioma completo de Turing - por exemplo, sendmail.cf;) okmij.org/ftp/Computation/#sendmail-Turing
Maglob
17
Se você conhecesse alguma dessas linguagens, não diria que o Python faz bem a programação funcional. Não faz. Faz bem o suficiente para incorporar uma parte das coisas do FP, mas não é melhor.

Respostas:

20

Agradeço a pergunta, porque pessoalmente sou um grande fã do Python e do estilo funcional de programação. Eu tenho uma longa experiência em Python e comecei a aprender Haskell recentemente, então aqui estão alguns pontos baseados em minhas experiências pessoais sobre as diferenças entre essas linguagens também, de uma perspectiva funcional.

Pureza

Mesmo que você não se importe com a pureza das funções (isto é, sem causar efeitos colaterais) como um princípio, ele tem um efeito prático sobre a facilidade de ler o código e raciocinar sobre ele. Mesmo que você mantenha a pureza em suas próprias funções Python, há uma grande diferença em fazer o compilador impor a pureza e, acima de tudo, em ter a biblioteca padrão construída em termos de pureza e estruturas de dados imutáveis.

atuação

Você pode ou não se importar com o desempenho, dependendo do domínio do seu aplicativo, mas a digitação estática e a pureza garantida oferecem muito mais ao compilador, em comparação com o Python e outras linguagens dinâmicas (embora eu deva admitir que o PyPy está se saindo bem) incursões e, por exemplo, LuaJIT está à beira de milagres).

Otimização de chamada de cauda

Relacionado ao desempenho, mas um pouco diferente. Mesmo que você não se importe muito com o desempenho do tempo de execução, não ter a otimização de chamada de cauda (especialmente para recursão de cauda) limita as maneiras de implementar algoritmos no Python sem atingir os limites de pilha.

Sintaxe

Essa é a maior razão pela qual comecei a olhar para linguagens funcionais "reais" em vez de apenas usar Python com estilo funcional. Embora eu pense que o Python tenha uma sintaxe muito expressiva em geral, ele tem alguns pontos fracos específicos da codificação funcional. Por exemplo:

  • A sintaxe das funções lambda é bastante detalhada e limitada no que elas podem conter
  • Sem açúcar sintático para a composição da função, ou seja, f = g . hvs.f = lambda *arg: g(h(*arg))
  • Sem açúcar sintático para aplicação parcial, isto é, f = map gvs.f = functools.partial(map, g)
  • Sem açúcar sintático para usar operadores infix em funções de ordem superior, isto é, sum = reduce (+) lstvs.sum = reduce(operator.add, lst)
  • Nenhuma correspondência de padrão ou proteção para argumentos de função, o que facilita a expressão de condições finais de recursão e alguns casos de borda com sintaxe muito legível.
  • Os colchetes nunca são opcionais para chamadas de função e não há açúcar sintático para chamadas aninhadas. Acho que isso é uma questão de gosto, mas especialmente no código funcional, acho que é comum encadear chamadas de função e acho y = func1 $ func2 $ func3 xmais fácil de ler do que y = func1(func2(func3(x)))quando você está familiarizado com essa notação.
shang
fonte
28

Estas são as diferenças mais importantes:

Haskell

  • Avaliação preguiçosa
  • Compila para código de máquina
  • A digitação estática garante que as funções sejam puras
  • Inferência de tipo

Haskell e Erlang

  • Correspondência de padrões

Erlang

  • Modelo de ator de concorrência, processos leves

Esquema

  • Macros

Todas as línguas

  • fechamentos reais (ruby tem fechamentos, se python pode ser debatido, veja os comentários)
  • uma biblioteca padrão adequada para um estilo de programação funcional (coleções imutáveis, mapa, filtro, dobra etc.)
  • recursão de cauda (isso também pode ser encontrado em algumas linguagens não funcionais)

Além disso, você deve dar uma olhada nas linguagens da família ML, como SML, Ocaml e F # e Scala, que fundem OO e programação funcional de uma nova maneira. Todos esses idiomas têm características interessantes únicas.

Kim
fonte
3
+1 boa postagem. Você pode adicionar inferência de tipo nos processos Haskell e Leve no Erlang.
Jonas
1
Python tem mapear, filtrar e dobrar (reduzir). Em relação aos "fechamentos reais": se você definir um fechamento real como um fechamento que pode conter algo diferente de uma única expressão, Haskell também não tem fechamentos reais (mas é claro que Haskell tem poucas coisas que não são expressões ...) . No entanto, o ponto sobre estruturas de dados imutáveis ​​é bom, então marque com +1 isso. Também: recursão da cauda (e recursão geralmente mais barata).
sepp2k
2
+1 para "a digitação estática garante que as funções sejam puras". É muito legal ter um sistema de tipos que discrimine entre funções puras e não puras. (C ++ 's const corça ranho contagem :).
Macke
1
@ Btilly: Eu não consideraria um fechamento se você não puder atribuir a uma variável que está no escopo. Caso contrário, teríamos que dizer que o Java também possui fechamentos, já que você pode usar o mesmo truque lá.
11117 Kim
3
Em um encerramento, posso acessar uma variável da mesma maneira que normalmente. Isso vale para os fechamentos de Haskell e Ruby, mas não para os pobres substitutos de Python ou Java. Talvez alguém possa nos esclarecer sobre erlang, eu não sei muito bem disso.
11117 Kim
19

É 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:

  1. Funções e fechamentos de primeira classe (Ruby, Python e todos os outros que você listou possuem isso).
  2. Otimização de chamada de cauda garantida (Erlang, Haskell, Scala e Scheme têm isso, mas não Python, Ruby ou Clojure (ainda)).
  3. 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).
  4. 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).

shosti
fonte
2
+1 para a explicação completa - até eu, como um outsider da FP, entendi. Obrigado! :-)
Péter Török
resposta mais valiosa para esta pergunta até agora. Fundada em fatos e muita informação. Bom trabalho.
Wirrbel # 23/13
O Scala tem recursão de cauda e, como no Scheme, é feito automaticamente se uma chamada recursiva estiver na posição de cauda (diferente do Clojure, onde deve ser explicitamente solicitado). Há até uma anotação para que possa verificar que o compilador irá gerar o código recursiva cauda. O que ele não possui é o TCO mais generalizado do Scheme. Suponho que você saiba disso, mas desde que você entrou em tantos detalhes sobre a maioria das outras coisas, isso pareceu uma dispensa / omissão estranha.
itsbruce
@itsbruce Este post é bem antigo, o IIRC Scala não o tinha no momento (ou talvez eu esteja errado;). Atualizada.
shosti 21/11
Eu não uso o Scala desde o início, mas ele teve recursão em 2008, quando estava me interessando ;-) Refiro as pessoas que perguntam sobre esse tópico a essa pergunta específica do SO, porque ela tem boas respostas e eu apenas notou essa estranheza, então comentou a integridade.
precisa saber é o seguinte
11

Os idiomas que você menciona são muito diferentes.

Enquanto Python e Ruby são linguagens de tipo dinâmico, Haskell é estaticamente. Erlang é um idioma simultâneo, usa o modelo de ator e é muito diferente de todos os outros idiomas mencionados.

Python e Ruby têm muitas construções imperativas, enquanto em uma linguagem funcional mais pura como Haskell, tudo retorna algo ou, em outras palavras, tudo é uma função.

Jonas
fonte
@KRON: Bem, o sistema de tipos é uma propriedade importante de uma linguagem e ele perguntou: "Existe algo diferente que essas linguagens de programação funcional específicas fornecem?". Claro, você pode usar o modelo Actor com outros idiomas, mas Erlang o incorporou no idioma. Erlang usa processos leves e constrói construções de linguagem para programação distribuída - daí uma linguagem "simultânea".
Jonas
8

Tarde para a festa, como sempre, mas vou dizer as coisas de qualquer maneira.

Uma linguagem de programação funcional não é uma linguagem que permita a programação funcional. Se seguíssemos essa definição, praticamente qualquer linguagem em qualquer lugar é uma linguagem de programação funcional. (O mesmo se aplica a OOP, aliás. Você pode escrever em um estilo de OOP em C, se quiser. Portanto, de acordo com sua lógica, C é uma linguagem de OOP.)

O que torna uma linguagem de programação funcional não é o que permite programar, é o que permite programar com facilidade . Essa é a chave.

Portanto, o Python possui lambdas (que são casos incrivelmente anêmicos de fechamento) e oferece algumas funções de biblioteca que você verá nas bibliotecas funcionais, como "map" e "fold". Isso não é suficiente para torná-lo uma linguagem de programação funcional, no entanto, porque é difícil impossível programar consistentemente um estilo funcional adequado (e a linguagem certamente não impõe esse estilo!). Em sua essência, o Python é uma linguagem imperativa preocupada com operações de manipulação de estado e estado e isso está simplesmente em desacordo com a semântica de expressão e avaliação de expressão de uma linguagem funcional.

Então, por que temos linguagens de programação funcional quando Python (ou Ruby (ou insira a linguagem de sua escolha)) pode "fazer programação funcional"? Porque Python, et al não podem fazer a programação funcional adequada. É por isso.

APENAS MINHA OPINIÃO correta
fonte
6

Você pode fazer programação funcional em Java (consulte, por exemplo, http://functionaljava.org/ ). Você também pode fazer programação orientada a objetos em C . Simplesmente não é tão idiomático.

Portanto, não precisamos absolutamente de Erlang, Haskell, Scheme ou qualquer linguagem de programação específica, mas todos eles representam abordagens diferentes e trocas diferentes, tornando algumas tarefas mais fáceis e mais difíceis. O que você deve usar depende do que você deseja alcançar.

Joonas Pulakka
fonte
4

Esta questão pode ser aplicada a um número infinito de linguagens e paradigmas.

  • Como todos usam C ++, por que precisamos de outras linguagens de uso geral?
  • Como o java é uma ótima linguagem OO, por que existem outras línguas OO?
  • Como o perl é uma linguagem de script incrível, por que precisamos de python?
  • Yatta, yatta, yatta

A maioria, se não todos os idiomas, existe por um motivo específico. Eles existem porque alguém tinha uma necessidade que nenhum idioma atual preencheu ou preencheu mal. (Isso obviamente não se aplica a todas as linguagens, mas eu sinto que se aplica à maioria das linguagens conhecidas.) Por exemplo, o python foi originalmente desenvolvido para interagir com o Amoeba OS [ 1 , 2 ] e Erlang foi criado para ajudar no desenvolvimento de aplicações de telefonia [ 3 ]. Então, uma resposta para a pergunta "Por que precisamos de outra linguagem funcional?" pode simplesmente ser, porque [insira o nome de alguém que sabe como projetar linguagens] não gostou da maneira como o python o fez.

Isso resume muito bem o que eu acho que é a resposta. Enquanto você pode fazer qualquer coisa com python que você pode fazer com uma linguagem funcional, você realmente gostaria? Tudo o que você pode fazer em C, você pode fazer em montagem, mas você gostaria? Diferentes idiomas sempre serão melhores em fazer coisas diferentes, e é assim que deve ser.

cledoux
fonte
2

A programação funcional é tanto um paradigma de design quanto recursos de linguagem específicos. Ou, dito de outra forma, lambdas e uma função de mapa não produzem uma linguagem de programação funcional. Python e Ruby têm alguns recursos inspirados em linguagens de programação funcional, mas você ainda escreve código de uma maneira muito imperativa. (É como C: você pode escrever código semelhante ao OO em C, mas ninguém considera seriamente o C como uma linguagem OO.)

Veja: programação funcional não é apenas sobre lambdas, ou mapfunções de ordem superior. É sobre design . Um programa escrito em uma linguagem de programação funcional "verdadeira" resolve problemas através da composição de funções. Embora os programas escritos em Ruby ou Python possam usar recursos do tipo FP, eles geralmente não são lidos como um conjunto de funções compostas.

mipadi
fonte
0

Cada linguagem funcional que você mencionou se encaixa muito bem em um determinado nicho e os padrões idiomáticos que cada um incentiva os tornam muito adequados para determinadas tarefas que seriam impossíveis de realizar no Python, a menos que você construísse uma enorme biblioteca de módulos auxiliares. O mais óbvio de um desses nichos de excelência é o modelo de concorrência de Erlang. Os outros também têm pontos fortes semelhantes.

davidk01
fonte
0

Todos os conceitos disponíveis no lambda-calculus, LISP e Scheme estão disponíveis no Python, portanto, sim, você pode fazer uma programação funcional nele. Se é conveniente ou não, é uma questão de contexto e bom gosto.

Você pode escrever facilmente um intérprete LISP (ou outra linguagem funcional) em Python (Ruby, Scala) (o que isso significa?). Você pode escrever um intérprete para Python usando puramente funcional, mas isso levaria muito trabalho. Até as linguagens "funcionais" são multiparadigmas hoje em dia.

Este livro antigo e bonito tem a maioria das respostas (embora não todas) sobre qual é a essência da programação funcional.

Apalala
fonte
@Arkaaito Mas você concorda que todos os conceitos disponíveis no cálculo lambda, LISP e Scheme estão disponíveis no Python?
Mark C
Você tem certeza de que todo conceito lisp está disponível no Python? Macros, código-é-dados?
SK-logic
@ As macros SK-logic não estão no cálculo lambda, mas sim, estão disponíveis no Python, embora não com esse nome. O Python fornece uma eval()função, necessária para o código e os dados , mas vai além: permite modificar a maior parte do ambiente de tempo de execução, como no LISP.
Apalala
@Apalala, linguagens dinâmicas eval()é uma metaprogramação em tempo de execução. Às vezes é útil, mas é extremamente caro. As macros Lisp são diferentes, é uma metaprogramação em tempo de compilação. Não está disponível no Python de nenhuma forma utilizável.
SK-logic,
@ SK-logic Esse tipo de macros quebra a premissa de que o código é dado , pois os programas não podem mudar de macros (ou mesmo saber de sua existência). No Python, os programas têm acesso a suas próprias árvores de análise em tempo de execução, se necessário. As macros (pré-processamento estático) não são funcionais.
Apalala
-5

Porque o Python também pode programar em um estilo não funcional, e isso não é bom o suficiente para o purista de FP. Porém, codificadores mais pragmáticos podem aproveitar os benefícios do estilo funcional sem serem dogmáticos:

“O programador funcional parece um monge medieval, negando a si mesmo os prazeres da vida na esperança de que isso o torne virtuoso. Para os mais interessados ​​em benefícios materiais, essas “vantagens” não são muito convincentes. Programadores funcionais argumentam que há grandes benefícios materiais ... [mas] isso é claramente ridículo. Se a omissão das declarações de atribuição trouxesse benefícios tão enormes, os programadores da FORTRAN já o faziam há vinte anos. É uma impossibilidade lógica de tornar uma linguagem mais poderosa, omitindo recursos, por mais ruins que sejam. ”

- John Hughes, por que a programação funcional é importante

Mason Wheeler
fonte
6
Concordo. Qualquer idioma sem gotoé terrível.
Anon.
2
@ Anon: Ou seu grande irmão call-with-current-continuation,.
Chris Jester-Young
4
Mason, o artigo que você está citando está dizendo exatamente o oposto. Sua citação é apenas um fragmento de alguns parágrafos de um artigo que conta uma história diferente. De fato, se você citasse os dois parágrafos como um todo, eles mostrariam claramente outro significado.
Marco Mustapic
2
@Mason: sim, o autor afirma que modularização e avaliação lenta são os verdadeiros benefícios. Mas observe como sua citação original estava fora de contexto - você parece sugerir que o autor está reivindicando algo contra a FP, quando na verdade você citou um parágrafo de um artigo com a conclusão de que a FP é ótima, mas não pelo motivo enganoso de " menos é mais". O título do artigo mostra claramente a intenção do autor: "por que a programação funcional é importante" ;-) Certamente não suporta a sua afirmação de que os idiomas mais puros de FP "são um incômodo".
7117 Andres F.
6
@Mason Wheeler: linguagens híbridas precedem o Python por um longo período. O Lisp, por exemplo, data de 1959 e é, de fato, uma linguagem multiparadigma. Ele suporta totalmente abordagens funcionais, abordagens processuais e abordagens orientadas a objetos para programação. Com os pacotes de macro corretos no topo, você também pode fazer a programação lógica com muita facilidade. O esquema também antecede o Python (e este artigo). Isso remonta a 1975. Talvez você deva dar uma olhada nesta linha do tempo das linguagens de programação em algum momento.
APENAS MINHA OPINIÃO correta