Monoides Livres

Esse post é baseado no livro Teoria das Categorias para Programadores, de Bartosz Milewski.

1 Monoids Livres

Relembrando o conceito de Monoid, temos um único objeto \(M\) equipados com um operador de multiplicação \(\cdot\) e um elemento neutro \(\epsilon\) tal que:

\(a, b \in M, a \cdot b \in M\)

\(a \cdot \epsilon = \epsilon \cdot a = a\)

\((a \cdot b) \cdot c = a \cdot (b \cdot c) = a \cdot b \cdot c\)

ou em Haskell:

class Monoid m where
  mempty  :: m
  mappend :: m -> m -> m

Vimos que os números inteiros podem representar um Monoid com o operador de multiplicação e o elemento neutro \(1\) ou com o operador de soma e o elemento neutro \(0\). Uma lista é um Monoid com o operador de concatenação e o elemento neutro de lista vazia.

instance Monoid [] where
  mempty  = []
  mappend = (++)

No caso dos números inteiros, além das propriedades dos Monoids, temos algumas propriedades extras, por exemplo \(2 \cdot 3 = 6\), ou seja, o elemento \(2 \cdot 3\) é equivalente ao elemento \(6\), ambos pertencentes a \(M\).

Já para o caso de listas, temos que [2] ++ [3] / [6]=, ou seja, uma lista contém as propriedades dos Monoids e nada mais.

Um Monoid livre é um Monoid que consegue gerar outros Monoids, partindo de um gerador e adicionando novas operações e propriedades.

Em programação podemos utilizar uma lista como um Monoid livre. Dado um tipo \(A\) podemos gerar o Monoid \([A]\) enumerando todas as combinações de elementos agrupados em uma lista. Considere transformar uma lista de Bool em um Monoid livre, começamos com o elemento neutro da lista e as listas contendo um dos valores possíveis:

data Bool = True | False

m = [ [], [True], [False] ]

Ao aplicar o operador de concatenação entre cada par de elementos dessa lista inicial temos:

m = [ [], [True], [False], [True, True], [True, False], [False, True], [False, False] ]

Continuando tal operação, temos uma lista infinita de todos os elementos do nosso Monoid livre. Para definir um novo Monoid a partir do Monoid livre, basta definirmos um morfismo h :: [a] -> a de tal forma que:

h (mappend x y) = mappend (h x) (h y)

No nosso caso podemos definir h = and para a instância de Monoid de Bool com &&:

and :: [Bool] -> Bool
and []     = True
and (b:bs) = b && (and bs)

instance Monoid Bool where
  mempty  = True
  mappend = (&&)

Podemos verificar que essa é uma função que segue a propriedade:

h ([True] ++ [False]) = (h [True]) && (h [False])
h [True, False] = True && False
False = False

2 Referências

https://bartoszmilewski.com/2014/10/28/category-theory-for-programmers-the-preface/