No último post você viu um exemplo de uma função para calcular o fatorial de um número.

Agora, continuando no tópico de funções, veremos mais alguns conceitos.

Primeiramente, você viu que foi especificado o tipo da função fatorial, que era Int -> Int, ou seja, uma função que recebe um inteiro e retorna um inteiro.

Agora, vejamos outra função:

soma :: Int -> Int -> Int
soma a b = a + b

Como o nome nos diz, essa função retorna a soma de seus dois argumentos.

Vamos começar a ver alguns de seus detalhes.

A primeira coisa que deve ser notada é que esta é uma função pura. ou seja, ela não faz alteração de nenhum estado e seu valor de retorno depende unicamente de seus valores de entrada.

“Mas você não havia dito que linguagens funcionais não guardam estado? Então todas as funções são puras?”

Não exatamente. Para conflitar com isso, vejamos, por exemplo, uma função que retorne um número aleatório. Normalmente estas não recebem nenhum parâmetro, mas retornam resultados variados. Então esse tipo de função não é pura.
Outro exemplo de funções impuras são funções que realizam operações de entrada e saída.

Outra coisa que deve ser notada é a declaração do tipo da função: Int -> Int -> Int. Isso não parece com algo que receba dois inteiros e retorne outro. E realmente não é isso.

Se você olhar a origem das linguagens funcionais, verá que são uma evolução do Cálculo Lambda, e uma de suas características é que as funções são unárias, ou seja, recebem apenas um argumento.

Voltemos ao fato de que funções são tipos de “alta ordem” em linguagens funcionais. Isso quer dizer que funções podem ser passadas como argumentos, ou podem ser retornadas de outros funções.

Na verdade, o que acontece é exatamente isso, em uma técnica chamada currying. O que ocorre realmente é que a função soma recebe apenas um argumento, retorna uma função que recebe mais um argumento e que, aí sim, retorna o resultado: Int -> (Int -> Int).

No último post (que ficou meio incompleto, confesso), comecei a falar um pouco sobre o paradigma da programação funcional.

Uma das coisas citadas é o fato de o programa ser tratado como um conjunto de funções matemáticas. O que isso quer dizer?

Para visualizar isso, vamos ao clássico exemplo do fatorial.

Matematicamente falando, n! (n fatorial) é o produto de todos os números menores ou iguais a n. Ou seja, o produto entre todos os números de 1 até n. Por exemplo: 5! = 1 * 2 * 3 * 4 * 5.

Observe que 0! = 1 já que o resultado de não se multiplicar nenhum número (identidade multiplicativa) é 1 (mais informações em http://en.wikipedia.org/wiki/Empty_product).

Normalmente existem 2 formas de se fazer isso: recursiva e interativa:

Recursiva:

int fatorial(int n)
{
    if(n <= 1)
        return 1;
    return n * fatorial(n - 1);
}

 
Interativa:

int fatorial(int n)
{
    int r = 1;

    while(n > 1)
        r *= n--;
    return r;
}

Isso em uma linguagem funcional (no caso, Haskell), ficaria como:

fatorial :: Int -> Int
fatorial 0 = 1
fatorial n = n * fatorial (n - 1)

Muito parecido com a versão recursiva em linguagens não-funcionais.

E, de fato, muito do que se faz em uma linguagem funcional é recursivo.

Porém, uma linguagem funcional normalmente oferece vários “artifícios matemáticos” para se trabalhar.

Em Haskell, por exemplo, isso poderia ser escrito como

fatorial :: Int -> Int
fatorial n = product [1..n]

ou seja, literalmente o que diz a definição: o produto entre todos os números de 1 até n.

Uma das principais vantagens disso é a redução da quantidade de código. Considerando que a declaração do tipo não é necessáriamente obrigatória (apesar de uma boa prática) a função pode ser implementada em apenas uma ou duas linhas, respeitando os padrões de identação e tudo mais.

Como o título do post sugere, começarei a abordar Programação Funcional. Ainda olhando para o título, pode-se perceber que essa é apenas a primeira de uma série de artigos. E olhando mais para o final do título, você poderá perceber que esta é apenas uma definição, ou simples explanação do que é o paradigma de programação funcional.

Então, vamos lá…

Programação funcional é o estilo de programação que trata as partes de um programa como funções matemáticas. Isso quer dizer que o programa não guarda estado, não possui variáveis, enfim, não tem nada que altere o valor. Tudo (ou quase) são funções.

Linguagens que seguem este estilo (como Haskell, por exemplo) tem as funções como tipos de “primeira classe”, isto é, você pode declarar funções, passá-las como parâmetros para outras funções, enfim, são tipos de dados como qualquer outro.

Outra característica da programação funcional é que normalmente as funções são puras, ou seja, não dependem de nenhum “efeito colateral”, como variáveis globais, passagem por referência, enfim, seu resultado depende apenas dos parâmetros de entrada.

Agora você deve estar se perguntando: “Por que eu iria querer usar uma linguagem que não tem variáveis?”.

Algumas das vantagens da programação funcional:

  • Não guardando o estado de nada, o compilador pode fazer otimizações normalmente inseguras em outro tipo de linguagem;
  • Normalmente as linguagens funcionais usam Lazy Evaluation, ou seja, só avaliam uma coisa se ela for realmente usada;
  • É eficiente em aplicativos multi-threaded, já que não há risco de que algo indesejado seja alterado.