Por que o Python não é muito bom para programação funcional? [fechadas]

324

Eu sempre pensei que a programação funcional pode ser feita em Python. Portanto, fiquei surpreso que o Python não tenha sido mencionado nessa pergunta e, quando foi mencionado, normalmente não era muito positivo. No entanto, não foram dadas muitas razões para isso (a falta de correspondência de padrões e os tipos de dados algébricos foram mencionados). Então, minha pergunta é: por que o Python não é muito bom para programação funcional? Existem mais motivos do que a falta de correspondência de padrões e tipos de dados algébricos? Ou esses conceitos são tão importantes para a programação funcional que uma linguagem que não os suporta pode ser classificada apenas como uma linguagem de programação funcional de segunda classe? (Lembre-se de que minha experiência com programação funcional é bastante limitada.)

David Johnstone
fonte
2
2018 - O Coconut (uma linguagem de programação funcional compilada em Python) aprimora a programação funcional em Python. Veja também esta série de artigos da IBM Página1 page2 page3
cssyphus

Respostas:

393

A pergunta que você faz referência pergunta quais idiomas promovem o OO e a programação funcional. O Python não promove programação funcional, embora funcione razoavelmente bem.

O melhor argumento contra a programação funcional no Python é que os casos de uso imperativos / OO são cuidadosamente considerados por Guido, enquanto os casos de uso de programação funcional não são. Quando escrevo Python imperativo, é uma das linguagens mais bonitas que conheço. Quando escrevo Python funcional, ele se torna tão feio e desagradável quanto a linguagem comum que não possui um BDFL .

O que não quer dizer que seja ruim, apenas que você terá que trabalhar mais do que faria se mudasse para uma linguagem que promova a programação funcional ou passasse a escrever OO Python.

Aqui estão as coisas funcionais que sinto falta no Python:


  • Nenhuma correspondência de padrões e nenhuma recursão de cauda significam que seus algoritmos básicos precisam ser escritos de forma imperativa. A recursão é feia e lenta no Python.
  • Uma pequena biblioteca de listas e sem dicionários funcionais significam que você deve escrever muitas coisas.
  • Nenhuma sintaxe para currying ou composição significa que o estilo sem pontos é tão cheio de pontuação quanto passar argumentos explicitamente.
  • Iteradores, em vez de listas preguiçosas, significa que você precisa saber se deseja eficiência ou persistência e dispersar as chamadas, listse quiser persistência. (Os iteradores são usados ​​uma vez)
  • A sintaxe simples e imperativa do Python, junto com seu analisador LL1 simples, significa que uma melhor sintaxe para expressões if e lambda é basicamente impossível. Guido gosta assim, e acho que ele está certo.
Nathan Shively-Sanders
fonte
5
+1 por falta de recursão da cauda - embora as construções em loop a tenham substituído, ainda vale a pena perder algo entre Python e Scheme.
precisa saber é o seguinte
5
Excelente resposta quanto à completude e composição. Infelizmente, como em tantas respostas com forte histórico funcional, o uso abusivo da terminologia da OMI. Embora eu entenda que você não pode elaborar cada conceito em uma resposta, pergunto-me se o OP (com experiência limitada em FP permitida) é bem informado ao ler termos como "correspondência de padrões", "dicionários funcionais", "currying" ou " listas preguiçosas ".
6116 ThomasH
4
Bom ponto; Eu acho que a solução é adicionar links. Você tem representante suficiente para editar minha resposta? Nesse caso, sinta-se à vontade para adicionar links aos vários conceitos. Começarei quando tiver tempo mais tarde.
Nathan Shively-Sanders
5
Sei que isso tem 5 anos, mas ... parece que é mais sobre as coisas que você sente falta do Haskell , não das linguagens funcionais . Por exemplo, a maioria dos dialetos e descendentes de ML e Lisp não tem currying automático, torna o estilo sem ponto excessivamente detalhado, não possui listas de espera, etc. Portanto, se iteradores em vez de listas de espera tornar o Python uma linguagem funcional ruim, nem deve fazer do CaML uma linguagem funcional terrível ?
abarnert
4
@abarnert: Caml tem todos os pontos, exceto listas preguiçosas, que estão disponíveis como uma biblioteca. Ocasionalmente, usei o Caml no momento em que escrevi essa resposta e atualmente uso o F #. Ambos são linguagens funcionais muito agradáveis.
Nathan Shively-Sanders
102

