Programação funcional - Functional programming

Em ciência da computação , a programação funcional é um paradigma de programação onde os programas são construídos aplicando e compondo funções . É um paradigma de programação declarativo no qual as definições de função são árvores de expressões que mapeiam valores para outros valores, em vez de uma sequência de instruções imperativas que atualizam o estado de execução do programa.

Na programação funcional, as funções são tratadas como cidadãos de primeira classe , o que significa que podem ser vinculadas a nomes (incluindo identificadores locais ), passados ​​como argumentos e retornados de outras funções, assim como qualquer outro tipo de dados pode. Isso permite que os programas sejam escritos em um estilo declarativo e combinável , onde pequenas funções são combinadas de maneira modular .

A programação funcional às vezes é tratada como sinônimo de programação puramente funcional , um subconjunto da programação funcional que trata todas as funções como funções matemáticas determinísticas ou funções puras . Quando uma função pura é chamada com alguns argumentos fornecidos, ela sempre retornará o mesmo resultado e não pode ser afetada por nenhum estado mutável ou outros efeitos colaterais . Isso contrasta com os procedimentos impuros , comuns na programação imperativa , que podem ter efeitos colaterais (como modificar o estado do programa ou receber informações de um usuário). Os defensores da programação puramente funcional afirmam que, ao restringir os efeitos colaterais, os programas podem ter menos bugs , ser mais fáceis de depurar e testar e ser mais adequados à verificação formal .

A programação funcional tem suas raízes na academia , evoluindo do cálculo lambda , um sistema formal de computação baseado apenas em funções. A programação funcional tem sido historicamente menos popular do que a programação imperativa, mas muitas linguagens funcionais estão sendo usadas hoje na indústria e na educação, incluindo Common Lisp , Scheme , Clojure , Wolfram Language , Racket , Erlang , Elixir , OCaml , Haskell e F # . A programação funcional também é fundamental para algumas linguagens que tiveram sucesso em domínios específicos, como JavaScript na Web, R em estatísticas, J , K e Q em análises financeiras e XQuery / XSLT para XML . Linguagens declarativas específicas de domínio, como SQL e Lex / Yacc, usam alguns elementos de programação funcional, como não permitir valores mutáveis . Além disso, muitas outras linguagens de programação oferecem suporte à programação em um estilo funcional ou implementaram recursos de programação funcional, como C ++ 11 , Kotlin , Perl , PHP , Python , Go , Rust , Raku , Scala e Java (desde Java 8 ) .

História

O cálculo lambda , desenvolvido na década de 1930 por Alonzo Church , é um sistema formal de computação construído a partir da aplicação de funções . Em 1937, Alan Turing provou que o cálculo lambda e as máquinas de Turing são modelos equivalentes de computação, mostrando que o cálculo lambda é Turing completo . O cálculo lambda é a base de todas as linguagens de programação funcional. Uma formulação teórica equivalente, a lógica combinatória , foi desenvolvida por Moses Schönfinkel e Haskell Curry nas décadas de 1920 e 1930.

Mais tarde, Church desenvolveu um sistema mais fraco, o cálculo lambda de tipo simples , que estendia o cálculo lambda atribuindo um tipo a todos os termos. Isso forma a base para a programação funcional estaticamente tipada.

A primeira linguagem de programação funcional, LISP , foi desenvolvida no final dos anos 1950 para a série IBM 700/7000 de computadores científicos por John McCarthy enquanto trabalhava no Massachusetts Institute of Technology (MIT). As funções LISP foram definidas usando a notação lambda de Church, estendida com uma construção de rótulo para permitir funções recursivas . Lisp primeiro introduziu muitos recursos paradigmáticos de programação funcional, embora os primeiros Lisps fossem linguagens multiparadigmáticas e incorporassem suporte para vários estilos de programação à medida que novos paradigmas evoluíam. Dialetos posteriores, como Scheme e Clojure , e ramificações como Dylan e Julia , procuraram simplificar e racionalizar o Lisp em torno de um núcleo funcional, enquanto o Common Lisp foi projetado para preservar e atualizar as características paradigmáticas dos numerosos dialetos mais antigos que substituiu.

