Então você ainda não entende monads?

Marcelo Camargo
Rung Developers
Published in
4 min readDec 12, 2017
Um monad em seu habitat natural

Acadêmicos, não me matem. Este artigo visa abordar monads de uma maneira humana e prática, analisando problemas e soluções, e não as leis matemáticas que os compõe. A Internet está cheia de artigos com a definição formal, mas faltam textos mais contextuais e filosóficos sobre o tema.

Quando você começa a aprofundar-se em programação funcional, começa também a deparar-se com termos obscuros até então nunca sequer ouvidos, termos abstratos e que as pessoas tem dificuldade de explicar por envolverem outros termos tão abstratos quanto, termos matemáticos; então você finge que entendeu o conceito porque está em alta, porque é 🔝, porque é “apenas um monoid na categorias dos endofunctors”, mas por dentro ainda está confuso 😕.

Existem diversos tutoriais sobre monads na Internet com as mais bizarras analogias para tentar explicá-los. Analogias que variam de elefantes a burritos mexicanos. Monads foram aplicados às linguagens de programação para resolver problemas, mas que problemas eles resolvem e como resolvem? Vamos entender antes um pouco como a maioria das linguagens funcionais trabalham.

Ordem? O que é isso?

Levando em conta que todas as definições de um programa consistem de funções e possuímos imutabilidade, a ordem de computação das expressões do seu código deixa de ser importante porque temos segurança de que não existirão efeitos capazes interferir na resolução de dependências entre expressões. Considere a seguinte expressão, válida em Haskell e em JavaScript:

É apenas uma lista com operações em números inteiros, certo?

É sim, mas a diferença de Haskell para JavaScript é que em Haskell você não tem controle sobre a ordem em que as expressões são computadas. Enquanto JavaScript vai fazer 1 + 1, então 2 + 2 e assim computar sequencialmente, Haskell vai computar arbitrariamente porque não há dependência entre os valores; geralmente vai computar em paralelo (mais de uma expressão ao mesmo tempo), o que nos dá “de graça” grandes ganhos de performance através de garantia matemática, porque consegue realizar múltiplas computações em diferentes núcleos do seu processador e diferentes processos, mas nem tudo são flores.

O problema

Mas e quando a ordem realmente importa? E quando eu tenho efeitos e preciso que eles sigam uma sequência, como quando escrevo algo na tela em uma chamada de função ou leio algo da tela?

Monads ao resgate!

Monads não são sobre “purismo funcional”, são sobre sequenciamento e ordem. O termo é bastante elitizado, mas, basicamente, é uma especificação ─ um conjunto de leis ─ que permite controlarmos a ordem e a dependência de computações em linguagens funcionais e possuirmos algo relativamente próximo aos statements das linguagens imperativas. Analise o seguinte trecho em Python:

Note que o programa é composto por duas ações:

  • Ler o nome do usuário;
  • Exibir o nome do usuário em uma mensagem.

Nessa situação, precisamos de uma operação que bloqueie a UI e bloqueie a computação seguinte (por conta da dependência), mas linguagens puramente funcionais, como Haskell, não tem o conceito de sequenciar, então o que faremos?

A solução

Como a abstração de linguagens funcionais deriva do cálculo-λ, usamos a abstração matemática para moldar sequenciamento.

Um monad, então, programaticamente, é uma estrutura que pode ser composta com outra de maneira a retornar outra estrutura também capaz disso. Linguagens imperativas não precisam de monads porque já temos sequenciamento por padrão (atráves dos statements), mas o lado ruim delas é que é muito mais difícil para o compilador encontrar oportunidades de otimização). Vamos reescrever o programa em Python que vimos antes em Haskell:

Utilizamos os operadores >> e >>= para sequenciar as operações. É como definimos os “passos” de um programa de maneira composicional.

É como brincar de Lego!

As operações de escrever e ler da tela retornam monads. Escrever retorna um monad sem conteúdo interno e ler retorna um monad com o texto. O operador >> sequencia e ignora o valor interno do monad à esquerda e o operador >>= sequencia e chama a função à direita passando o conteúdo do monad e exige que outro monad seja retornado para poder continuar compondo. Uma analogia próxima das linguagens atuais é comparar monads com o ;; apesar das distinções “matemáticas”, o problema que visam resolver é similar.

Quando monads são úteis?

  • Quando precisamos combinar computações;
  • Quando lidamos com computações que podem falhar e sua continuação é condicional;
  • Quando queremos lidar com computações bloqueantes e dependentes;
  • Se precisarmos definir um intervalo de tempo entre cada operação, similar ao setTimeout do JavaScript;
  • Quando queremos dizer para o amiguinho que conhecemos monads 😎.

Há diversas aplicações práticas, mas que visam resolver problemas de linguagens com um mindset funcional. Se você utiliza apenas Java e C, você precisa conhecer monads tanto quanto um peixe precisa de sua bicicleta, mas se você utiliza ou estuda uma linguagem funcional, conhecer a definição vai ser de grande valia.

De uma maneira mais formal

Para ser considerado um monad, há leis matemáticas que precisam ser cumpridas 👮; esse não é o foco deste artigo. Você pode encontrar diversos tutoriais sobre a formalização; este é um tutorial para humanos.

Leituras recomendadas

--

--