Vimos que existem dois tipos de função de alta ordem:
- Aquelas que recebem uma ou mais funções como argumentos
- Aquelas que devolvem outra função como valor de retorno
Hoje veremos como funciona o segundo tipo de função de alta ordem: funções que retornam funções.
Para uma primeira exposição da idéia vamos tomar emprestado um exemplo do livro Functional Programming Patterns in Scala and Clojure:
def discount(percent: Double) = { if(percent < 0.0 || percent > 100.0) throw new IllegalArgumentException( "Discounts must be between 0.0 and 100.0.") return (originalPrice: Double) => originalPrice - (originalPrice * percent * 0.01) }
discount é nossa função de alta ordem. Ela retorna uma função que aplica um desconto a um determinado valor. A taxa de desconto a ser aplicada é um parâmetro da função discount.
A beleza deste mecanismo está justamente no fato de que a função de alta ordem possui o poder de construir funções sob medida.
Este tipo de função fica ainda mais flexível ao esperar funções como argumentos.
Um exemplo clássico é o que chamamos de function composition. Quando combinamos as funções f(), g() e h() produzimos uma nova função: f(g(h())).
Vejamos o seguinte trecho de código escrito em CoffeeScript para uma single-page app:
finalizarPadrao = () => @modelOriginal.set @model.attributes finalizarMesmoEnderecoConjuge = _.compose( finalizarPadrao, () => @modelOriginal.get('conjuge').set endereco: @model.get('endereco') ) finalizar = if @model.mesmoEnderecoConjuge then finalizarMesmoEnderecoConjuge else finalizarPadrao
Este código nunca teve a sorte de ser refatorado mas o importante aqui é provar que a combinação de funções (linha 2) nos ajuda a manter distância da duplicação de código. No caso, finalizarMesmoEnderecoConjuge é a função resultante da combinação da função finalizarPadrao com uma função anônima.
Por fim, perceba que compose nada mais é do que uma função que retorna outra função.
Eis abaixo a definição desta função, um lindo trecho de código abrigado por uma linda biblioteca.
_.compose = function() { var funcs = arguments; return function() { var args = arguments; for (var i = funcs.length - 1; i >= 0; i--) { args = [funcs[i].apply(this, args)]; } return args[0]; }; };
A linda biblioteca em questão é o Underscore. Esta biblioteca fornece dezenas de funções utilitárias, inclusive funções de alta ordem universais como find, filter, map e fold e ainda muitas funções inexistentes na biblioteca nativa do JavaScript.
Quero aproveitar este momento para apresentar um conceito importante. O modelo de programação funcional implica na necessidade de “funções como cidadãos de primeiro nível”. Ou seja, funções são tão importantes como números, por exemplo.
Uma linguagem de programação com um bom suporte a este modelo permite que funções possam ser tidas como valores a serem produzidos, consumidos e compostos. Já vimos que é o caso de JavaScript e de Scala.
Por fim, você já deve ter ouvido falar dos padrões Decorator, Strategy e Templated Method.
Mas você nem precisaria ter estudado estes padrões, não fosse a ausência do paradigma funcional nas clássicas linguagens de programação Java e C++.
Isto porque com funções de alta ordem resolvemos estes problemas com o pé nas costas, não é mesmo?
Rafa Noronha
Rafa Noronha gosta de construir aplicações web inovadoras em qualquer plataforma. Possui experiência em diversas tecnologias mas a única com quem rolou algo mais sério foi a Web. Nos últimos anos precisou se especializar em JavaScript, single-page apps e Backbone.js. Na Lambda3 Rafa Noronha jura que Java é legal e está sempre vencendo seus colegas nos confrontos de Mortal Kombat e FIFA14.