Information Processing Language (IPL), 1956, às vezes é citado como a primeira linguagem de programação funcional baseada em computador. É uma linguagem de estilo assembly para manipular listas de símbolos. Ele tem uma noção de gerador , que equivale a uma função que aceita uma função como um argumento e, por ser uma linguagem de nível de montagem, o código pode ser um dado, então o IPL pode ser considerado como tendo funções de ordem superior. No entanto, ele depende muito da estrutura da lista em mutação e de recursos imperativos semelhantes.

Kenneth E. Iverson desenvolveu o APL no início dos anos 1960, descrito em seu livro de 1962, A Programming Language ( ISBN  9780471430148 ). APL foi a principal influência sobre John Backus da FP . No início de 1990, Iverson e Roger Hui criado J . Em meados da década de 1990, Arthur Whitney , que já havia trabalhado com Iverson, criado K , que é usado comercialmente em indústrias financeiras, juntamente com seu descendente Q .

John Backus apresentou FP em sua palestra do Prêmio Turing de 1977 "A programação pode ser liberada do estilo de von Neumann ? Um estilo funcional e sua álgebra de programas". Ele define os programas funcionais como sendo construídos de forma hierárquica por meio de "formas combinadas" que permitem uma "álgebra de programas"; na linguagem moderna, isso significa que os programas funcionais seguem o princípio da composicionalidade . O artigo de Backus popularizou a pesquisa em programação funcional, embora enfatizasse a programação em nível de função em vez do estilo lambda-cálculo agora associado à programação funcional.

A linguagem ML 1973 foi criada por Robin Milner na Universidade de Edimburgo , e David Turner desenvolveu a linguagem SASL na Universidade de St Andrews . Também em Edimburgo na década de 1970, Burstall e Darlington desenvolveram a linguagem funcional NPL . O NPL foi baseado nas Equações de Recursão de Kleene e foi introduzido pela primeira vez em seu trabalho sobre a transformação do programa. Burstall, MacQueen e Sannella então incorporaram a verificação de tipo polimórfica de ML para produzir a linguagem Hope . O ML eventualmente se desenvolveu em vários dialetos, os mais comuns dos quais agora são OCaml e Standard ML .

Na década de 1970, Guy L. Steele e Gerald Jay Sussman desenvolveram o Scheme , conforme descrito nos Lambda Papers e no livro de texto de 1985 Structure and Interpretation of Computer Programs . Scheme foi o primeiro dialeto do lisp a usar o escopo léxico e a exigir a otimização da chamada de cauda , recursos que encorajam a programação funcional.

Na década de 1980, Per Martin-Löf desenvolveu a teoria dos tipos intuicionista (também chamada de teoria dos tipos construtivos ), que associa programas funcionais com provas construtivas expressas como tipos dependentes . Isso levou a novas abordagens para a prova interativa de teoremas e influenciou o desenvolvimento de subsequentes linguagens de programação funcional.

A linguagem funcional preguiçosa, Miranda , desenvolvida por David Turner, apareceu inicialmente em 1985 e teve forte influência em Haskell . Com Miranda sendo proprietário, Haskell começou com um consenso em 1987 para formar um padrão aberto para pesquisa de programação funcional; lançamentos de implementação estão em andamento desde 1990.

Mais recentemente, encontrou uso em nichos como CAD paramétrico cortesia da linguagem OpenSCAD construída na estrutura de geometria CSG, embora sua restrição na reatribuição de valores (todos os valores são tratados como constantes) levou à confusão entre os usuários que não estão familiarizados com a programação funcional como um conceito.

A programação funcional continua a ser usada em ambientes comerciais.

Conceitos

Vários conceitos e paradigmas são específicos à programação funcional e geralmente estranhos à programação imperativa (incluindo a programação orientada a objetos ). No entanto, as linguagens de programação geralmente atendem a vários paradigmas de programação, portanto, os programadores que usam linguagens "principalmente imperativas" podem ter utilizado alguns desses conceitos.

Funções de primeira classe e de ordem superior

Funções de ordem superior são funções que podem receber outras funções como argumentos ou retorná-las como resultados. No cálculo, um exemplo de função de ordem superior é o operador diferencial , que retorna a derivada de uma função .

As funções de ordem superior estão intimamente relacionadas às funções de primeira classe , pois as funções de ordem superior e as funções de primeira classe permitem funções como argumentos e resultados de outras funções. A distinção entre os dois é sutil: "ordem superior" descreve um conceito matemático de funções que operam em outras funções, enquanto "primeira classe" é um termo da ciência da computação para entidades de linguagem de programação que não têm restrição ao seu uso (portanto, primeiro funções -class podem aparecer em qualquer lugar no programa que outras entidades de primeira classe como números podem, incluindo como argumentos para outras funções e como seus valores de retorno).