Guido tem uma boa explicação sobre isso aqui . Aqui está a parte mais relevante:

Eu nunca considerei o Python fortemente influenciado por linguagens funcionais, não importa o que as pessoas digam ou pensem. Eu estava muito mais familiarizado com linguagens imperativas como C e Algol 68 e, embora tivesse criado funções de objetos de primeira classe, não via o Python como uma linguagem de programação funcional. No entanto, anteriormente, ficou claro que os usuários queriam fazer muito mais com listas e funções.

...

Também é importante notar que, embora eu não visse o Python como uma linguagem funcional, a introdução de encerramentos foi útil no desenvolvimento de muitos outros recursos avançados de programação. Por exemplo, certos aspectos de classes de novo estilo, decoradores e outros recursos modernos contam com esse recurso.

Por fim, apesar de vários recursos de programação funcional terem sido introduzidos ao longo dos anos, o Python ainda carece de certos recursos encontrados em linguagens de programação funcional "reais". Por exemplo, o Python não realiza certos tipos de otimizações (por exemplo, recursão de cauda). Em geral, devido à natureza extremamente dinâmica do Python, é impossível fazer o tipo de otimização em tempo de compilação conhecida em linguagens funcionais como Haskell ou ML. E tudo bem.

Eu retiro duas coisas disso:

  1. O criador da linguagem não considera realmente o Python uma linguagem funcional. Portanto, é possível ver recursos "de estilo funcional", mas é improvável que você veja algo que é definitivamente funcional.
  2. A natureza dinâmica do Python inibe algumas das otimizações que você vê em outras linguagens funcionais. É verdade que o Lisp é tão dinâmico (se não mais dinâmico) quanto o Python, portanto, essa é apenas uma explicação parcial.
Jason Baker
fonte
8
Você pode fazer otimização de chamada de cauda no Python muito bem. Guido não / não entendeu isso.
Jules
26
Parece que tudo se resume a que Guido van Rossum não gosta de estilo funcional.
Svante
59
Eu acho que é mais preciso dizer que Guido van Rossum não entende estilo funcional e não entende por que o Python precisa deles. Você precisa entender duas coisas: 1) as linguagens de programação estão no fundo de uma pilha de tecnologia e afetam tudo o que é construído sobre elas e 2), como qualquer outro software, é mais fácil adicionar recursos do que removê-los. Então, eu acho que é uma boa qualidade para um designer de linguagem criticar esses tipos de solicitações.
Jason Baker
8
"É verdade que o Lisp é tão dinâmico" -> quanto imperativo!
pyon 9/12/13
6
@Jules, você se importa de compartilhar uma diretriz para o uso da otimização de chamada de cauda no Python? Um ponteiro para alguma fonte seria útil.
precisa saber é o seguinte
52

O esquema não possui tipos de dados algébricos ou correspondência de padrões, mas certamente é uma linguagem funcional. Coisas irritantes sobre Python de uma perspectiva de programação funcional:

  1. Lambdas aleijados. Como o Lambdas pode conter apenas uma expressão e você não pode fazer tudo tão facilmente em um contexto de expressão, isso significa que as funções que você pode definir "on the fly" são limitadas.

  2. Ifs são declarações, não expressões. Isso significa, entre outras coisas, que você não pode ter um lambda com um If dentro dele. (Isso é corrigido por ternários no Python 2.5, mas parece feio.)

  3. Guido ameaça remover o mapa, filtrar e reduzir de vez em quando

Por outro lado, o python possui fechamentos lexicais, lambdas e compreensão de listas (que são realmente um conceito "funcional", independentemente de Guido o admitir). Eu faço bastante programação em "estilo funcional" em Python, mas dificilmente diria que é o ideal.

