Esse post é baseado no livro Teoria das Categorias para Programadores, de Bartosz Milewski.
Dados dois Functors \(F, G : C \rightarrow D\) da categoria \(C\) para a categoria \(D\), chamamos o morfismo \(\alpha_a :: F a \rightarrow G a\) uma Transformação Natural entre \(F\) e \(G\):
alpha :: F a -> G a
Tal transformação deve permitir o quadrado comutativo:
Isso permite criarmos a função g :: F a -> G b
de duas maneiras:
-- G f . alpha = alpha . F f
g = fmap f . alpha = alpha . fmap f
A comutatividade implica que ao aplicar a primeira ou a segunda
definição de g
para um valor de a
, o resultado deve ser exatamente o
mesmo valor de b
. Por exemplo, considere a seguinte transformação
natural de lista para o tipo Maybe
:
safeHead :: [a] -> Maybe a
safeHead [] = Nothing
safeHead (x:xs) = Just x
Para ser uma transformação natural devemos garantir que
fmap f . safeHead = safeHead . fmap f
:
fmap f . safeHead [] = safeHead . fmap f []
fmap f Nothing = safeHead []
Nothing = Nothing
fmap f . safeHead (x:xs) = safeHead . fmap f (x:xs)
fmap f . Just x = safeHead . f x
Just (f x) = Just (f x)
Um outro exemplo é a função length
:
length :: [a] -> Int
podemos pensar nela como uma transformação natural que converte uma
lista para um Functor Const Int a
:
length :: [a] -> Const Int a
Podemos verificar que é uma transformação natural pois pela definição de
fmap
para lista, o tamanho da lista permanece o mesmo. Da mesma forma
a função fmap
aplicado em um valor Const
retorna o próprio valor.
https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/