As funções de ordem superior permitem a aplicação parcial ou currying , uma técnica que aplica uma função a seus argumentos um por vez, com cada aplicativo retornando uma nova função que aceita o próximo argumento. Isso permite que um programador expresse sucintamente, por exemplo, a função sucessora como o operador de adição parcialmente aplicado ao número natural um.

Funções puras

Funções puras (ou expressões) não têm efeitos colaterais (memória ou E / S). Isso significa que funções puras têm várias propriedades úteis, muitas das quais podem ser usadas para otimizar o código:

  • Se o resultado de uma expressão pura não for usado, ele pode ser removido sem afetar outras expressões.
  • Se uma função pura é chamada com argumentos que não causam efeitos colaterais, o resultado é constante com relação a essa lista de argumentos (às vezes chamada de transparência referencial ou idempotência ), ou seja, chamar a função pura novamente com os mesmos argumentos retorna o mesmo resultado. (Isso pode permitir otimizações de cache, como memoização .)
  • Se não houver dependência de dados entre duas expressões puras, sua ordem pode ser invertida ou podem ser executadas em paralelo e não podem interferir uma na outra (em outros termos, a avaliação de qualquer expressão pura é thread-safe ).
  • Se toda a linguagem não permitir efeitos colaterais, qualquer estratégia de avaliação pode ser usada; isso dá ao compilador liberdade para reordenar ou combinar a avaliação de expressões em um programa (por exemplo, usando desmatamento ).

Enquanto a maioria dos compiladores para linguagens de programação imperativas detectam funções puras e executam eliminação de subexpressão comum para chamadas de função puras, eles nem sempre podem fazer isso para bibliotecas pré-compiladas, que geralmente não expõem essas informações, evitando otimizações que envolvem essas funções externas. Alguns compiladores, como o gcc , adicionam palavras-chave extras para um programador marcar explicitamente funções externas como puras, para permitir tais otimizações. O Fortran 95 também permite que as funções sejam designadas como puras . C ++ 11 adicionou constexprpalavra-chave com semântica semelhante.

Recursão

A iteração (looping) em linguagens funcionais geralmente é realizada por meio de recursão . As funções recursivas invocam a si mesmas, permitindo que uma operação seja repetida até atingir o caso base . Em geral, a recursão requer a manutenção de uma pilha , o que consome espaço em uma quantidade linear até a profundidade da recursão. Isso poderia tornar o uso da recursão proibitivamente caro em vez de loops imperativos. No entanto, uma forma especial de recursão conhecida como recursão final pode ser reconhecida e otimizada por um compilador no mesmo código usado para implementar iteração em linguagens imperativas. A otimização da recursão de cauda pode ser implementada transformando o programa em um estilo de passagem de continuação durante a compilação, entre outras abordagens.

O padrão de linguagem Scheme requer implementações para suportar a recursão de cauda adequada, o que significa que eles devem permitir um número ilimitado de chamadas de cauda ativas. A recursão de cauda adequada não é simplesmente uma otimização; é um recurso de linguagem que garante aos usuários que eles podem usar a recursão para expressar um loop e fazer isso seria seguro para o espaço. Além disso, ao contrário do seu nome, ele é responsável por todas as chamadas de cauda, ​​não apenas a recursão de cauda. Embora a recursão de cauda adequada seja geralmente implementada transformando o código em loops imperativos, as implementações podem implementá-lo de outras maneiras. Por exemplo, CHICKEN intencionalmente mantém uma pilha e permite que ela transborde . No entanto, quando isso acontecer, seu coletor de lixo reivindicará espaço de volta, permitindo um número ilimitado de chamadas finais ativas, embora não transforme a recursão final em um loop.

Padrões comuns de recursão podem ser abstraídos usando funções de ordem superior, com catamorfismos e anamorfismos (ou "dobras" e "desdobramentos") sendo os exemplos mais óbvios. Esses esquemas de recursão desempenham um papel análogo a estruturas de controle embutidas, como loops em linguagens imperativas .