Jacob B
fonte
3
Mapear, filtrar e reduzir realmente não são necessários em python. Ainda estou para ver um pedaço de código que foi muito simplificado usando-os. Além disso, as funções de chamada no Python podem ser caras, por isso geralmente é melhor usar uma compreensão de lista / gerador ou um loop for de qualquer maneira.
Jason Baker
2
É exatamente o que Nathan Sanders diz abaixo: "O Python não promove programação funcional, mesmo que funcione razoavelmente bem". Se Guido quisesse que o Python se tornasse uma linguagem funcional, ele faria a implementação funcionar suficientemente bem para usar funções descartáveis ​​e descriptografaria o Lambdas na medida em que você possa realmente usar o mapa / filtro / redução de maneiras úteis. Por outro lado, pessoas funcionais estão começando a despertar para a grandiosidade das compreensões da lista. Espero que não tenhamos que escolher um ou outro.
Jacob B
7
mapa e filtro são substituídos trivialmente por uma compreensão da lista. reduza - quase sempre - é tão ineficiente que deve ser substituído por uma função de gerador.
214/09 S.Lott
13
@ S.Lott como você substitui o reduzir por um gerador?
Antimony
17
As compreensões do @JacobB List estavam disponíveis em linguagens funcionais cerca de 15 anos antes da criação do Python e 25 anos antes do Python ganhar uma implementação do recurso. A idéia de que o Python influenciou sua propagação, ou que a fp aprendeu isso com o Python, ou mesmo simplesmente que sua popularidade no mundo dos fp pós-data da implementação do Python, está simplesmente errada. A implementação do Python foi tirada diretamente do Haskell. Talvez eu tenha entendido mal você e não foi isso que você quis dizer, mas estou perplexo com "as pessoas funcionais estão começando a acordar com a grandiosidade das compreensões da lista".
itsbruce
23

Eu nunca chamaria o Python de "funcional", mas sempre que programa em Python, o código invariavelmente acaba sendo quase puramente funcional.

É certo que isso se deve principalmente à compreensão extremamente agradável da lista. Portanto, eu não sugeriria necessariamente o Python como uma linguagem de programação funcional, mas sugeriria a programação funcional para quem usa o Python.

Konrad Rudolph
fonte
17

Deixe-me demonstrar com um pedaço de código retirado de uma resposta a uma pergunta Python "funcional" no SO

Pitão:

def grandKids(generation, kidsFunc, val):
  layer = [val]
  for i in xrange(generation):
    layer = itertools.chain.from_iterable(itertools.imap(kidsFunc, layer))
  return layer

Haskell:

grandKids generation kidsFunc val =
  iterate (concatMap kidsFunc) [val] !! generation

A principal diferença aqui é que a biblioteca padrão de Haskell tem funções úteis para programação funcional: neste caso iterate, concate(!!)

yairchu
fonte
7
Aqui está uma substituição de uma linha para grandKids()o corpo com gerador de expressões: return reduce(lambda a, v: concat((x for x in kidsFunc(v)) for v in a), xrange(generation), [val]).
Lloeki 22/10
6
E aqui está um que não precisa concat:return reduce(lambda a, v: (x for v in a for x in kidsFunc(v)), xrange(generation), [val])
Lloeki 22/10
9
@Lloeki: iterate simplificaria significativamente esse código e (x para v em a para x em kidsFunc (v)) é muito mais claro que o concatMap (kidsFunc). A falta de bons componentes internos de ordem superior do Python torna o código equivalente enigmático e detalhado em comparação ao Haskell.
Phob 14/09/12
2
concat pode ser substituído poritertools.chain.from_iterable
Antimony
@ Anttimony: bom saber. thx
yairchu 02/09
14

Uma coisa que é realmente importante para esta pergunta (e as respostas) é a seguinte: O que diabos é a programação funcional e quais são as propriedades mais importantes dela. Vou tentar dar a minha opinião:

A programação funcional é como escrever matemática em um quadro branco. Ao escrever equações em um quadro branco, você não pensa em uma ordem de execução. Não há (normalmente) nenhuma mutação. Você não volta no dia seguinte e olha para ele e, quando faz os cálculos novamente, obtém um resultado diferente (ou pode, se tiver tomado um café fresco :)). Basicamente, o que está no quadro está lá, e a resposta já estava lá quando você começou a escrever as coisas, mas você ainda não percebeu o que é.

A programação funcional é muito parecida com isso; você não muda as coisas, apenas avalia a equação (ou, neste caso, "programa") e descobre qual é a resposta. O programa ainda está lá, sem modificações. O mesmo com os dados.

