Nos últimos anos, funções anônimas (funções AKA lambda) tornaram-se uma construção de linguagem muito popular e quase todas as principais linguagens de programação as introduziram ou estão planejadas para introduzi-las em uma revisão futura do padrão.
No entanto, funções anônimas são um conceito muito antigo e muito conhecido em Matemática e Ciência da Computação (inventado pelo matemático Alonzo Church por volta de 1936 e usado pela linguagem de programação Lisp desde 1958, veja, por exemplo, aqui ).
Então, por que as principais linguagens de programação de hoje (muitas das quais foram originadas de 15 a 20 anos atrás) não suportam funções lambda desde o início e só as introduzem mais tarde?
E o que desencadeou a adoção maciça de funções anônimas nos últimos anos? Existe algum evento específico, novo requisito ou técnica de programação que iniciou esse fenômeno?
NOTA IMPORTANTE
O foco desta questão é a introdução de funções anônimas em linguagens modernas de fluxo principal (e, portanto, talvez com algumas exceções, não funcionais). Além disso, observe que funções anônimas (blocos) estão presentes no Smalltalk, que não é uma linguagem funcional, e que funções nomeadas normais estão presentes mesmo em linguagens procedurais como C e Pascal há muito tempo.
Por favor, não generalize demais suas respostas falando sobre "a adoção do paradigma funcional e seus benefícios", porque esse não é o tema da pergunta.
fonte
Respostas:
Certamente há uma tendência notável em direção à programação funcional, ou pelo menos em certos aspectos dela. Algumas das linguagens populares que em algum momento adotaram funções anônimas são C ++ ( C ++ 11 ), PHP ( PHP 5.3.0 ), C # ( C # v2.0 ), Delphi (desde 2009), Objetivo C ( blocos ) enquanto Java 8 trará suporte para lambdas para o idioma . E há linguagens populares que geralmente não são consideradas funcionais, mas suportam funções anônimas desde o início, ou pelo menos no início, o exemplo brilhante é o JavaScript.
Como em todas as tendências, tentar procurar um único evento que as desencadeou provavelmente é uma perda de tempo, geralmente é uma combinação de fatores, a maioria dos quais não é quantificável. O prático Common Lisp , publicado em 2005, pode ter desempenhado um papel importante ao trazer nova atenção ao Lisp como uma linguagem prática , pois há algum tempo o Lisp era principalmente uma linguagem que você encontraria em um ambiente acadêmico ou em nichos de mercado muito específicos. A popularidade do JavaScript também pode ter desempenhado um papel importante ao atrair nova atenção para funções anônimas, como explica em sua resposta .
Além da adoção de conceitos funcionais de linguagens polivalentes, há também uma mudança notável em direção a linguagens funcionais (ou principalmente funcionais). Idiomas como Erlang (1986), Haskell (1990), OCaml (1996), Scala (2003), F # (2005), Clojure (2007) e até mesmo idiomas específicos de domínio como R (1993) parecem ter ganhado fortemente seguidores fortemente depois que eles foram introduzidos. A tendência geral chamou nova atenção para linguagens funcionais mais antigas, como Scheme (1975) e, obviamente, Common Lisp.
Eu acho que o evento mais importante é a adoção de programação funcional no setor. Não tenho absolutamente nenhuma idéia de por que isso não costumava ser o caso, mas parece-me que, em algum momento do início e meados dos anos 90, a programação funcional começou a encontrar seu lugar na indústria, começando (talvez) com a proliferação de Erlang no mundo. telecomunicações e adoção da Haskell no design aeroespacial e de hardware .
Joel Spolsky escreveu um post de blog muito interessante, The Perils of JavaSchools , onde argumenta contra a (então) tendência das universidades de favorecer o Java em detrimento de outras línguas, talvez mais difíceis de aprender. Embora a postagem do blog tenha pouco a ver com programação funcional, ela identifica um problema importante:
Ainda me lembro do quanto odiava Lisp, quando a conheci durante meus anos de faculdade. Definitivamente, é uma amante dura, e não é um idioma em que você pode ser imediatamente produtivo (bem, pelo menos eu não). Comparado com Lisp, Haskell (por exemplo) é muito mais amigável, você pode ser produtivo sem muito esforço e sem se sentir um completo idiota, e isso também pode ser um fator importante na mudança para a programação funcional.
Em suma, isso é uma coisa boa. Várias linguagens de múltiplos propósitos estão adotando conceitos de paradigma que podem ter parecido misteriosos para a maioria de seus usuários antes, e a diferença entre os principais paradigmas está diminuindo.
Perguntas relacionadas:
Leitura adicional:
fonte
Eu acho interessante o quanto a popularidade da programação funcional tem paralelo ao crescimento e proliferação do Javascript. Javascript possui muitos recursos radicais ao longo do espectro de programação funcional que, no momento de sua criação (1995), não eram muito populares entre as linguagens de programação convencionais (C ++ / Java). Foi injetado repentinamente no mainstream como a única linguagem de programação da Web do lado do cliente. De repente, muitos programadores simplesmente precisavam saber Javascript e, portanto, você tinha que saber algo sobre os recursos funcionais da linguagem de programação.
Eu me pergunto como seriam as linguagens / recursos funcionais populares se não fosse pelo aumento repentino do Javascript.
fonte
Os manipuladores de eventos JavaScript e DOM significavam que milhões de programadores precisavam aprender pelo menos um pouco sobre as funções de primeira classe para fazer qualquer interatividade na web.
A partir daí, é um passo relativamente curto para funções anônimas . Como o JavaScript não fecha
this
, também o incentiva a aprender sobre fechamentos também. E então você é de ouro: entende as funções anônimas de primeira classe que se fecham sobre os escopos lexicais.Quando estiver confortável com isso, você o desejará em todos os idiomas que usar.
fonte
Certamente não é o único fator, mas vou apontar a popularidade do Ruby. Não estou dizendo que isso é mais importante do que qualquer uma das seis respostas que já estão no quadro, mas acho que muitas coisas aconteceram ao mesmo tempo e que é útil enumerá-las todas.
Ruby não é uma linguagem funcional e seus lambdas, prods e blocos parecem desajeitados quando você usa algo como ML, mas o fato é que popularizou a noção de mapeamento e redução a uma geração de jovens programadores que fogem de Java e PHP para hipper pastagens. Lambdas em vários idiomas parecem ser movimentos defensivos mais do que qualquer outra coisa ("Fique por aqui! Temos isso também !!)
Mas a sintaxe do bloco e a maneira como ela se integrou a .each, .map, .reduce e assim por diante popluarizaram a idéia de uma função anônima, mesmo que seja realmente uma construção sintática que se comporte como uma corotina. E a fácil conversão para um processo via & o torna uma droga de gateway para programação funcional.
Argumento que os programadores do Ruby on Rails que escreviam JavaScript já estavam ativados para fazer as coisas com um estilo funcional leve. Junte isso aos blogs de programadores, a invenção do Reddit, do hacker News e do Stack Overflow na mesma época, e as idéias se espalharam mais rapidamente pela Internet do que nos dias dos Newsgroups.
TL; DR: Ruby, Rails, JavaScript, blogs e Reddit / Hacker News / Stack Overflow levaram idéias funcionais para um mercado de massa, então todos os queriam nos idiomas existentes para evitar mais deserções.
fonte
Como Yannis apontou, há vários fatores que influenciaram a adoção de funções de alta ordem em idiomas que anteriormente não existiam. Um dos itens importantes nos quais ele apenas tocou levemente é a proliferação de processadores com vários núcleos e, com isso, o desejo de um processamento mais paralelo e simultâneo.
O estilo de mapear / filtrar / reduzir a programação funcional é muito amigável à paralelização, permitindo que o programador faça uso fácil de múltiplos núcleos, sem escrever nenhum código explícito de encadeamento.
Como observa Giorgio, há mais na programação funcional do que apenas funções de alta ordem. Funções, além de um mapa / filtro / redução de padrão de programação e imutabilidade são o núcleo da programação funcional. Juntas, essas coisas criam ferramentas poderosas de programação paralela e simultânea. Felizmente, muitas linguagens já suportam alguma noção de imutabilidade e, mesmo que não o façam, os programadores podem tratar as coisas como imutáveis, permitindo que as bibliotecas e o compilador criem e gerenciem operações assíncronas ou paralelas.
Adicionar funções de alta ordem a um idioma é uma etapa importante para simplificar a programação simultânea.
Atualizar
Vou adicionar alguns exemplos mais detalhados para abordar as preocupações que Loki observou.
Considere o seguinte código C # que percorre uma coleção de widgets, criando uma nova lista de preços de widgets.
Para uma grande coleção de widgets ou um método CalculateWidgetPrice (Widget) intensivamente computacional, esse loop não faria bom uso de nenhum núcleo disponível. Para fazer os cálculos de preços em diferentes núcleos, é necessário que o programador crie e gerencie explicitamente threads, passando soluções e coletando os resultados juntos.
Considere uma solução depois que funções de ordem superior forem adicionadas ao C #:
O loop foreach foi movido para o método Select, ocultando seus detalhes de implementação. Tudo o que resta ao programador é dizer ao Select que função aplicar a cada elemento. Isso permitiria que a implementação Select execute os cálculos em paralelo, lidando com todas as preocupações de sincronização e gerenciamento de encadeamentos sem o envolvimento do programador.
Mas, é claro, o Select não faz seu trabalho em paralelo. É aí que entra a imutabilidade. A implementação do Select não sabe que a função fornecida (CalculateWidgets acima) não tem efeitos colaterais. A função pode alterar o estado do programa fora da exibição do Select e sua sincronização, interrompendo tudo. Por exemplo, nesse caso, o valor de salesTax pode ser alterado por engano. Linguagens funcionais puras fornecem imutabilidade, portanto a função Select (map) pode ter certeza de que nenhum estado está mudando.
O C # resolve isso fornecendo o PLINQ como uma alternativa ao Linq. Seria assim:
O que faz pleno uso de todos os núcleos do seu sistema sem gerenciamento explícito desses núcleos.
fonte
cause
e umperceived affect
sem explicar ocorrelation
. A última linha IMO é sobre o que se trata; mas você não respondeu. Por que simplifica a programação simultânea?Eu concordo com muitas das respostas aqui, mas o interessante é que, quando eu aprendi sobre lambdas e pulei nelas, não foi por nenhuma das razões pelas quais outras pessoas mencionaram.
Em muitos casos, as funções lambda simplesmente melhoram a legibilidade do seu código. Antes das lambdas, quando você chama um método que aceita um ponteiro de função (ou função, ou delegar), você tinha que definir o corpo dessa função em outro lugar; portanto, quando você tinha uma construção "foreach", seu leitor teria que pular para uma outra parte do código para ver exatamente o que exatamente você estava planejando fazer com cada elemento.
Se o corpo da função que processa elementos tiver apenas algumas linhas, eu usaria uma função anônima porque agora, quando você está lendo código, a funcionalidade permanece inalterada, mas o leitor não precisa ir e voltar, a implementação inteira é ali mesmo na frente dele.
Muitas das técnicas de programação funcional e paralelização poderiam ser alcançadas sem funções anônimas; apenas declare um regular e passe uma referência a isso sempre que precisar. Mas, com as lambdas, a facilidade de escrever código e a facilidade de leitura do código são bastante aprimoradas.
fonte
Tendo me envolvido um pouco na história recente, acredito que um fator foi a adição de genéricos ao Java e .NET. Isso naturalmente leva a Func < , > e outras abstrações computacionais fortemente tipadas (Tarefa < >, Async < > etc.)
No mundo .NET, adicionamos esses recursos precisamente para dar suporte ao FP. Isso desencadeou um conjunto em cascata de trabalhos de linguagem relacionados à programação funcional, especialmente C # 3.0, LINQ, Rx e F #. Essa progressão também influenciou outros ecossistemas e ainda hoje continua em C #, F # e TypeScript.
Também ajuda que Haskell trabalhe na MSR, é claro :)
É claro que também havia muitas outras influências (JS certamente) e essas etapas foram influenciadas por muitas outras coisas - mas a adição de genéricos a essas linguagens ajudou a quebrar a rígida ortodoxia OO do final dos anos 90 em grandes partes do mundo do software e ajudou a abrir a porta para FP.
Don Syme
O ps F # era 2003, não 2005 - embora digamos que não atingiu 1,0 até 2005. Também fizemos um protótipo Haskell.NET em 2001-02.
fonte
Isso não pretende ser uma resposta séria, mas a pergunta me lembrou um post bem humorado de James Iry - Uma história breve, incompleta e muito errada de linguagens de programação, que inclui a seguinte frase:
"Os lambdas são relegados à relativa obscuridade até que o Java os torne populares por não tê-los".
fonte
Pelo que vejo, a maioria das respostas concentra-se em explicar por que a programação funcional em geral voltou e entrou no mainstream. No entanto, senti que isso realmente não responde à pergunta sobre funções anônimas em particular, e por que elas se tornaram tão populares de repente.
O que realmente ganhou popularidade são os fechamentos . Como na maioria dos casos, os fechamentos são variáveis descartáveis passadas variáveis, obviamente faz sentido usar a sintaxe de função anônima para elas. E, de fato, em alguns idiomas, é a única maneira de criar um fechamento.
Por que os fechamentos ganharam popularidade? Porque eles são úteis na programação orientada a eventos, ao criar funções de retorno de chamada . Atualmente, é a maneira de escrever código de cliente JavaScript (na verdade, é a maneira de escrever qualquer código GUI). Atualmente, é também a maneira de escrever código de back-end altamente eficiente, bem como código do sistema, pois o código escrito no paradigma orientado a eventos geralmente é assíncrono e sem bloqueio . Para back-end, isso se tornou popular como solução para o problema C10K .
fonte
Penso que o motivo é a crescente prevalência de programação concorrente e distribuída, onde o núcleo da programação orientada a objetos (encapsulando a mudança de estado com objetos) não se aplica mais. No caso de um sistema distribuído, porque não é nenhum estado compartilhado (e software abstrações desse conceito são gotejante) e, no caso de um sistema concorrente, porque corretamente sincronizar o acesso a estado compartilhado foi provado complicado e propenso a erros. Ou seja, um dos principais benefícios da programação orientada a objetos não se aplica mais a muitos programas, tornando o paradigma orientado a objetos muito menos útil do que costumava ser.
Por outro lado, o paradigma funcional não usa estado mutável. Qualquer experiência que adquirimos com paradigmas e padrões funcionais é, portanto, mais imediatamente transferível para computação concorrente e distribuída. E, em vez de reinventar a roda, a indústria agora empresta esses padrões e recursos de linguagem para atender a sua necessidade.
fonte
Se adicionarmos € 0,02, embora eu concorde com a importância do JavaScript na introdução do conceito, acho que mais do que a programação simultânea eu culparia a moda atual da programação assíncrona. Ao fazer chamadas assíncronas (necessárias com as páginas da web), funções anônimas simples são tão obviamente úteis, que todo programador da web (ou seja, todo programador) precisava se familiarizar com o conceito.
fonte
Outro exemplo realmente antigo de algo semelhante a funções anônimas / lambdas é a chamada por nome no Algol 60. Observe, no entanto, que a chamada por nome está mais próxima de passar macros como parâmetros do que de passar funções verdadeiras, e é mais frágil / mais difícil de usar. entender como resultado.
fonte
Aqui a ascendência com o melhor de meu conhecimento.
fonte
Funções anônimas são boas porque nomear coisas é difícil e, se você usar uma função apenas uma vez, ela não precisará de um nome.
As funções Lambda só recentemente se tornaram populares porque, até recentemente, a maioria dos idiomas não suportava fechamentos.
Eu sugeriria que o Javascript empurrou esse mainstream. É uma linguagem universal que não tem como expressar paralelismo, e funções anônimas facilitam o uso de modelos de retorno de chamada. Além disso, idiomas populares como Ruby e Haskell contribuíram.
fonte