A maioria das linguagens de programação funcional de propósito geral permite recursão irrestrita e é Turing completa , o que torna o problema de parada indecidível , pode causar problemas de raciocínio equacional e geralmente requer a introdução de inconsistência na lógica expressa pelo sistema de tipos da linguagem . Algumas linguagens de propósito especial, como Coq, permitem apenas recursão bem fundamentada e são fortemente normalizadoras (cálculos não terminais podem ser expressos apenas com fluxos infinitos de valores chamados codata ). Como consequência, essas linguagens deixam de ser Turing completas e expressar certas funções nelas é impossível, mas elas ainda podem expressar uma ampla classe de cálculos interessantes, evitando os problemas introduzidos pela recursão irrestrita. A programação funcional limitada a uma recursão bem fundamentada com algumas outras restrições é chamada de programação funcional total .

Avaliação estrita versus avaliação não estrita

As linguagens funcionais podem ser categorizadas por usarem avaliação estrita (ansiosa) ou não estrita (preguiçosa) , conceitos que se referem a como os argumentos da função são processados ​​quando uma expressão está sendo avaliada. A diferença técnica está na semântica denotacional de expressões contendo cálculos com falha ou divergentes. Sob avaliação estrita, a avaliação de qualquer termo que contenha um subtermo reprovado falha. Por exemplo, a expressão:

print length([2+1, 3*2, 1/0, 5-4])

falha na avaliação estrita devido à divisão por zero no terceiro elemento da lista. Na avaliação preguiçosa, a função de comprimento retorna o valor 4 (ou seja, o número de itens na lista), uma vez que avaliá-la não tenta avaliar os termos que compõem a lista. Em resumo, a avaliação estrita sempre avalia totalmente os argumentos da função antes de invocar a função. A avaliação lenta não avalia os argumentos da função, a menos que seus valores sejam necessários para avaliar a própria chamada da função.

A estratégia de implementação usual para avaliação preguiçosa em linguagens funcionais é a redução de gráficos . A avaliação lenta é usada por padrão em várias linguagens funcionais puras, incluindo Miranda , Clean e Haskell .

Hughes 1984 defende a avaliação preguiçosa como um mecanismo para melhorar a modularidade do programa por meio da separação de interesses , facilitando a implementação independente de produtores e consumidores de fluxos de dados. Launchbury 1993 descreve algumas dificuldades que a avaliação preguiçosa introduz, particularmente na análise dos requisitos de armazenamento de um programa, e propõe uma semântica operacional para auxiliar em tal análise. Harper 2009 propõe incluir avaliação estrita e preguiçosa no mesmo idioma, usando o sistema de tipo do idioma para distingui-los.

Sistemas de tipo

Especialmente desde o desenvolvimento da inferência de tipo Hindley-Milner na década de 1970, as linguagens de programação funcional tenderam a usar cálculo lambda tipado , rejeitando todos os programas inválidos em tempo de compilação e arriscando erros positivos falsos , em oposição ao cálculo lambda não tipado , que aceita todos os cálculos válidos programas em tempo de compilação e riscos de erros falsos negativos , usados ​​em Lisp e suas variantes (como Scheme ), embora rejeitem todos os programas inválidos em tempo de execução quando a informação é suficiente para não rejeitar programas válidos. O uso de tipos de dados algébricos torna conveniente a manipulação de estruturas de dados complexas; a presença de uma forte verificação de tipo em tempo de compilação torna os programas mais confiáveis ​​na ausência de outras técnicas de confiabilidade, como o desenvolvimento orientado a testes , enquanto a inferência de tipo libera o programador da necessidade de declarar manualmente os tipos para o compilador na maioria dos casos.

Algumas linguagens funcionais orientadas para pesquisa, como Coq , Agda , Cayenne e Epigram são baseadas na teoria dos tipos intuicionista , que permite que os tipos dependam dos termos. Esses tipos são chamados de tipos dependentes . Esses sistemas de tipo não têm inferência de tipo decidível e são difíceis de entender e programar. Mas os tipos dependentes podem expressar proposições arbitrárias na lógica de ordem superior . Por meio do isomorfismo de Curry-Howard , então, programas bem tipados nessas linguagens se tornam um meio de escrever provas matemáticas formais a partir das quais um compilador pode gerar código certificado . Embora essas linguagens sejam de interesse principalmente na pesquisa acadêmica (inclusive na matemática formalizada ), elas também começaram a ser usadas na engenharia. Compcert é um compilador para um subconjunto da linguagem de programação C que é escrito em Coq e verificado formalmente.

