DEV Community

Marcio Frayze
Marcio Frayze

Posted on • Updated on

Programação Funcional: Functors

Definição

Em Haskell, um Functor representa uma Type class capaz de realizar uma transformação, e é definido por:

class Functor f where  
    fmap :: (a -> b) -> f a -> f b  
Enter fullscreen mode Exit fullscreen mode

fmap é uma função de primeira ordem e (a -> b) representa a função de transformação que será aplicada à cada elemento de f a. O resultado final será um novo Functor, representado por f b.

A primeira linha deste código indica que será possível executar a função fmap em qualquer tipo que seja uma instância de Functor.

Lembrando que em Haskell, Class tem um significado diferente do usado na orientação a objetos. Para mais informações, leia sobre o que é uma Type class (em inglês).

Se estiver confusa, não se preocupe. Vamos analisar alguns exemplos e é mais simples do que parece.

Exemplos

Um exemplo clássico de Functor são as Listas.

Podemos fazer um fmap em uma lista, executando uma função de transformação para cada elemento. Como resultado temos uma lista de mesmo tamanho, mas podendo conter elementos de algum outro tipo ou do mesmo tipo mas com outros valores, ou até mesmo uma lista igual à inicial.

Como toda função de primeira ordem, a função que passamos como argumento para o fmap pode ser nomeada (que tenha sido declarada anteriormente) ou uma expressão lambda (também conhecida como função anônima).

Utilizando fmap com uma expressão lambda

Se quisermos somar 1 à todos os elementos de uma lista de números, podemos escrever o seguinte código em Haskell:

fmap (\n -> n + 1) [1, 2, 3, 4] 
Enter fullscreen mode Exit fullscreen mode

Para definir uma expressão lambda nesta linguagem, utilizamos o caractere \ (que lembra um pouco o letra lambda do alfabeto grego: λ).

No trecho (\n -> n + 1) estamos definindo uma expressão lambda com um parâmetro numérico n, que irá retornar n + 1. Esta função será executada para cada elemento da lista e uma nova lista será criada ao final dessas execuções.

Desta forma o resultado deste fmap será:

[2, 3, 4, 5]
Enter fullscreen mode Exit fullscreen mode

Voltando para a definição, neste exemplo:

  • f é um Functor do tipo Lista, que em Haskell é representando pelo símbolo [].
  • a neste caso é um Num, representando o tipo dos elementos contidos na lista de entrada.
  • b também é um Num, representando o tipo dos elementos da lista que será retornada.
  • (a -> b) é uma função de transformação, que irá receber um elemento do tipo a e retornar um elemento do tipo b. Neste caso, irá receber um número a e retornar um outro número b.

Em Haskell Num é uma Type class que representa os tipos numéricos.

Neste exemplo, a e b são da mesma classe, mas poderíamos ter como resultado uma lista com elementos de outra classe.

Transformando o tipo dos elementos de uma lista

Se quiséssemos por exemplo transformar uma lista de números em uma lista de strings (que no Haskell é representado por um array de char), poderíamos escrever o seguinte código:

fmap (\n -> show n) [10, 11, 12, 13] 
Enter fullscreen mode Exit fullscreen mode

Onde show é uma função capaz de transformar um número em um array de caracteres.

Em nosso código acima, fmap irá primeiro executar a função show passando o primeiro elemento da lista como argumento, que irá retornar o valor "1".

Em seguida percorrerá os demais elementos da lista e, um por um, irá executar a função show, passando um elemento da lista por vez. Conforme esta operação é realizada, o resultado de cada executação é armazenado em uma nova lista.

Após percorrer por todos os elementos e terminar de criar a lista com o resultado de todas as operações, fmap irá retornar uma nova lista contendo:

["10", "11", "12", "13"]
Enter fullscreen mode Exit fullscreen mode

Dizemos que fmap é uma função de tansformação. Mas, embora seja possível mudar o tipo dos elementos contidos dentro do Functor (neste caso, a Lista), através do fmap não é possível mudar o tipo do Functor e nem seu tamanho. Ou seja, dada uma lista com 4 elementos, o fmap sempre retornará uma lista contendo 4 elementos.

Voltando mais uma vez para definição:

class Functor f where  
    fmap :: (a -> b) -> f a -> f b  
Enter fullscreen mode Exit fullscreen mode

Note que a entrada é f a e a saída é f b. Ou seja, o tipo do Functor, representado pela letra f, é obrigatórimente o mesmo.

Utilizado fmap com uma função nomeada (named function)

Podemos utilizar uma função já existente e passa-lá com argumento para o fmap.

No exemplo abaixo definimos uma função chamada soma1 e em seguida a passamos como argumento para o fmap:

soma1 n = n + 1

fmap soma1 [1, 2, 3, 4] 
Enter fullscreen mode Exit fullscreen mode

E o resultado será igual ao obtido quando utilizamos uma expressão lambda:

[2, 3, 4, 5]
Enter fullscreen mode Exit fullscreen mode

Este mecanismo é útil em 2 situações:

  • Quando a função é muito complexa, para facilitar o entendimento do código ou;
  • Quando queremos re-aproveitar uma função pré-existente.

Processamento preguiçoso

Na verdade eu menti um pouquinho para você! Em Haskell o processamento da função fmap é preguiçoso (lazy evaluation). Por isso, simpliquei um pouco quando disse que fmap retorna uma nova lista.

O que ocorre na verdade nesta linguagem de programação (e algumas outras também) é que este processamento é postergado até o último momento possível. A execução fica pendente e, quando o próximo valor da lista é requisitado, a operação é realizada para aquele elemento específico e retornado para o chamador.

Mas este já é um assunto para um outro artigo.

Outros Functors

É comum usarmos listas para exemplificar o uso do fmap, mas existem vários outros exemplos muito importantes e podemos até mesmo instanciar nossos próprios Functors!

Outros Functors famosos incluem o Maybe (conhecido também como Optional em algumas linguagens) e o Either. Mas este assunto também vai ficar para um outro artigo.

Conclusão

O que foi discutido aqui é apenas uma introdução ao tema. Para aprender mais sobre este e outro conceitos da Programação Funcional, recomendo o excelente (e gratuito) livro Learn You a Haskell for Great Good! e se quiser se aprofundar em Haskell recomendo ler também o Haskell in Depth.


Curtiu este artigo? Talvez você vá gostar também do p de Podcast, um podcast semanal que participo sobre Arquitetura de Software e Tecnologia: https://anchor.fm/pdepodcast

Top comments (1)

Collapse
 
epsi profile image
E.R. Nurwijayadi