Baixe o app para aproveitar ainda mais
Prévia do material em texto
PARADIGMAS DE PROGRAMAÇÃO Fabricio Machado da Silva Programação funcional: currying Objetivos de aprendizagem Ao final deste texto, você será capaz de: � Explicar a técnica de currying. � Discutir as vantagens do currying. � Aplicar o currying. Introdução O currying é uma técnica utilizada em programação funcional, que consiste basicamente em transformar uma função originalmente com múltiplos parâmetros em uma que aceite apenas um. Isso é possível em linguagens que implementam closure, fazendo um encadeamento de funções, em que uma retorna a outra a qual recebe outro parâmetro, e assim suces- sivamente até que o resultado esperado seja alcançado. Inventada independentemente, como uma homenagem ao matemá- tico Haskell Curry — por isso a origem do nome —, tem sua importância devido à motivação prática da frequente utilização de funções obtidas por meio de apenas algum dos parâmetros. Algumas linguagens de programação, como Haskell, ML e JavaScript, possuem suporte sintático nativo à técnica de currying. Neste capítulo, você conhecerá um pouco mais sobre o que é a técnica de currying, suas vantagens e aplicabilidades em programação funcional. Definição da técnica currying Currying é uma técnica de transformação de uma função originalmente com múltiplos parâmetros em uma avaliação parcial dos argumentos. A nomencla- tura é uma homenagem a Haskell Curry, que estudava o método (FLANAGAN, 2013). Segundo Flanagan (2013, p. 140), “métodos podem definir múltiplas listas de parâmetros. Quando um método é chamado com uma lista menor de parâ- metros, então será retornada uma função que recebe a lista de parâmetros que falta como argumentos”. Podemos dizer, então, que o processo de currying consiste em quebrar funções de n parâmetros em n funções, onde cada uma deve receber apenas um parâmetro e retornar outra função que espera os parâmetros restantes — logo, uma função representada originalmente por: int, int -> int Ao aplicarmos a técnica de currying, essa função teria o seu tipo alterado, passando a ser representada por: int -> int -> int Para facilitar o entendimento, vamos analisar a Figura 1, a seguir, que mostra originalmente um código em JavaScript de uma função que recebe os parâmetros x e y e retorna a soma de x e y. Figura 1. Função originalmente escrita em JavaScript sem a técnica de currying. Fonte: Adaptada de Lima (2016). var add = function (x,y) { add(1, 2) //3 return x + y; }; Programação funcional: currying2 Agora, passamos à análise da Figura 2, que mostra exatamente a mesma função, com a aplicação da técnica de currying. Figura 2. Função com aplicação da técnica de currying. Fonte: Adaptada de Lima (2016). var add = function(x) { add(1)(2); //3 return function(y) { return x + y; }; }; Ao realizarmos uma análise mais superficial, em um primeiro momento, apenas parece que estamos adicionando mais dificuldade sem nenhum ganho específico. Mas já temos um grande retorno, pois estamos transformando o código em pequenas partes mais expressivas e com maior possibilidade de reuso (FLANAGAN, 2013). Pense em uma aplicação que necessite verificar duas variáveis e retornar as duas em ordem alfabética. Porém, apenas uma delas é recebida, usando uma função normal — certamente o retorno seria um erro, pois não seria possível a comparação. Ao aplicar a técnica de currying, poderia ser retornada uma função que espere que a segunda variável seja enviada e, somente após, ordenasse e realizasse o retorno com as variáveis em ordem alfabética. 3Programação funcional: currying Tanto currying quanto a aplicação parcial são conceitos muito poderosos e que podem facilitar muito suas tarefas no dia a dia, mesmo em linguagens que não são totalmente funcionais. Todavia, a aplicação parcial é um pouco diferente do processo de currying, mas também envolve a questão dos tipos de uma função. Como pudemos percebemos, a técnica de currying tem suas vantagens e, por isso, uma grande importância em linguagens de programação funcionais. Na próxima seção, discutiremos e conheceremos um pouco mais sobre as vantagens dessa aplicação (FLANAGAN, 2013). Vantagens do currying Para entendermos as vantagens da utilização do currying, faz-se necessário conhecer e estar ciente do termo aridade, que representa o número de argu- mentos usados por uma função. Uma função que aceita um único argumento tem aridade de um; uma função que recebe três argumentos tem uma aridade de três (OLIVEIRA, 2017). Além do fato de facilitar o processo de leitura de código, o currying oferece aos desenvolvedores mais controle sobre suas funções. Em resumo, você será capaz de escrever um código mais descritivo que se parece com um idioma simples e, também, de mais fácil reutilização. Além disso, o currying é útil quando você usa funções complexas e deseja ocultar sua implementação. As funções podem ser classificadas com base no número de entradas que aceitam: como se a função binária recebesse duas entradas, enquanto uma função unária, apenas uma única entrada. Segundo Oliveira (2017, p. 142), “em Haskell, toda função recebe apenas uma entrada, ou seja, toda função em Haskell pode ser considerada unária”. Então, não é possível implementar uma função que aceite vários parâmetros? Isso é possível, utilizando currying. Programação funcional: currying4 Podemos, também, ver o currying como uma especialização, uma maneira tradicional de lidar com funções n-árias gerais, desde que as únicas que você possa definir sejam unárias. Para Ayala-Rincón e Moura (2014, p. 98): No cálculo lambda (a partir do qual as linguagens de programação funcio- nal derivam), existem apenas abstrações de uma variável (que se traduz em funções unárias nas linguagens funcionais). Em relação ao cálculo lambda, acho mais fácil provar coisas sobre esse formalismo, já que você não precisa lidar com o caso de funções n-árias (uma vez que você pode representar qualquer função n-ária com várias funções unárias através do currying). Não podemos compor diretamente funções que usam vários parâmetros. Como a composição da função é um dos principais conceitos da programação funcional, usando a técnica currying, podemos compor funções que usam vários parâmetros. Em um exemplo prático, veja a utilização e a vantagem de currying. O framework de JavaScript angular tem uma implementação de curry que permite “definir” parâmetros na função original e, posteriormente, passar um número infinito de parâmetros adicionais. O exemplo apresentado na Figura 3, a seguir, é o mesmo que é usado na documentação do framework. Figura 3. Exemplo do framework angular. 5Programação funcional: currying Considerando que f tem três argumentos curry, podemos chamá-la novamente sem ter que os repetir, poupando, assim, a referida repetição de código e argumentos: // alerta "1, 2, 3, c, d" f('c', 'd'); Currying é, na prática, vantajoso com a reutilização de código. Na próxima seção, você verá alguns exemplos de aplicação de currying com as linguagens de programação Haskell e JavaScript. Aplicação do currying Nas seções anteriores, entendemos o que é o currying e as vantagens de sua aplicação. Para ilustrar, veremos um exemplo de aplicação da técnica utilizando Haskell (OLIVEIRA, 2017). Sendo PLUS uma função que adiciona dois números, queremos, então, adicionar os números X e Y. X como a entrada da função PLUS, que retor- nará uma função chamada PLUS X. A função PLUS X pega um número e adiciona X a ele. Agora, a entrada para essa função será Y, e a saída final será X + Y. A Figura 4, a seguir, ilustra a função PLUS em uma implementação normal e em sua versão currying. Figura 4. Função PLUS em versão normal e aplicando currying. X XY PLUS PLUS PLUS X X + Y Y X + Y Programação funcional: currying6 Pelo uso da função em currying, podemos resolver muitos problemas facilmente. Por exemplo, precisamos criar uma função que use uma lista e outra função como entrada. Aplique essa funçãoa cada elemento dessa lista e retorne à nova lista. Segundo Oliveira (2017, p. 146), “em Haskell, isso pode ser feito com muita facilidade usando a função currying embutida, chamada map.Definition”: map :: (a -> b) -> [a] -> [b] map _ [ ] = [ ] map f (x : xs) = f x : map f xs Analisando o código em Haskell, temos que: � a primeira linha é a inicialização da função; � o símbolo :: significa que "é do tipo"; � [a] representa uma lista de elementos semelhantes, entidade escrita após a última -> é sempre o tipo de retorno da função; � uma função em Haskell sempre retorna apenas uma entidade; � (a-> b) define uma função de a a b; � [] indica lista vazia, e _ indica "qualquer coisa"; � a segunda linha mostra que, se uma lista vazia e qualquer função for inserida, a saída será uma lista vazia; � x: xs é usado para remover os elementos um a um da lista, x é o primeiro elemento (cabeça) e xs é a lista restante (cauda) – : significa concatenação; � a terceira linha está pegando cada elemento da lista e aplicando a função f neles e concatenando-o com a lista restante; � map (+7) [1, 2, 3, 4, 5] deverá retornar a lista [8, 9, 10, 11, 12], onde +7 é a função. Passamos, então, à análise de outro exemplo de aplicação de currying, utilizando a linguagem JavaScript. Imaginemos uma função que cumprimente alguém. Essa função simples de saudação recebe um nome e uma saudação e registra a saudação com o nome no console: var greet = function(greeting, name) { console.log(greeting + ", " + name); }; greet("Olá", "Usuário"); //"Olá, Usuário" 7Programação funcional: currying Essa função requer que o nome e a saudação sejam passados como argumen- tos para funcionar corretamente. Mas podemos reescrevê-la usando currying aninhado simples, para que a função básica exija apenas uma saudação e retorne outra função que leve o nome da pessoa a ser cumprimentada. O código reescrito aplicando currying é: var greetCurried = function(greeting) { return function(name) { console.log(greeting + ", " + name); }; }; Essa modificação na maneira como escrevemos a função permite-nos criar uma nova função para qualquer tipo de saudação e transmitir a ela o nome da pessoa que queremos cumprimentar: var greetHello = greetCurried("Olá"); greetHello("Usuário"); //"Olá, Usuário" greetHello("Usuário Novo"); //"Olá, Usuário Novo" Também podemos chamar a função ao currying original diretamente. Isso é feito apenas passando cada um dos parâmetros em um conjunto separado de parênteses, um após o outro: greetCurried("Olá")("Usuário"); //"Olá, Usuário" O interessante é que, agora que entendemos como modificar nossa função tradicional para usar currying e lidar com argumentos, podemos fazer isso com quantos argumentos quisermos: var greetDeeplyCurried = function(cumprimento) { return function(separador) { return function(enfase) { return function(nome) { console.log(cumprimento + separador + nome + enfase); }; }; }; }; Programação funcional: currying8 O currying é uma técnica incrivelmente útil para programação funcional. Ele permite gerar uma biblioteca de funções pequenas e facilmente confi- guradas, que se comportam de maneira consistente, são rápidas de usar e podem ser entendidas ao ler seu código. A adição de currying à sua prática de codificação incentivará o uso de funções parcialmente aplicadas em todo o código, evitando muitas repetições em potencial e ajudando você a ter melhores hábitos de nomeação e tratamento de argumentos de função. Acesse o link a seguir e veja comentários sobre a técnica de currying na programação. https://qrgo.page.link/nzFzF AYALA-RINCÓN, M.; MOURA, F. L. C. Fundamentos da programação lógica e funcional: o princípio de resolução e a teoria de reescrita. Brasília: Finatec; UnB, 2014. 232 p. FLANAGAN, D. JavaScript: o guia definitivo. 6. ed. Porto Alegre: Bookman, 2013. 1080 p. LIMA, M. Entendendo Programação Funcional em JavaScript de uma vez. [S. l.], 3 mar. 2016. Medium: tableless. Disponível em: https://medium.com/tableless/entendendo- -programa%C3%A7%C3%A3o-funcional-em-javascript-de-uma-vez-c676489be08b. Acesso em: 24 out. 2019. OLIVEIRA, A. G. Haskell: uma introdução à programação funcional. São Paulo: Casa do Código, 2017. 161 p. 9Programação funcional: currying
Compartilhar