Uma forma limitada de tipos dependentes chamados tipos de dados algébricos generalizados (GADTs) pode ser implementada de uma forma que forneça alguns dos benefícios da programação com tipos dependentes, evitando a maior parte de seus inconvenientes. Os GADTs estão disponíveis no Compilador Glasgow Haskell , em OCaml e em Scala , e foram propostos como acréscimos a outras linguagens, incluindo Java e C #.

Transparência referencial

Os programas funcionais não possuem instruções de atribuição, ou seja, o valor de uma variável em um programa funcional nunca muda depois de definido. Isso elimina qualquer chance de efeitos colaterais porque qualquer variável pode ser substituída por seu valor real em qualquer ponto de execução. Portanto, os programas funcionais são referencialmente transparentes.

Considere a instrução de atribuição Cx = x * 10 , isso muda o valor atribuído à variável x. Digamos que o valor inicial de xfoi 1, então duas avaliações consecutivas dos xrendimentos variáveis 10e 100respectivamente. Claramente, substituir x = x * 10por 10ou 100dá a um programa um significado diferente e, portanto, a expressão não é referencialmente transparente. Na verdade, as instruções de atribuição nunca são referencialmente transparentes.

Agora, considere outra função, como é transparente, uma vez que não muda implicitamente a entrada x e, portanto, não tem tais efeitos colaterais . Os programas funcionais usam exclusivamente esse tipo de função e, portanto, são referencialmente transparentes. int plusone(int x) {return x+1;}

Estruturas de dados

Estruturas de dados puramente funcionais são freqüentemente representadas de uma maneira diferente das suas contrapartes imperativas . Por exemplo, a matriz com acesso e tempos de atualização constantes é um componente básico da maioria das linguagens imperativas, e muitas estruturas de dados imperativas, como a tabela de hash e o heap binário , são baseadas em matrizes. Os arrays podem ser substituídos por mapas ou listas de acesso aleatório, que admitem implementação puramente funcional, mas possuem acesso logarítmico e tempos de atualização. Estruturas de dados puramente funcionais têm persistência , uma propriedade de manter as versões anteriores da estrutura de dados inalteradas. Em Clojure, estruturas de dados persistentes são usadas como alternativas funcionais para suas contrapartes imperativas. Os vetores persistentes, por exemplo, usam árvores para atualização parcial. Chamar o método de inserção resultará na criação de alguns, mas não todos os nós.

Comparação com a programação imperativa

A programação funcional é muito diferente da programação imperativa . As diferenças mais significativas decorrem do fato de que a programação funcional evita efeitos colaterais , que são usados ​​na programação imperativa para implementar estado e E / S. A programação funcional pura evita completamente os efeitos colaterais e fornece transparência referencial.

As funções de ordem superior raramente são usadas na programação imperativa mais antiga. Um programa imperativo tradicional pode usar um loop para percorrer e modificar uma lista. Um programa funcional, por outro lado, provavelmente usaria uma função de “mapa” de ordem superior que recebe uma função e uma lista, gerando e retornando uma nova lista aplicando a função a cada item da lista.

Programação imperativa vs. funcional

Os dois exemplos a seguir (escritos em JavaScript ) obtêm o mesmo efeito: eles multiplicam todos os números pares em uma matriz por 10 e somam todos, armazenando a soma final na variável "resultado".

Loop imperativo tradicional:

const numList = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let result = 0;
for (let i = 0; i < numList.length; i++) {
  if (numList[i] % 2 === 0) {
    result += numList[i] * 10;
  }
}

Programação funcional com funções de ordem superior:

const result = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
               .filter(n => n % 2 === 0)
               .map(a => a * 10)
               .reduce((a, b) => a + b);

Simulando estado

Existem tarefas (por exemplo, manter o saldo de uma conta bancária) que muitas vezes parecem mais naturalmente implementadas com o estado. A programação funcional pura executa essas tarefas e tarefas de E / S, como aceitar a entrada do usuário e imprimir na tela, de uma maneira diferente.

