Haskell · conceitos · monades
As monades em Haskell, explicadas sem o jargão
«Monade» tem uma reputação assustadora que não merece. Despida do jargão, uma monade em Haskell é simplesmente um tipo que lhe permite sequenciar cálculos que transportam um certo contexto — uma possível falha, um efeito secundário, vários resultados — sem escrever a canalização à mão de cada vez. Este guia explica o que é realmente uma monade, as duas operações que a definem, a do-notation e as monades do dia a dia que já utiliza.
A ideia numa frase
Uma monade é um tipo dotado de uma forma de encadear passos em que cada passo depende do resultado do anterior, e em que o «contexto» (falha, efeitos, não-determinismo) é propagado automaticamente. É tudo. Os famosos tipos Maybe, Either, IO e lista são todos monades porque cada um define este encadeamento para o seu próprio tipo de contexto.
As duas operações
A typeclass Monad é definida por duas funções:
return :: a -> m a -- wrap a plain value into the monad
(>>=) :: m a -> (a -> m b) -> m b -- "bind": feed the result into the next step >>= (que se pronuncia «bind») é o seu cerne: recebe um valor em contexto e uma função que produz o valor seguinte em contexto, e junta-os — gerindo o contexto por si.
do-notation: a mesma coisa, de forma legível
Os blocos do são apenas açúcar por cima de >>=. Estes são equivalentes:
-- with bind
getLine >>= \name -> putStrLn ("Hi " ++ name)
-- with do-notation
do name <- getLine
putStrLn ("Hi " ++ name) A do-notation permite-lhe escrever código sequenciado que transporta contexto e que se lê como passos imperativos, mantendo-se puramente funcional.
As monades que já utiliza
Maybe— o contexto é «pode estar ausente». O bind faz curto-circuito emNothing, por isso encadeia pesquisas sem verificações de null aninhadas.Either— o contexto é «pode falhar com um erro». ComoMaybe, mas transporta a razão da falha.IO— o contexto é «executa efeitos secundários». A monade IO é a forma como Haskell sequencia efeitos mantendo-se puro;main :: IO ()é o ponto de entrada.- Lista (
[]) — o contexto é «vários resultados». O bind explora cada combinação (não-determinismo).
Utiliza monades desde o seu primeiro main — IO é uma delas.
Por que as monades importam
Permitem-lhe abstrair a canalização do tratamento de falhas, dos efeitos e da sequenciação num padrão reutilizável, para que o mesmo código em estilo do funcione em contextos muito diferentes. É por isso que Haskell consegue manter IO separado e explícito mantendo-se ergonómico. Para executar tudo isto, precisa da toolchain — veja instalar Haskell com o GHCup e o guia do compilador GHC, e experimente os exemplos ao vivo no GHCi.
FAQ
O que é uma monade em termos simples? Um tipo que lhe permite encadear cálculos que transportam um contexto (falha, efeitos, vários resultados), propagando esse contexto automaticamente através do operador bind.
IO é uma monade? Sim — IO é a monade que Haskell usa para sequenciar efeitos secundários mantendo-se puro. O seu main é executado dentro dela.
É preciso compreender a teoria das categorias? Não. Pode usar as monades de forma produtiva em Haskell conhecendo apenas return, >>= e a do-notation; a teoria é opcional.
Qual é a diferença entre Maybe e Either? Ambas modelam uma possível falha; Maybe diz apenas «ausente» (Nothing), enquanto Either transporta um valor de erro que explica a falha.