Eu classificaria o seguinte como os recursos mais importantes da programação funcional: a) transparência referencial - se você avaliar a mesma declaração em outro momento e local, mas com os mesmos valores de variável, ainda assim significará o mesmo. b) sem efeito colateral - não importa quanto tempo você olhe para o quadro branco, a equação que outro cara está olhando para outro quadro branco não mudará acidentalmente. c) funções também são valores. que podem ser passados ​​e aplicados com ou para outras variáveis. d) composição da função, você pode fazer h = g · f e, assim, definir uma nova função h (..) que é equivalente a chamar g (f (..)).

Como a lista está na minha ordem priorizada, a transparência referencial é a mais importante, seguida por nenhum efeito colateral.

Agora, se você passar pelo python e verificar o quão bem a linguagem e as bibliotecas suportam e garantem esses aspectos - você estará no caminho certo para responder sua própria pergunta.

xeno
fonte
2
As funções são de primeira classe em Python.
Carl Smith
@CarlSmith Esse, mas deixa 3/4 que o Python não possui. : - \
arya
1
Não acho que o Python seja uma boa linguagem para programação funcional. Agora nem tenho certeza do que eu quis dizer com esse comentário para ser honesto. Não parece relevante para a resposta. Eu o excluiria, mas seu comentário ficaria fora de contexto.
Carl Smith
1
Transparência e imutabilidade referenciais não são realmente recursos de linguagem. Sim, algumas linguagens (Haskell) as enfatizam e dificultam a ausência delas, mas você pode criar uma função referencialmente transparente ou um objeto imutável em praticamente qualquer linguagem. Você só precisa contornar a biblioteca padrão, que frequentemente as violará.
30716 Kevin
Além disso, o Python tem suporte para currying e composição, mas não no nível da linguagem, está na biblioteca padrão.
Kevin
10

Python é quase uma linguagem funcional. É "leve funcional".

Possui recursos extras, portanto, não é puro o suficiente para alguns.

Também não possui alguns recursos, portanto, não está completo o suficiente para alguns.

Os recursos ausentes são relativamente fáceis de escrever. Confira posts como este no FP em Python.

S.Lott
fonte
2
Na maior parte, concordo com este post. Mas não posso concordar em dizer que Python é uma linguagem funcional. Isso incentiva muito a programação imperativa, e é difícil não usar os "recursos extras" mencionados. Eu acho que é melhor se referir ao Python como "funcional lite", como você fez no outro post. :-)
Jason Baker
8
-1 Desculpe, não. Quero dizer, apenas não. Linguagens funcionais fornecem construções que facilitam o raciocínio formal: indutivo (ML), equacional (Haskell). Somente o fechamento e as funções anônimas são apenas um açúcar sintático para o padrão de estratégia.
pyon
8

Outro motivo não mencionado acima é que muitas funções e métodos internos de tipos internos modificam um objeto, mas não retornam o objeto modificado. Se esses objetos modificados fossem retornados, isso tornaria o código funcional mais limpo e conciso. Por exemplo, se some_list.append (some_object) retornou some_list com some_object anexado.

DVD Avins
fonte
4

Além de outras respostas, uma das razões pelas quais o Python e a maioria das outras linguagens multiparadigma não são adequadas para a verdadeira programação funcional é porque seus compiladores / máquinas virtuais / tempos de execução não oferecem suporte à otimização funcional. Esse tipo de otimização é alcançado pelo compilador que entende as regras matemáticas. Por exemplo, muitas linguagens de programação suportam ummap função ou método. Essa é uma função bastante padrão que assume uma função como um argumento e é iterável, pois o segundo argumento aplica essa função a cada elemento no iterável.

De qualquer forma, acontece que map( foo() , x ) * map( foo(), y )é o mesmo quemap( foo(), x * y ) . O último caso é realmente mais rápido que o anterior, porque o primeiro executa duas cópias, enquanto o último executa uma.

Linguagens funcionais melhores reconhecem esses relacionamentos com base matemática e executam automaticamente a otimização. Idiomas que não são dedicados ao paradigma funcional provavelmente não serão otimizados.


fonte
map( foo() , x ) * map( foo(), y ) == map( foo(), x * y )não é verdade para todas as funções. Por exemplo, considere o caso ao foocalcular uma derivada.
Eli Korvigo 29/07
1
Eu acho que ele quis dizer em +vez de *.
user1747134
Você está assumindo que foo () é linear?
Juan Isaza
essa propriedade NÃO é verdadeira para foo (x) = x + 1. Como (x + 1) * (y + 1)! = X * y + 1.
juan Isaza