A linguagem de programação puramente funcional Haskell os implementa usando mônadas , derivadas da teoria das categorias . As mônadas oferecem uma maneira de abstrair certos tipos de padrões computacionais, incluindo (mas não se limitando a) modelagem de cálculos com estado mutável (e outros efeitos colaterais, como E / S) de maneira imperativa, sem perder a pureza. Embora as mônadas existentes possam ser fáceis de aplicar em um programa, dados modelos e exemplos apropriados, muitos alunos acham difícil entendê-los conceitualmente, por exemplo, quando solicitados a definir novas mônadas (o que às vezes é necessário para certos tipos de bibliotecas).

As linguagens funcionais também simulam estados passando estados imutáveis. Isso pode ser feito fazendo com que uma função aceite o estado como um de seus parâmetros e retorne um novo estado junto com o resultado, deixando o antigo estado inalterado.

Linguagens funcionais impuras geralmente incluem um método mais direto de gerenciamento de estado mutável. Clojure , por exemplo, usa referências gerenciadas que podem ser atualizadas aplicando funções puras ao estado atual. Esse tipo de abordagem permite a mutabilidade, ao mesmo tempo que promove o uso de funções puras como a forma preferida de expressar cálculos.

Métodos alternativos, como lógica Hoare e exclusividade , foram desenvolvidos para rastrear efeitos colaterais em programas. Algumas linguagens de pesquisa modernas usam sistemas de efeitos para tornar explícita a presença de efeitos colaterais.

Problemas de eficiência

Linguagens de programação funcional são normalmente menos eficientes no uso de CPU e memória do que linguagens imperativas, como C e Pascal . Isso está relacionado ao fato de que algumas estruturas de dados mutáveis, como arrays, têm uma implementação muito direta usando o hardware atual. As matrizes planas podem ser acessadas de maneira muito eficiente com CPUs com pipelines profundas, pré-buscadas com eficiência por meio de caches (sem perseguição de ponteiro complexo ) ou manipuladas com instruções SIMD. Também não é fácil criar suas contrapartes imutáveis ​​de uso geral igualmente eficientes. Para linguagens puramente funcionais, o pior caso de desaceleração é logarítmico no número de células de memória usadas, porque a memória mutável pode ser representada por uma estrutura de dados puramente funcional com tempo de acesso logarítmico (como uma árvore balanceada). No entanto, essas desacelerações não são universais. Para programas que realizam cálculos numéricos intensivos, linguagens funcionais como OCaml e Clean são apenas ligeiramente mais lentas que C, de acordo com o The Computer Language Benchmarks Game . Para programas que lidam com grandes matrizes e bancos de dados multidimensionais , as linguagens funcionais de array (como J e K ) foram projetadas com otimizações de velocidade.

A imutabilidade dos dados pode, em muitos casos, levar à eficiência de execução, permitindo que o compilador faça suposições que não são seguras em uma linguagem imperativa, aumentando assim as oportunidades de expansão em linha .

A avaliação preguiçosa também pode acelerar o programa, mesmo assintoticamente, ao passo que pode desacelerá-lo no máximo por um fator constante (no entanto, pode introduzir vazamentos de memória se usado incorretamente). Launchbury 1993 discute questões teóricas relacionadas a vazamentos de memória da avaliação preguiçosa, e O'Sullivan et al. 2008 oferece alguns conselhos práticos para analisá-los e corrigi-los. No entanto, as implementações mais gerais de avaliação preguiçosa que fazem uso extensivo de código e dados não referenciados têm um desempenho ruim em processadores modernos com pipelines profundos e caches de vários níveis (onde um erro de cache pode custar centenas de ciclos).

Programação funcional em linguagens não funcionais

É possível usar um estilo funcional de programação em linguagens que não são tradicionalmente consideradas linguagens funcionais. Por exemplo, tanto D quanto Fortran 95 suportam explicitamente funções puras.

JavaScript , Lua , Python e Go tiveram funções de primeira classe desde o início. Python tinha suporte para " lambda ", " mapear ", " reduzir " e " filtrar " em 1994, bem como encerramentos no Python 2.2, embora o Python 3 tenha relegado "reduzir" para o functoolsmódulo de biblioteca padrão. Funções de primeira classe foram introduzidas em outras linguagens convencionais, como PHP 5.3, Visual Basic 9 , C # 3.0, C ++ 11 e Kotlin .

Em PHP , classes anônimas , encerramentos e lambdas são totalmente suportados. Bibliotecas e extensões de linguagem para estruturas de dados imutáveis ​​estão sendo desenvolvidas para auxiliar a programação no estilo funcional.

