Eu li muitos posts que explicam o que são mônadas, como unit
e bind
funcionam, algumas delas mergulhando diretamente na teoria das categorias tão abstratas (pelo menos para mim) que fazem os olhos sangrarem, outras ignorando isso completamente e tocando em analogias estranhas de burritos, caixas e o que não.
Depois de algumas semanas de estudo e muitos neurônios fritos, (eu acho), eu entendo como as mônadas funcionam. Mas ainda há uma coisa que escapa à minha compreensão, algo que poucas postagens realmente abordam (exceto IO e estado):
PORQUE?
Por que as mônadas são importantes? Por que eles são tão importantes? Quais são os problemas que eles estão resolvendo? Esses problemas só podem ser resolvidos com mônadas ou existem outras maneiras?
functional-programming
problem-solving
monad
Dummy Me
fonte
fonte
Respostas:
Você não precisa de mônadas para resolver nada. Eles apenas simplificam certas coisas. Muitas pessoas são abstratas e teóricas ao explicar mônadas. Principalmente, as mônadas são um padrão que surge repetidamente na programação. Ao reconhecer esse padrão, podemos simplificar nosso código e evitar reimplementar determinadas funções.
Para Haskell, de um ponto de vista concreto, a coisa mais visível que as mônadas permitem é a notação . A compreensão de listas em Haskell e em outros idiomas também tira grande vantagem das mônadas. Você também pode criar bibliotecas como Control.Monad .
Tudo isso fornece simplificações úteis e, depois de implementado para uma mônada, você o obtém automaticamente para todas as mônadas. Essa é uma das principais razões pelas quais a reutilização de código é muito mais fácil para a programação funcional do que outros paradigmas.
fonte
IO
é a mônada mais proeminente de Haskell , mas eu não estava listando exemplos de mônadas individuais. Eu estava listando exemplos das abstrações abrangentes que eles permitem. Eu estava tentando entender por que, por exemplo, o primeiro cara a pensar em usar uma mônada para E / S consideraria uma boa ideia em geral.É mais fácil entender se você olhar para as mônadas específicas e ver quais problemas elas resolvem. Por exemplo, em Haskell:
IO: Permite que o IO seja representado no sistema de tipos, para que você possa separar funções puras das funções que executam o IO.
Lista: permite fazer uma compreensão de lista e cálculos nodeterminísticos.
Talvez: Uma alternativa melhor para os nulos e suportando algo comparável ao operador de coalescência nula do C #.
Parsec: um DSL conveniente para escrever analisadores.
Portanto, é fácil (espero) ver a justificativa para as mônadas individuais, pois todas elas são bastante úteis. Mas os problemas que eles resolvem também são bem diferentes e, aparentemente, eles não têm muito em comum, exceto que todos estão relacionados a alguma lógica para operações de encadeamento. Mônadas são úteis porque permitem que você construa uma variedade de ferramentas.
Os exemplos acima podem ser implementados sem mônadas? Certamente eles poderiam ser implementados de maneira ad-hoc, mas ter mônadas incorporadas na linguagem permite o suporte direto ao idioma, como a
do
notação que funciona com todas as mônadas.fonte
Uma coisa que torna confuso é que o "popular" funciona como
bind
e<*>
é orientado para a práxis. Mas, para entender os conceitos, é mais fácil observar outras funções primeiro. Também vale a pena notar que as mônadas se destacam porque são um pouco exageradas em comparação com outros conceitos conectados. Então, vou começar com functors.Functors oferecem uma função (na notação Haskell)
fmap :: (Functor f) => (a -> b) -> f a -> f b
. Em outras palavras, você tem um contexto nof
qual você pode elevar uma função. Como você pode imaginar, quase tudo é um functor. Lista, Talvez, Qualquer um, funções, E / S, tuplas, analisadores ... Cada um representa um contexto no qual um valor pode aparecer. Assim, você pode escrever funções extremamente versáteis que funcionam em praticamente qualquer contexto usandofmap
ou sua variante embutida<$>
.Que outras coisas você quer fazer com os contextos? Você pode combinar dois contextos. Então você pode querer obter uma generalização
zip :: [a] -> [b] -> [(a,b)]
por exemplo como este:pair :: (Monoidal f) => f a -> f b -> f (a,b)
.Mas, como é ainda mais útil na prática, as bibliotecas Haskell oferecem
Applicative
, que é uma combinação deFunctor
eMonoidal
, e também deUnit
, o que apenas acrescenta que você pode realmente colocar valores "dentro" do seu contextounit
.Você pode escrever funções extremamente genéricas, declarando essas três coisas sobre o contexto em que está trabalhando.
Monad
é apenas mais uma coisa que você pode afirmar além disso. O que eu não mencionei antes é que você já tem duas maneiras de combinar dois contextos: você pode não apenaspair
eles, mas também pode empilhá-los; por exemplo, você pode ter uma lista de listas. No contexto de E / S, um exemplo seria uma ação de E / S que pode ler outras ações de E / S de um arquivo, portanto você teria um tipoFilePath -> IO (IO a)
. Como podemos nos livrar desse empilhamento para obter uma função executávelIO a
? É aí queMonad
sjoin
chega, ele nos permite combinar dois contextos empilhados do mesmo tipo. O mesmo vale para analisadores, talvez etc. Ebind
é apenas uma maneira mais prática de usarjoin
Portanto, um contexto monádico apenas oferece quatro coisas e pode ser usado com quase todo o maquinário desenvolvido para E / S, analisadores, falhas etc.
fonte
Mônadas permite que você expresse vários cálculos não puros, além de simplificar o código
E, mais importante, sem comprometer as construções de linguagem pura e obter uma linguagem mais limpa com isso
fonte
Maybe
não estão relacionadas a nada externo.