Em Java , as classes anônimas às vezes podem ser usadas para simular fechamentos ; no entanto, as classes anônimas nem sempre são substitutos adequados para os encerramentos porque têm recursos mais limitados. Java 8 oferece suporte a expressões lambda como uma substituição para algumas classes anônimas.

Em C # , as classes anônimas não são necessárias, porque closures e lambdas são totalmente suportados. Bibliotecas e extensões de linguagem para estruturas de dados imutáveis ​​estão sendo desenvolvidas para auxiliar a programação no estilo funcional em C #.

Muitos padrões de design orientado a objetos podem ser expressos em termos de programação funcional: por exemplo, o padrão de estratégia simplesmente dita o uso de uma função de ordem superior e o padrão de visitante corresponde aproximadamente a um catamorfismo , ou dobra .

Da mesma forma, a ideia de dados imutáveis ​​de programação funcional é frequentemente incluída em linguagens de programação imperativas, por exemplo, a tupla em Python, que é um array imutável, e Object.freeze () em JavaScript.

Formulários

Planilhas

As planilhas podem ser consideradas uma forma de sistema de programação funcional puro, de ordem zero e de avaliação estrita. No entanto, as planilhas geralmente carecem de funções de ordem superior, bem como de reutilização de código e, em algumas implementações, também carecem de recursão. Várias extensões foram desenvolvidas para programas de planilha para permitir funções de ordem superior e reutilizáveis, mas até agora permanecem principalmente de natureza acadêmica.

Academia

A programação funcional é uma área ativa de pesquisa no campo da teoria da linguagem de programação . Existem vários locais de publicação revisados ​​por pares com foco em programação funcional, incluindo a Conferência Internacional sobre Programação Funcional , o Jornal de Programação Funcional e o Simpósio sobre Tendências em Programação Funcional .

Indústria

A programação funcional tem sido usada em uma ampla variedade de aplicações industriais. Por exemplo, Erlang , que foi desenvolvido pela empresa sueca Ericsson no final dos anos 1980, foi originalmente usado para implementar sistemas de telecomunicações tolerantes a falhas , mas desde então se tornou popular para construir uma variedade de aplicativos em empresas como Nortel , Facebook , Électricité de França e WhatsApp . Scheme , um dialeto do Lisp , foi usado como base para várias aplicações nos primeiros computadores Apple Macintosh e foi aplicado a problemas como software de simulação de treinamento e controle de telescópio . OCaml , que foi introduzido em meados da década de 1990, teve uso comercial em áreas como análise financeira, verificação de driver , programação de robô industrial e análise estática de software embarcado . Haskell , embora inicialmente pretendido como uma linguagem de pesquisa, também foi aplicado por uma série de empresas, em áreas como sistemas aeroespaciais, design de hardware e programação web.

Outras linguagens de programação funcional que foram usadas na indústria incluem Scala , F # , Wolfram Language , Lisp , Standard ML e Clojure .

As "plataformas" funcionais têm sido populares em finanças para análise de risco (especialmente com os maiores bancos de investimento). Os fatores de risco são codificados como funções que formam gráficos interdependentes (categorias) para medir correlações nas mudanças de mercado, não ao contrário das otimizações de base de Gröbner, mas também para conformidade regulatória, como Análise e Revisão Abrangente de Capital . Dado o uso de variações OCAML ou CAML em finanças, esses sistemas às vezes são considerados relacionados a uma máquina abstrata categórica ou CAM. Na verdade, a programação funcional é fortemente influenciada pela teoria das categorias .

Educação

Muitas universidades ensinam ou ensinaram programação funcional como parte de seus cursos de graduação em Ciência da Computação. Alguns o usam como introdução à programação, enquanto outros o ensinam depois de ensinar programação imperativa.

Fora da ciência da computação, a programação funcional está sendo usada como um método para ensinar a solução de problemas, álgebra e conceitos geométricos. Também tem sido usado como uma ferramenta para ensinar mecânica clássica em Estrutura e Interpretação da Mecânica Clássica .

Veja também

Referências

Leitura adicional

links externos

Ouça este artigo ( 28 minutos )
Ícone falado da Wikipedia
Este arquivo de áudio foi criado a partir de uma revisão deste artigo datada de 25 de agosto de 2011 e não reflete as edições subsequentes. ( 25/08/2011 )