Tendo examinado brevemente Haskell recentemente, qual seria uma explicação breve, sucinta e prática sobre o que é essencialmente uma mônada?
Descobri que a maioria das explicações que encontrei é bastante inacessível e carece de detalhes práticos.
haskell
functional-programming
monads
terminology
Peter Mortensen
fonte
fonte
Respostas:
Primeiro: o termo mônada é um pouco vazio se você não é um matemático. Um termo alternativo é construtor de computação, que é um pouco mais descritivo do que eles realmente são úteis.
Você pede exemplos práticos:
Exemplo 1: Compreensão da lista :
Essa expressão retorna as duplas de todos os números ímpares no intervalo de 1 a 10. Muito útil!
Acontece que este é realmente apenas um açúcar sintático para algumas operações dentro da mônada da Lista. A mesma compreensão da lista pode ser escrita como:
Ou até:
Exemplo 2: Entrada / Saída :
Ambos os exemplos usam mônadas, construtores de computação AKA. O tema comum é que a mônada encadeia operações de alguma maneira específica e útil. Na compreensão da lista, as operações são encadeadas de modo que, se uma operação retorna uma lista, as seguintes operações são executadas em todos os itens da lista. A mônada de E / S, por outro lado, executa as operações sequencialmente, mas transmite uma "variável oculta", que representa "o estado do mundo", o que nos permite escrever o código de E / S de uma maneira pura e funcional.
Acontece que o padrão de operações de encadeamento é bastante útil e é usado para muitas coisas diferentes em Haskell.
Outro exemplo são as exceções: usando a
Error
mônada, as operações são encadeadas de forma que sejam executadas seqüencialmente, exceto se um erro for gerado; nesse caso, o restante da cadeia será abandonado.Tanto a sintaxe de compreensão da lista quanto a doação são açúcar sintático para operações de encadeamento usando o
>>=
operador. Uma mônada é basicamente apenas um tipo que suporta o>>=
operador.Exemplo 3: Um analisador
Este é um analisador muito simples que analisa uma string ou um número entre aspas:
As operações
char
,digit
etc. são bastante simples. Eles combinam ou não. A mágica é a mônada que gerencia o fluxo de controle: as operações são executadas seqüencialmente até que uma correspondência falhe; nesse caso, a mônada retorna para a última<|>
e tenta a próxima opção. Novamente, uma maneira de encadear operações com alguma semântica adicional e útil.Exemplo 4: Programação assíncrona
Os exemplos acima estão em Haskell, mas o F # também suporta mônadas. Este exemplo é roubado de Don Syme :
Este método busca uma página da web. A linha de perfuração é o uso de
GetResponseAsync
- na verdade aguarda a resposta em um thread separado, enquanto o thread principal retorna da função. As últimas três linhas são executadas no encadeamento gerado quando a resposta é recebida.Na maioria dos outros idiomas, você teria que criar explicitamente uma função separada para as linhas que lidam com a resposta. A
async
mônada é capaz de "dividir" o bloco por conta própria e adiar a execução da segunda metade. (Aasync {}
sintaxe indica que o fluxo de controle no bloco é definido pelaasync
mônada.)Como eles trabalham
Então, como uma mônada pode fazer tudo isso? O que realmente acontece em um bloco de tarefas (ou em uma expressão de cálculo, como são chamados em F #), é que todas as operações (basicamente todas as linhas) são agrupadas em uma função anônima separada. Essas funções são então combinadas usando o
bind
operador (escrito>>=
em Haskell). Como abind
operação combina funções, ela pode executá-las como achar necessário: sequencialmente, várias vezes, ao contrário, descarta algumas, executa algumas em um encadeamento separado quando lhe apetecer e assim por diante.Como exemplo, esta é a versão expandida do código IO do exemplo 2:
Isso é mais feio, mas também é mais óbvio o que realmente está acontecendo. O
>>=
operador é o ingrediente mágico: ele pega um valor (no lado esquerdo) e o combina com uma função (no lado direito), para produzir um novo valor. Esse novo valor é então utilizado pelo próximo>>=
operador e novamente combinado com uma função para produzir um novo valor.>>=
pode ser visto como um mini-avaliador.Observe que
>>=
está sobrecarregado para tipos diferentes, portanto, cada mônada tem sua própria implementação>>=
. (Todas as operações na cadeia precisam ser do tipo da mesma mônada, caso contrário, o>>=
operador não funcionará.)A implementação mais simples possível
>>=
apenas pega o valor à esquerda e aplica-o à função à direita e retorna o resultado, mas, como dito anteriormente, o que torna todo o padrão útil é quando há algo extra acontecendo na implementação da mônada de>>=
.Há alguma inteligência adicional em como os valores são passados de uma operação para a seguinte, mas isso requer uma explicação mais profunda do sistema do tipo Haskell.
Resumindo
Em termos de Haskell, uma mônada é um tipo parametrizado que é uma instância da classe de tipo Mônada, que define
>>=
junto com alguns outros operadores. Em termos leigos, uma mônada é apenas um tipo para o qual a>>=
operação está definida.Por si
>>=
só, é apenas uma maneira complicada de encadear funções, mas com a presença da notação que oculta o "encanamento", as operações monádicas acabam sendo uma abstração muito agradável e útil, útil em muitos lugares da linguagem e útil para criar seus próprios mini-idiomas no idioma.Por que as mônadas são difíceis?
Para muitos aprendizes de Haskell, as mônadas são um obstáculo que atingem como uma parede de tijolos. Não é que as mônadas sejam complexas, mas a implementação depende de muitos outros recursos avançados do Haskell, como tipos parametrizados, classes de tipos e assim por diante. O problema é que o Haskell I / O é baseado em mônadas, e é provavelmente uma das primeiras coisas que você deseja entender ao aprender um novo idioma - afinal, não é muito divertido criar programas que não produzem nenhum resultado. Não tenho solução imediata para esse problema de galinha e ovo, exceto tratar a E / S como "mágica acontece aqui" até que você tenha experiência suficiente com outras partes da linguagem. Desculpa.
Excelente blog sobre mônadas: http://adit.io/posts/2013-04-17-functors,_applicatives,_and_monads_in_pictures.html
fonte
Explicar "o que é uma mônada" é como dizer "o que é um número?" Usamos números o tempo todo. Mas imagine que você conheceu alguém que não sabia nada sobre números. Como diabos você explicaria o que são números? E como você começaria a descrever por que isso pode ser útil?
O que é uma mônada? A resposta curta: é uma maneira específica de encadear operações.
Em essência, você está escrevendo etapas de execução e vinculando-as à "função de ligação". (Em Haskell, ele é chamado
>>=
.) Você pode gravar as chamadas para o operador de ligação, ou pode usar a sintaxe sugar, que faz com que o compilador insira essas chamadas de função para você. Mas, de qualquer maneira, cada etapa é separada por uma chamada para essa função de ligação.Portanto, a função de ligação é como um ponto e vírgula; separa as etapas em um processo. O trabalho da função de ligação é pegar a saída da etapa anterior e alimentá-la na próxima etapa.
Isso não parece muito difícil, certo? Mas há mais de um tipo de mônada. Por quê? Quão?
Bem, a função de ligação pode apenas pegar o resultado de uma etapa e alimentá-lo para a próxima etapa. Mas se isso é "tudo" a mônada faz ... isso realmente não é muito útil. E isso é importante para entender: toda mônada útil faz outra coisa além de ser apenas uma mônada. Toda mônada útil tem um "poder especial", o que a torna única.
(Uma mônada que não faz nada de especial é chamada de "mônada de identidade". Mais ou menos como a função de identidade, isso soa como uma coisa totalmente sem sentido, mas acaba não sendo ... Mas isso é outra história ™.)
Basicamente, cada mônada tem sua própria implementação da função de ligação. E você pode escrever uma função de ligação, de modo que faça coisas interessantes entre as etapas de execução. Por exemplo:
Se cada etapa retornar um indicador de êxito / falha, você poderá executar a ligação na próxima etapa apenas se a anterior tiver sido bem-sucedida. Dessa forma, uma etapa com falha aborta toda a sequência "automaticamente", sem nenhum teste condicional de você. (A Mônada de Falha .)
Estendendo essa idéia, você pode implementar "exceções". (A Mônada de erro ou Mônaco de exceção ). Como você está definindo elas mesmas, e não como um recurso de idioma, é possível definir como elas funcionam. (Por exemplo, talvez você queira ignorar as duas primeiras exceções e abortar apenas quando uma terceira exceção for lançada.)
Você pode fazer com que cada etapa retorne vários resultados e faça com que a função de vinculação faça um loop sobre eles, alimentando cada uma delas na próxima etapa. Dessa forma, você não precisa continuar escrevendo loops em todo o lugar ao lidar com vários resultados. A função de ligação "automaticamente" faz tudo isso para você. (A lista Mônada .)
Além de passar um "resultado" de uma etapa para outra, você pode fazer com que a função de ligação passe dados extras também. Esses dados agora não aparecem no seu código-fonte, mas você ainda pode acessá-los de qualquer lugar, sem precisar passá-los manualmente para todas as funções. (O leitor Mônada .)
Você pode fazer isso para que os "dados extras" possam ser substituídos. Isso permite simular atualizações destrutivas , sem realmente fazer atualizações destrutivas. (A Mônada do Estado e seu primo, a Mônada do Escritor .)
Como você está apenas simulando atualizações destrutivas, pode fazer coisas triviais que seriam impossíveis com atualizações destrutivas reais . Por exemplo, você pode desfazer a última atualização ou reverter para uma versão mais antiga .
Você pode fazer uma mônada onde os cálculos podem ser pausados , para que você possa pausar seu programa, entrar e mexer nos dados internos do estado e depois retomar.
Você pode implementar "continuações" como uma mônada. Isso permite que você quebre a mente das pessoas!
Tudo isso e muito mais é possível com mônadas. Obviamente, tudo isso também é perfeitamente possível sem mônadas. É drasticamente mais fácil usar mônadas.
fonte
Na verdade, ao contrário do entendimento comum das mônadas, elas nada têm a ver com o estado. Mônadas são simplesmente uma maneira de embrulhar as coisas e fornecer métodos para executar operações no material embrulhado sem desembrulhá-lo.
Por exemplo, você pode criar um tipo para quebrar outro, em Haskell:
Para embrulhar coisas que definimos
Para executar operações sem desembrulhar, digamos que você tenha uma função
f :: a -> b
, você pode fazer isso para elevar essa função para agir com base em valores agrupados:Isso é tudo o que há para entender. No entanto, verifica-se que há uma função mais geral para fazer esse levantamento , que é
bind
:bind
pode fazer um pouco mais do quefmap
, mas não vice-versa. Na verdade, sófmap
pode ser definido em termos debind
ereturn
. Portanto, ao definir uma mônada, você indica o tipo (aqui estavaWrapped a
) e depois diz como as operaçõesreturn
ebind
as funções funcionam.O legal é que isso acaba sendo um padrão tão geral que aparece em todo o lugar, encapsular o estado de maneira pura é apenas um deles.
Para um bom artigo sobre como as mônadas podem ser usadas para introduzir dependências funcionais e, assim, controlar a ordem de avaliação, como é usada na mônada de IO da Haskell, consulte IO Inside .
Quanto à compreensão das mônadas, não se preocupe muito com isso. Leia sobre eles o que achar interessante e não se preocupe se não entender imediatamente. Depois, basta mergulhar em um idioma como Haskell. As mônadas são uma dessas coisas em que a compreensão entra em seu cérebro pela prática; um dia você percebe de repente que as entende.
fonte
Mas, você poderia ter inventado as mônadas!
fonte
Uma mônada é um tipo de dados que possui duas operações:
>>=
(akabind
) ereturn
(akaunit
).return
pega um valor arbitrário e cria uma instância da mônada com ele.>>=
pega uma instância da mônada e mapeia uma função sobre ela. (Você já pode ver que uma mônada é um tipo estranho de tipo de dados, pois na maioria das linguagens de programação não era possível escrever uma função que pega um valor arbitrário e cria um tipo a partir dela. As mônadas usam um tipo de polimorfismo paramétrico .)Na notação Haskell, a interface monad é escrita
Essas operações devem obedecer a certas "leis", mas isso não é muito importante: as "leis" apenas codificam a maneira como as implementações sensíveis das operações devem se comportar (basicamente, isso
>>=
ereturn
devem concordar sobre como os valores são transformados em instâncias mônadas e isso>>=
é associativo).As mônadas não são apenas sobre estado e E / S: elas abstraem um padrão comum de computação que inclui trabalhar com estado, E / S, exceções e não-determinismo. Provavelmente, as mônadas mais simples de entender são listas e tipos de opções:
onde
[]
e:
são os construtores da lista,++
é o operador de concatenação eJust
eNothing
são osMaybe
construtores. Ambas as mônadas encapsulam padrões comuns e úteis de computação em seus respectivos tipos de dados (observe que nenhum deles tem nada a ver com efeitos colaterais ou E / S).Você realmente precisa escrever um código Haskell não trivial para apreciar o que são as mônadas e por que são úteis.
fonte
Você deve primeiro entender o que é um functor. Antes disso, entenda as funções de ordem superior.
Uma função de ordem superior é simplesmente uma função que aceita uma função como argumento.
Um functor é qualquer construção de tipo
T
para a qual exista uma função de ordem superior, chame-amap
, que transforma uma função do tipoa -> b
(dados dois tiposa
eb
) em uma funçãoT a -> T b
. Essamap
função também deve obedecer às leis de identidade e composição, de modo que as seguintes expressões retornem verdadeiras para todosp
eq
(notação de Haskell):Por exemplo, um construtor de tipo chamado
List
é um functor se for equipado com uma função do tipo(a -> b) -> List a -> List b
que obedece às leis acima. A única implementação prática é óbvia. AList a -> List b
função resultante itera sobre a lista fornecida, chamando a(a -> b)
função para cada elemento e retorna a lista dos resultados.A mônada é essencialmente apenas um functor
T
com dois métodos extra,join
, do tipoT (T a) -> T a
, eunit
(às vezes chamadoreturn
,fork
oupure
) do tipoa -> T a
. Para listas em Haskell:Por que isso é útil? Porque você pode, por exemplo,
map
sobre uma lista com uma função que retorna uma lista.Join
pega a lista resultante de listas e as concatena.List
é uma mônada porque isso é possível.Você pode escrever uma função que sim
map
, entãojoin
. Essa função é chamadabind
, ouflatMap
, ou(>>=)
, ou(=<<)
. Normalmente, é assim que uma instância de mônada é fornecida no Haskell.Uma mônada deve satisfazer certas leis, a saber, que
join
devem ser associativas. Isso significa que, se você tiver um valorx
do tipo[[[a]]]
,join (join x)
deverá ser igualjoin (map join x)
. Epure
deve ser uma identidade parajoin
taljoin (pure x) == x
.fonte
[Isenção de responsabilidade: ainda estou tentando gritar completamente as mônadas. A seguir, é exatamente o que eu entendi até agora. Se estiver errado, espero que alguém com conhecimento me ligue no tapete.]
Arnar escreveu:
É exatamente isso. A ideia é a seguinte:
Você pega algum tipo de valor e o envolve com algumas informações adicionais. Assim como o valor é de um determinado tipo (por exemplo, um número inteiro ou uma string), as informações adicionais são de um determinado tipo.
Por exemplo, essa informação extra pode ser um
Maybe
ou umIO
.Então você tem alguns operadores que permitem operar os dados agrupados enquanto carregam essas informações adicionais. Esses operadores usam as informações adicionais para decidir como alterar o comportamento da operação no valor agrupado.
Por exemplo, a
Maybe Int
pode ser umJust Int
ouNothing
. Agora, se você adicionar umMaybe Int
a aMaybe Int
, o operador verificará se ambos estãoJust Int
dentro de si e, se houver, desembrulhará osInt
s, passará a eles o operador de adição e reorganizará o resultadoInt
em um novoJust Int
(que é válidoMaybe Int
) e, portanto, retorne aMaybe Int
. Mas se um deles fosseNothing
interno, esse operador retornaria imediatamenteNothing
, o que novamente é válidoMaybe Int
. Dessa forma, você pode fingir que seusMaybe Int
números são apenas normais e executar matemática regularmente com eles. Se você conseguir umNothing
, suas equações ainda produzirão o resultado certo - sem que você precise fazer verificações de lixo emNothing
todos os lugares .Mas o exemplo é exatamente o que acontece
Maybe
. Se a informação extra fosse umIO
, então esse operador especial definido paraIO
s seria chamado e poderia fazer algo totalmente diferente antes de realizar a adição. (OK, somar doisIO Int
s provavelmente não faz sentido - ainda não tenho certeza.) (Além disso, se você prestou atenção aoMaybe
exemplo, notou que "agrupar um valor com coisas extras" nem sempre é correto. Mas é difícil para ser exato, correto e preciso sem ser inescrutável.)Basicamente, "mônada" significa aproximadamente "padrão" . Mas, em vez de um livro cheio de padrões explicados informalmente e nomeados especificamente, agora você tem uma construção de linguagem - sintaxe e tudo - que permite declarar novos padrões como itens de seu programa . (A imprecisão aqui é que todos os padrões precisam seguir uma forma específica, de modo que uma mônada não é tão genérica quanto um padrão. Mas acho que esse é o termo mais próximo que a maioria das pessoas conhece e entende.)
E é por isso que as pessoas acham as mônadas tão confusas: porque são um conceito tão genérico. Perguntar o que faz de algo uma mônada é igualmente vago, como perguntar o que faz de algo um padrão.
Mas pense nas implicações de ter suporte sintático na linguagem para a idéia de um padrão: em vez de ter que ler o livro da Gangue dos Quatro e memorizar a construção de um padrão específico, basta escrever um código que implemente esse padrão de forma agnóstica, maneira genérica uma vez e então está pronto! Você pode reutilizar esse padrão, como Visitor ou Strategy ou Façade ou qualquer outra coisa, apenas decorando as operações em seu código com ele, sem ter que reimplementá-lo repetidamente!
É por isso que as pessoas que entendem as mônadas as acham tão úteis : não é um conceito de torre de marfim que os esnobes intelectuais se orgulham de entender (OK, isso também é claro, eles), mas na verdade torna o código mais simples.
fonte
M (M a) -> M a
. O fato de você poder transformar isso em um tipoM a -> (a -> M b) -> M b
é o que os torna úteis.Depois de muito esforço, acho que finalmente entendi a mônada. Depois de reler minha longa crítica à resposta predominantemente votada, apresentarei esta explicação.
Há três perguntas que precisam ser respondidas para entender as mônadas:
Como observei em meus comentários originais, muitas explicações sobre mônadas são abordadas na questão número 3, sem e antes de realmente cobrir adequadamente a questão 2 ou a questão 1.
Por que você precisa de uma mônada?
Linguagens funcionais puras como Haskell são diferentes das linguagens imperativas como C ou Java, pois um programa funcional puro não é necessariamente executado em uma ordem específica, uma etapa de cada vez. Um programa Haskell é mais parecido com uma função matemática, na qual você pode resolver a "equação" em qualquer número de possíveis ordens. Isso confere uma série de benefícios, entre os quais se elimina a possibilidade de certos tipos de bugs, particularmente aqueles relacionados a coisas como "estado".
No entanto, existem certos problemas que não são tão simples de resolver com esse estilo de programação. Algumas coisas, como a programação do console e a E / S de arquivo, precisam que as coisas aconteçam em uma ordem específica ou que mantenham o estado. Uma maneira de lidar com esse problema é criar um tipo de objeto que represente o estado de uma computação e uma série de funções que tomam um objeto de estado como entrada e retornam um novo objeto de estado modificado.
Então, vamos criar um valor hipotético de "estado", que representa o estado de uma tela do console. exatamente como esse valor é construído não é importante, mas digamos que seja uma matriz de caracteres ascii de comprimento de byte que represente o que está atualmente visível na tela e uma matriz que represente a última linha de entrada inserida pelo usuário no pseudocódigo. Definimos algumas funções que assumem o estado do console, modificam-no e retornam um novo estado do console.
Então, para fazer a programação do console, mas de uma maneira funcional pura, você precisaria aninhar muitas chamadas de função dentro de cada um.
Programar dessa maneira mantém o estilo funcional "puro", enquanto força as alterações no console para acontecer em uma ordem específica. Mas, provavelmente, queremos fazer mais do que apenas algumas operações por vez, como no exemplo acima. As funções de aninhamento dessa maneira começarão a se tornar desajeitadas. O que queremos é um código que faça essencialmente a mesma coisa que acima, mas que seja escrito um pouco mais como este:
Esta seria realmente uma maneira mais conveniente de escrevê-lo. Como fazemos isso?
O que é uma mônada?
Depois de ter um tipo (como
consolestate
) que você define, juntamente com várias funções projetadas especificamente para operar nesse tipo, você pode transformar todo o pacote dessas coisas em uma "mônada", definindo um operador como:
(bind) que automaticamente feeds retornam valores à esquerda, para parâmetros de função à direita, e umlift
operador que transforma funções normais, em funções que funcionam com esse tipo específico de operador de ligação.Como uma mônada é implementada?
Veja outras respostas, que parecem bastante livres para entrar nos detalhes disso.
fonte
Depois de responder a esta pergunta há alguns anos, acredito que posso melhorar e simplificar essa resposta com ...
Uma mônada é uma técnica de composição de funções que externaliza o tratamento para alguns cenários de entrada usando uma função de composição
bind
, para pré-processar a entrada durante a composição.Na composição normal, a função,
compose (>>)
é usada para aplicar a função composta ao resultado de seu predecessor em sequência. É importante ressaltar que a função que está sendo composta é necessária para lidar com todos os cenários de sua entrada.(x -> y) >> (y -> z)
Esse design pode ser aprimorado reestruturando a entrada para que os estados relevantes sejam mais facilmente interrogados. Portanto, em vez de simplesmente
y
o valor pode se tornarMb
, por exemplo,(is_OK, b)
sey
incluir uma noção de validade.Por exemplo, quando a entrada é possivelmente apenas um número, em vez de retornar uma sequência que pode conter um número obedientemente ou não, você pode reestruturar o tipo em um
bool
indicando a presença de um número válido e um número na tupla, comobool * float
,. As funções compostas não precisariam mais analisar uma sequência de entrada para determinar se existe um número, mas poderiam apenas inspecionar abool
parte de uma tupla.(Ma -> Mb) >> (Mb -> Mc)
Aqui, novamente, a composição ocorre naturalmente
compose
e, portanto, cada função deve lidar com todos os cenários de sua entrada individualmente, embora isso agora seja muito mais fácil.No entanto, e se pudéssemos externalizar o esforço de interrogatório nos momentos em que lidar com um cenário é rotineiro? Por exemplo, e se o nosso programa não fizer nada quando a entrada não estiver boa como em quando
is_OK
estáfalse
. Se isso fosse feito, as funções compostas não precisariam lidar com esse cenário, simplificando dramaticamente seu código e efetuando outro nível de reutilização.Para alcançar essa externalização, poderíamos usar uma função
bind (>>=)
,, para executar ocomposition
invés decompose
. Como tal, em vez de simplesmente transferir valores da saída de uma função para a entrada de outraBind
, inspecionaria aM
parteMa
e decidiria se e como aplicar a função composta àa
. Obviamente, a funçãobind
seria definida especificamente para o nosso particular, deM
modo a poder inspecionar sua estrutura e executar o tipo de aplicação que desejamos. No entanto, oa
pode ser qualquer coisa, poisbind
apenas passa o nãoa
inspecionado para a função composta quando determina a aplicação necessária. Além disso, as próprias funções compostas não precisam mais lidar com oM
parte da estrutura de entrada, simplificando-os. Conseqüentemente...(a -> Mb) >>= (b -> Mc)
ou mais sucintamenteMb >>= (b -> Mc)
Em resumo, uma mônada externaliza e, portanto, fornece comportamento padrão em relação ao tratamento de certos cenários de entrada, uma vez que a entrada é projetada para expô-los suficientemente. Esse design é um
shell and content
modelo em que o shell contém dados relevantes para a aplicação da função composta e é interrogado e permanece disponível apenas para abind
função.Portanto, uma mônada é três coisas:
M
concha para armazenar informações relevantes da mônada,bind
função implementada para fazer uso dessas informações do shell em sua aplicação das funções compostas aos valores de conteúdo encontrados dentro do shell, ea -> Mb
produzindo resultados que incluem dados de gerenciamento monádicos.De um modo geral, a entrada para uma função é muito mais restritiva do que sua saída, o que pode incluir coisas como condições de erro; portanto, a
Mb
estrutura de resultados é geralmente muito útil. Por exemplo, o operador de divisão não retorna um número quando o divisor é0
.Além disso,
monad
s podem incluir funções de quebra automática que envolvem valores,a
no tipo monádicoMa
, e funções geraisa -> b
, em funções monádicasa -> Mb
, envolvendo os resultados após a aplicação. Obviamentebind
, tais funções de wrap são específicas paraM
. Um exemplo:O design da
bind
função pressupõe estruturas de dados imutáveis e funções puras, outras coisas ficam complexas e garantias não podem ser feitas. Como tal, existem leis monádicas:Dado...
Então...
Associativity
significa quebind
preserva a ordem da avaliação, independentemente de quandobind
é aplicada. Ou seja, na definição deAssociativity
acima, a força de avaliação precoce do parêntesisbinding
def
eg
só vai resultar em uma função que esperaMa
, a fim de completar abind
. Portanto, a avaliação deMa
deve ser determinada antes que seu valor possa ser aplicadof
e esse resultado, por sua vez, aplicadog
.fonte
Uma mônada é, efetivamente, uma forma de "operador de tipo". Isso fará três coisas. Primeiro, ele "quebra" (ou converte) um valor de um tipo em outro (normalmente chamado de "tipo monádico"). Em segundo lugar, disponibilizará todas as operações (ou funções) no tipo subjacente no tipo monádico. Finalmente, ele fornecerá suporte para combinar a si próprio com outra mônada para produzir uma mônada composta.
A "talvez mônada" é essencialmente o equivalente a "tipos anuláveis" no Visual Basic / C #. Ele pega um tipo não anulável "T" e o converte em "Nullable <T>" e depois define o que todos os operadores binários significam em um Nullable <T>.
Os efeitos colaterais são representados de forma semelhante. É criada uma estrutura que contém descrições dos efeitos colaterais ao lado do valor de retorno de uma função. As operações "levantadas" copiam os efeitos colaterais à medida que os valores são passados entre as funções.
Eles são chamados de "mônadas" em vez do nome mais fácil de entender de "operadores de tipo" por vários motivos:
fonte
(Veja também as respostas em O que é uma mônada? )
Uma boa motivação para as Mônadas é o Você poderia ter inventado as Mônadas de sigfpe (Dan Piponi) ! (E talvez você já tenha) . Existem muitos outros tutoriais de mônada , muitos dos quais tentam explicar erroneamente mônadas em "termos simples" usando várias analogias: esta é a falácia do tutorial de mônada ; Evite-os.
Como o DR MacIver diz em Diga-nos por que seu idioma é péssimo :
Você diz que entende a mônada Talvez? Bom, você está a caminho. Comece a usar outras mônadas e, mais cedo ou mais tarde, você entenderá o que são mônadas em geral.
[Se você é orientado matematicamente, pode querer ignorar as dezenas de tutoriais e aprender a definição, ou seguir as aulas de teoria das categorias :) A parte principal da definição é que um Monad M envolve um "construtor de tipo" que define para cada tipo existente "T", um novo tipo "MT" e algumas maneiras de alternar entre os tipos "regulares" e "M".]
Além disso, surpreendentemente, uma das melhores introduções às mônadas é na verdade um dos primeiros trabalhos acadêmicos que apresentam mônadas, as mônadas de Philip Wadler para programação funcional . Na verdade, ele tem exemplos motivadores práticos e não triviais , ao contrário de muitos dos tutoriais artificiais existentes.
fonte
Mônadas devem controlar o fluxo que tipos de dados abstratos são para dados.
Em outras palavras, muitos desenvolvedores estão confortáveis com a idéia de Conjuntos, Listas, Dicionários (ou Hashes ou Mapas) e Árvores. Dentro desses tipos de dados, há muitos casos especiais (por exemplo, InsertionOrderPreservingIdentityHashMap).
No entanto, quando confrontados com o "fluxo" do programa, muitos desenvolvedores não foram expostos a muito mais construções do que se, switch / case, faz, while, goto (grr) e (talvez) fechamentos.
Assim, uma mônada é simplesmente uma construção de fluxo de controle. Uma frase melhor para substituir a mônada seria 'tipo de controle'.
Como tal, uma mônada possui slots para lógica de controle, instruções ou funções - o equivalente em estruturas de dados seria dizer que algumas estruturas de dados permitem adicionar dados e removê-los.
Por exemplo, a mônada "if":
na sua forma mais simples, tem dois slots - uma cláusula e um bloco. A
if
mônada geralmente é criada para avaliar o resultado da cláusula e, se não for falsa, avaliar o bloco. Muitos desenvolvedores não são apresentados às mônadas quando aprendem 'se', e simplesmente não é necessário entender as mônadas para escrever uma lógica eficaz.As mônadas podem se tornar mais complicadas, da mesma maneira que as estruturas de dados podem se tornar mais complicadas, mas existem muitas categorias amplas de mônadas que podem ter semântica semelhante, mas implementações e sintaxe diferentes.
Obviamente, da mesma maneira que as estruturas de dados podem ser iteradas sobre, ou atravessadas, mônadas podem ser avaliadas.
Os compiladores podem ou não ter suporte para mônadas definidas pelo usuário. Haskell certamente faz. Ioke possui alguns recursos semelhantes, embora o termo mônada não seja usado no idioma.
fonte
Meu tutorial favorito do Monad:
http://www.haskell.org/haskellwiki/All_About_Monads
(de 170.000 resultados em uma pesquisa no Google por "mônada tutorial"!)
@Stu: O objetivo das mônadas é permitir que você adicione (geralmente) a semântica seqüencial ao código puro; você pode até compor mônadas (usando Monad Transformers) e obter semânticas combinadas mais interessantes e complicadas, como analisar com tratamento de erros, estado compartilhado e registro, por exemplo. Tudo isso é possível em código puro, as mônadas apenas permitem abstraí-lo e reutilizá-lo em bibliotecas modulares (sempre boas em programação), além de fornecer sintaxe conveniente para torná-lo imperativo.
O Haskell já possui sobrecarga de operador [1]: ele usa classes de tipos da mesma forma que as interfaces em Java ou C #, mas o Haskell também permite também tokens não alfanuméricos como + && e> como identificadores de infix. É apenas uma sobrecarga de operador na sua maneira de ver se você quer dizer "sobrecarregar o ponto e vírgula" [2]. Parece magia negra e pedir problemas para "sobrecarregar o ponto e vírgula" (imagine os hackers Perl se divertindo com essa idéia), mas o ponto é que sem mônadas não há ponto e vírgula, pois o código puramente funcional não exige ou permite o seqüenciamento explícito.
Tudo isso parece muito mais complicado do que precisa. O artigo de sigfpe é bem legal, mas usa Haskell para explicá-lo, o que meio que falha em quebrar o problema do ovo e da galinha de entender Haskell para agredir mônadas e entender Mônadas para agredir Haskell.
[1] Este é um problema separado das mônadas, mas as mônadas usam o recurso de sobrecarga do operador da Haskell.
[2] Isso também é uma simplificação excessiva, pois o operador para encadear ações monádicas é >> = (pronunciado "bind"), mas há açúcar sintático ("do") que permite usar chaves e ponto e vírgula e / ou recuo e novas linhas.
fonte
Ultimamente, tenho pensado em mônadas de uma maneira diferente. Eu tenho pensado neles como abstraindo a ordem de execução de uma maneira matemática, o que possibilita novos tipos de polimorfismo.
Se você estiver usando uma linguagem imperativa e escrever algumas expressões em ordem, o código SEMPRE será executado exatamente nessa ordem.
E, no caso simples, quando você usa uma mônada, a mesma sensação é: você define uma lista de expressões que ocorrem em ordem. Exceto pelo fato de que, dependendo da mônada que você usa, seu código pode ser executado em ordem (como na mônada de IO), em paralelo sobre vários itens de uma vez (como na mônada de lista), ele pode parar no meio (como na mônada Maybe) , ele pode pausar parcialmente para ser retomado mais tarde (como em uma mônada de Retoma), pode retroceder e começar do começo (como em uma mônada de Transação) ou pode retroceder parcialmente para tentar outras opções (como em uma mônada da lógica) .
E como as mônadas são polimórficas, é possível executar o mesmo código em mônadas diferentes, dependendo de suas necessidades.
Além disso, em alguns casos, é possível combinar mônadas (com transformadores de mônada) para obter vários recursos ao mesmo tempo.
fonte
Ainda sou novo em mônadas, mas pensei em compartilhar um link que achei muito bom de ler (COM FOTOS !!): http://www.matusiak.eu/numerodix/blog/2012/3/11/ mônadas-para-leigo / (sem afiliação)
Basicamente, o conceito acolhedor e difuso que obtive do artigo foi o de que as mônadas são basicamente adaptadores que permitem que funções diferentes funcionem de maneira composível, ou seja, seja capaz de agrupar várias funções e misturá-las e combiná-las sem se preocupar com retorno inconsistente tipos e tal. Portanto, a função BIND é responsável por manter maçãs com maçãs e laranjas com laranjas quando estamos tentando fazer esses adaptadores. E a função LIFT é responsável por pegar as funções de "nível inferior" e "atualizá-las" para que funcionem com as funções BIND e também sejam compostas.
Espero ter acertado e, mais importante, espero que o artigo tenha uma visão válida sobre as mônadas. Se nada mais, este artigo ajudou a estimular meu apetite por aprender mais sobre mônadas.
fonte
Além das excelentes respostas acima, deixe-me oferecer um link para o seguinte artigo (de Patrick Thomson), que explica as mônadas relacionando o conceito à biblioteca JavaScript jQuery (e sua maneira de usar o "encadeamento de métodos" para manipular o DOM) : jQuery é uma mônada
A documentação do jQuery em si não se refere ao termo "mônada", mas fala sobre o "padrão do construtor", que provavelmente é mais familiar. Isso não muda o fato de que você tem uma mônada adequada lá, talvez sem nem perceber.
fonte
Mônadas não são metáforas , mas uma abstração praticamente útil emergindo de um padrão comum, como Daniel Spiewak explica.
fonte
Uma mônada é uma maneira de combinar cálculos juntos que compartilham um contexto comum. É como construir uma rede de tubulações. Ao construir a rede, não há dados fluindo através dela. Mas, quando terminei de juntar todos os bits com 'bind' e 'return', invoco algo parecido
runMyMonad monad data
e os dados fluem através dos pipes.fonte
Na prática, o monad é uma implementação personalizada do operador de composição de funções que cuida de efeitos colaterais e valores incompatíveis de entrada e retorno (para encadeamento).
fonte
Se entendi corretamente, IEnumerable é derivado de mônadas. Gostaria de saber se isso pode ser um ângulo de abordagem interessante para nós do mundo C #.
Pelo que vale, aqui estão alguns links para tutoriais que me ajudaram (e não, ainda não entendi o que são mônadas).
fonte
As duas coisas que me ajudaram a aprender melhor foram:
Capítulo 8, "Analisadores Funcionais", do livro de Graham Hutton, Programming in Haskell . Isso não menciona mônadas, na verdade, mas se você puder ler o capítulo e realmente entender tudo nele, principalmente como uma sequência de operações de ligação é avaliada, você entenderá o interior das mônadas. Espere que isso faça várias tentativas.
O tutorial Tudo sobre mônadas . Isso fornece vários bons exemplos de uso deles, e devo dizer que a analogia no Appendex funcionou para mim.
fonte
O Monoid parece ser algo que garante que todas as operações definidas em um Monoid e em um tipo suportado sempre retornem um tipo suportado dentro do Monoid. Por exemplo, Qualquer número + Qualquer número = Um número, sem erros.
Considerando que divisão aceita dois fracionários e retorna um fracionário, que definiu divisão por zero como Infinito em haskell de alguma forma (que por acaso é um fracionário de alguma forma) ...
De qualquer forma, parece que as mônadas são apenas uma maneira de garantir que sua cadeia de operações se comporte de maneira previsível, e uma função que afirma ser Num -> Num, composta por outra função de Num-> Num chamada com x, não digamos, atire nos mísseis.
Por outro lado, se temos uma função que dispara os mísseis, podemos compor com outras funções que também disparam os mísseis, porque nossa intenção é clara - queremos disparar os mísseis - mas não tentamos imprimindo "Hello World" por algum motivo estranho.
Em Haskell, main é do tipo IO (), ou IO [()], a distiction é estranha e não discutirei, mas eis o que acho que acontece:
Se eu tiver main, quero que ele execute uma cadeia de ações, a razão pela qual eu executo o programa é produzir um efeito - geralmente através de IO. Assim, eu posso encadear operações de IO em conjunto, principalmente para - fazer IO, nada mais.
Se eu tentar fazer algo que não "retorne IO", o programa reclamará que a cadeia não flui, ou basicamente "Como isso se relaciona com o que estamos tentando fazer - uma ação de IO", parece forçar o programador manter sua linha de pensamento, sem se desviar e pensar em disparar os mísseis, enquanto cria algoritmos para classificação - o que não flui.
Basicamente, as mônadas parecem ser uma dica para o compilador de que "ei, você conhece essa função que retorna um número aqui, ela nem sempre funciona, às vezes pode produzir um número e às vezes nada, apenas mantenha isso em mente". Sabendo disso, se você tentar afirmar uma ação monádica, a ação monádica poderá funcionar como uma exceção de tempo de compilação dizendo "ei, isso não é realmente um número, isso PODE ser um número, mas você não pode assumir isso, faça algo para garantir que o fluxo seja aceitável ". o que impede o comportamento imprevisível do programa - em uma extensão justa.
Parece que as mônadas não são sobre pureza, nem controle, mas sobre a manutenção de uma identidade de uma categoria na qual todo comportamento é previsível e definido, ou não é compilado. Você não pode fazer nada quando se espera que faça algo e não pode fazer algo se se espera que não faça nada (visível).
A maior razão que eu pude pensar para o Monads é - vá para o código Procedural / OOP, e você perceberá que não sabe onde o programa começa nem termina, tudo o que você vê é muito salto e muita matemática , mágica e mísseis. Você não será capaz de mantê-lo e, se puder, gastará muito tempo pensando em todo o programa antes de entender qualquer parte dele, porque a modularidade nesse contexto é baseada em "seções" interdependentes do código, em que o código é otimizado para ser o mais relacionado possível, com a promessa de eficiência / inter-relação. As mônadas são muito concretas e bem definidas por definição, e garantem que o fluxo do programa seja possível de analisar e isolar partes difíceis de analisar - como elas próprias são mônadas. Uma mônada parece ser uma " ou destruir o universo ou até distorcer o tempo - não temos idéia nem garantia de que É O QUE É. Uma mônada garante que é o que é. o que é muito poderoso. ou destruir o universo ou até distorcer o tempo - não temos idéia nem garantia de que É O QUE É. Uma mônada garante que é o que é. o que é muito poderoso.
Todas as coisas no "mundo real" parecem ser mônadas, no sentido de que estão vinculadas por leis definidas e observáveis que impedem a confusão. Isso não significa que precisamos imitar todas as operações desse objeto para criar classes; em vez disso, podemos simplesmente dizer "um quadrado é um quadrado", nada além de um quadrado, nem mesmo um retângulo nem um círculo, e "um quadrado tem área do comprimento de uma de suas dimensões existentes multiplicado por ele mesmo.Não importa qual quadrado você tenha, se for um quadrado no espaço 2D, sua área não pode ser outra coisa senão seu comprimento ao quadrado, é quase trivial provar. não precisamos fazer afirmações para garantir que nosso mundo seja o que é, apenas usamos implicações da realidade para impedir que nossos programas caiam no rumo.
Estou praticamente garantido que estou errado, mas acho que isso poderia ajudar alguém lá fora, por isso espero que ajude alguém.
fonte
No contexto do Scala, você encontrará a definição a seguir mais simples. Basicamente, o flatMap (ou bind) é 'associativo' e existe uma identidade.
Por exemplo
NOTA Estritamente falando, a definição de uma Mônada na programação funcional não é a mesma que a definição de uma Mônada na Teoria das Categorias , que é definida nos turnos de
map
eflatten
. Embora eles sejam equivalentes sob certos mapeamentos. Esta apresentação é muito boa: http://www.slideshare.net/samthemonad/monad-presentation-scala-as-a-categoryfonte
Essa resposta começa com um exemplo motivador, funciona através do exemplo, deriva um exemplo de mônada e define formalmente "mônada".
Considere estas três funções no pseudocódigo:
f
pega um par ordenado do formulário<x, messages>
e retorna um par ordenado. Ele deixa o primeiro item intocado e anexa"called f. "
ao segundo item. O mesmo comg
.Você pode compor essas funções e obter seu valor original, juntamente com uma sequência que mostra em qual ordem as funções foram chamadas:
Você não gosta do fato de que
f
eg
são responsáveis para acrescentar suas próprias mensagens de log para as informações de registro anterior. (Imaginem por causa do argumento de que, em vez de acrescentar cordas,f
eg
deve executar lógica complicado no segundo item do par Seria uma dor de repetição que a lógica complicada em dois -. Ou mais -. Funções diferentes)Você prefere escrever funções mais simples:
Mas veja o que acontece quando você os compõe:
O problema é que passar um par para uma função não fornece o que você deseja. Mas e se você pudesse alimentar um par em uma função:
Leia
feed(f, m)
como "feedm
intof
". Para alimentar um par<x, messages>
em uma funçãof
é passarx
emf
, ficar<y, message>
fora def
, e retornar<y, messages message>
.Observe o que acontece quando você faz três coisas com suas funções:
Primeiro: se você quebrar um valor e depois alimentar o par resultante em uma função:
É o mesmo que passar o valor para a função.
Segundo: se você alimentar um par em
wrap
:Isso não muda o par.
Terceiro: se você definir uma função que recebe
x
e alimentag(x)
emf
:e alimente um par:
É o mesmo que alimentar o par
g
e alimentar o par resultantef
.Você tem mais de uma mônada. Agora você só precisa saber sobre os tipos de dados no seu programa.
Que tipo de valor é
<x, "called f. ">
? Bem, isso depende de que tipo de valorx
é. Sex
for do tipot
, então seu par é um valor do tipo "par det
e sequência". Ligue para esse tipoM t
.M
é um construtor de tipos:M
sozinho não se refere a um tipo, masM _
se refere a um tipo depois que você preencher o espaço em branco com um tipo. AnM int
é um par de um int e uma string. AnM string
é um par de uma string e uma string. Etc.Parabéns, você criou uma mônada!
Formalmente, sua mônada é a tupla
<M, feed, wrap>
.Uma mônada é uma tupla em
<M, feed, wrap>
que:M
é um construtor de tipos.feed
pega uma (função que pegat
ae retorna anM u
) e anM t
e retorna anM u
.wrap
pega umv
e retorna umM v
.t
,,u
ev
existem três tipos que podem ou não ser iguais. Uma mônada satisfaz as três propriedades que você provou para sua mônada específica:Alimentar um embrulhado
t
em uma função é o mesmo que passar o desembrulhadot
para a função.Formalmente:
feed(f, wrap(x)) = f(x)
Alimentando um
M t
emwrap
nada contribui para aM t
.Formalmente:
feed(wrap, m) = m
Alimentando um
M t
(chamem
) em uma função quet
parag
M u
(chamen
) deg
n
emf
é o mesmo que
m
emg
n
deg
n
emf
Formalmente:
feed(h, m) = feed(f, feed(g, m))
ondeh(x) := feed(f, g(x))
Normalmente,
feed
é chamadobind
(AKA>>=
em Haskell) ewrap
é chamadoreturn
.fonte
Vou tentar explicar
Monad
no contexto de Haskell.Na programação funcional, a composição da função é importante. Ele permite que nosso programa seja composto por funções pequenas e fáceis de ler.
Digamos que temos duas funções:
g :: Int -> String
ef :: String -> Bool
.Nós podemos fazer
(f . g) x
, que é exatamente o mesmo quef (g x)
, ondex
é umInt
valor.Ao fazer a composição / aplicar o resultado de uma função a outra, é importante ter os tipos correspondentes. No caso acima, o tipo do resultado retornado por
g
deve ser o mesmo que o tipo aceito porf
.Mas, às vezes, os valores estão em contextos, e isso torna um pouco menos fácil alinhar os tipos. (Ter valores em contextos é muito útil. Por exemplo, o
Maybe Int
tipo representa umInt
valor que pode não estar lá, oIO String
tipo representa umString
valor que existe como resultado da execução de alguns efeitos colaterais.)Digamos que agora temos
g1 :: Int -> Maybe String
ef1 :: String -> Maybe Bool
.g1
ef1
são muito semelhantesg
ef
respectivamente.Não podemos fazer
(f1 . g1) x
ouf1 (g1 x)
, ondex
está umInt
valor. O tipo do resultado retornado porg1
não é o quef1
espera.Poderíamos compor
f
eg
com o.
operador, mas agora não podemos comporf1
eg1
com.
. O problema é que não podemos passar diretamente um valor em um contexto para uma função que espera um valor que não esteja em um contexto.Não seria bom se apresentássemos um operador para compor
g1
ef1
, para que possamos escrever(f1 OPERATOR g1) x
?g1
retorna um valor em um contexto. O valor será retirado do contexto e aplicadof1
. E sim, temos um operador assim. É<=<
.Também temos o
>>=
operador que faz exatamente a mesma coisa para nós, embora em uma sintaxe ligeiramente diferente.Nós escrevemos:
g1 x >>= f1
.g1 x
é umMaybe Int
valor. O>>=
operador ajuda a tirar esseInt
valor do contexto "talvez não esteja lá" e aplicá-lof1
. O resultado def1
, que é aMaybe Bool
, será o resultado de toda a>>=
operação.E, finalmente, por que é
Monad
útil? Uma vez queMonad
é a classe de tipo que define o>>=
operador, muito da mesma forma como aEq
classe de tipo que define a==
e/=
operadores.Para concluir, a
Monad
classe type define o>>=
operador que nos permite passar valores em um contexto (chamamos esses valores monádicos) a funções que não esperam valores em um contexto. O contexto será tratado.Se há uma coisa a lembrar aqui, é que
Monad
permite a composição de funções que envolve valores em contextos .fonte
tl; dr
Prólogo
O operador
$
de aplicação de funçõesé definido canonicamente
em termos de aplicação da função primitiva de Haskell
f x
(infixl 10
).Composição
.
é definido em termos de$
comoe satisfaz as equivalências
forall f g h.
.
é associativo eid
é sua identidade direita e esquerda.O triplo Kleisli
Na programação, uma mônada é um construtor do tipo functor com uma instância da classe do tipo mônada. Existem várias variantes equivalentes de definição e implementação, cada uma carregando intuições ligeiramente diferentes sobre a abstração da mônada.
Um functor é um tipo de construtor
f
de tipo* -> *
com uma instância da classe de tipo de functor.Além de seguir o protocolo de tipo imposto estaticamente, as instâncias da classe de tipo functor devem obedecer às leis algébricas do functor
forall f g.
Os cálculos de functor têm o tipo
Uma computação
c r
consiste em resultadosr
dentro do contextoc
.Funções monádicas unárias ou setas Kleisli têm o tipo
As setas de Kleisi são funções que recebem um argumento
a
e retornam uma computação monádicam b
.Mônadas são canonicamente definidas em termos do triplo de Kleisli
forall m. Functor m =>
implementado como a classe de tipo
A identidade Kleisli
return
é uma flecha Kleisli que promove um valort
no contexto monádicom
. O aplicativo Extension ou Kleisli=<<
aplica uma seta Kleislia -> m b
aos resultados de uma computaçãom a
.A composição de Kleisli
<=<
é definida em termos de extensão como<=<
compõe duas setas Kleisli, aplicando a seta esquerda aos resultados da aplicação da seta direita.Instâncias da classe do tipo mônada devem obedecer às leis da mônada , mais elegantemente declaradas em termos da composição de Kleisli:
forall f g h.
<=<
é associativo ereturn
é sua identidade direita e esquerda.Identidade
O tipo de identidade
é a função de identidade nos tipos
Interpretado como um functor,
Em Haskell canônico, a mônada de identidade é definida
Opção
Um tipo de opção
codifica computação
Maybe t
que não necessariamente produz um resultadot
, computação que pode "falhar". A opção monad é definidaa -> Maybe b
é aplicado a um resultado apenas seMaybe a
produz um resultado.Os números naturais podem ser codificados como números inteiros maiores ou iguais a zero.
Os números naturais não são fechados sob subtração.
A opção monad cobre uma forma básica de tratamento de exceções.
Lista
A mônada da lista, sobre o tipo de lista
e sua operação monóide aditiva "anexa"
codifica a computação não linear
[t]
produzindo uma quantidade natural0, 1, ...
de resultadost
.A extensão
=<<
concatena++
todas as listas[b]
resultantes de aplicativosf x
de uma seta de Kleislia -> [b]
em elementos de[a]
uma única lista de resultados[b]
.Deixe os divisores apropriados de um inteiro positivo
n
serentão
Ao definir a classe do tipo mônada, em vez da extensão
=<<
, o padrão Haskell usa seu flip, o operador de ligação>>=
.Por uma questão de simplicidade, esta explicação usa a hierarquia de classes de tipo
Em Haskell, a hierarquia padrão atual é
porque não apenas toda mônada é um functor, mas todo aplicativo é um functor e toda mônada também é um aplicativo.
Usando a mônada da lista, o pseudocódigo imperativo
traduz aproximadamente para o bloco do ,
a compreensão mônada equivalente ,
e a expressão
Compreensões de notação e mônada são açúcar sintático para expressões de ligação aninhadas. O operador de ligação é usado para ligação de nome local dos resultados monádicos.
Onde
A função de guarda está definida
onde o tipo de unidade ou "tupla vazia"
Mônadas aditivas que suportam escolha e falha podem ser abstraídas usando uma classe de tipo
onde
fail
e<|>
formar um monóideforall k l m.
e
fail
é o elemento zero absorvente / aniquilador das mônadas aditivasSe em
even p
é verdade, então o guarda produz[()]
e, pela definição de>>
, a função constante localé aplicado ao resultado
()
. Se falso, o guarda produz a mônada da listafail
([]
), que não produz resultado para a aplicação de uma flecha de Kleisli>>
; portanto, issop
é pulado.Estado
Infelizmente, as mônadas são usadas para codificar a computação com estado.
Um processador de estado é uma função
que transita um estado
st
e produz um resultadot
. O estadost
pode ser qualquer coisa. Nada, bandeira, contagem, matriz, manuseio, máquina, mundo.O tipo de processadores de estado é geralmente chamado
A mônada do processador de estado é o
* -> *
functor classificadoState st
. As setas Kleisli da mônada do processador de estado são funçõesEm Haskell canônico, a versão lenta da mônada do processador de estado é definida
Um processador de estado é executado fornecendo um estado inicial:
O acesso do estado é fornecido por métodos primitivos
get
eput
de abstração sobre mônadas com estado :m -> st
declara uma dependência funcional do tipo de estadost
na mônadam
; que aState t
, por exemplo, determinará o tipo de estado comot
único.com o tipo de unidade usado analogamente
void
em C.gets
é frequentemente usado com acessadores de campo de registro.O equivalente da mônada do estado da variável threading
onde
s0 :: Int
é igualmente referencialmente transparente, mas infinitamente mais elegante e práticomodify (+ 1)
é um cálculo do tipoState Int ()
, exceto pelo efeito equivalente areturn ()
.A lei mônada da associatividade pode ser escrita em termos de
>>=
forall m f g.
ou
Como na programação orientada a expressões (por exemplo, Rust), a última declaração de um bloco representa seu rendimento. O operador de ligação às vezes é chamado de "ponto e vírgula programável".
As primitivas da estrutura de controle de iteração da programação imperativa estruturada são emuladas monadicamente
Entrada / Saída
A mônada do processador de estado mundial de E / S é uma reconciliação do puro Haskell e do mundo real, da semântica operacional denotativa e imperativa funcional. Um análogo próximo da implementação estrita real:
A interação é facilitada por primitivas impuras
A impureza do código que usa
IO
primitivas é permanentemente protocolada pelo sistema de tipos. Porque a pureza é incrível, o que aconteceIO
, permaneceIO
.Ou, pelo menos, deveria.
A assinatura de tipo de um programa Haskell
expande para
Uma função que transforma um mundo.
Epílogo
A categoria cujos objetos são do tipo Haskell e quais dos morfismos são funções entre os tipos de Haskell é, “rápido e solto”, a categoria
Hask
.Um functor
T
é um mapeamento de uma categoriaC
para uma categoriaD
; para cada objeto emC
um objeto emD
e para cada morfismo em
C
um morfismo emD
onde
X
,Y
são objetosC
.HomC(X, Y)
é a classe de homomorfismo de todos os morfismosX -> Y
emC
. O functor deve preservar a identidade e composição do morfismo, a "estrutura" deC
, emD
.A categoria Kleisli de uma categoria
C
é dada por um triplo Kleislide um endofuncor
(
f
), um morfismo de identidadeeta
(return
) e um operador de extensão*
(=<<
).Cada morfismo Kleisli em
Hask
pelo operador de extensão
recebe um morfismo na
Hask
categoria Kleisli deA composição na categoria Kleisli
.T
é dada em termos de extensãoe satisfaz os axiomas da categoria
aplicando as transformações de equivalência
em termos de extensão são canonicamente dadas
Mônadas também podem ser definidas em termos não da extensão kleisliana, mas de uma transformação natural
mu
, na programação chamadajoin
. Uma mônada é definida em termos demu
triplo sobre uma categoriaC
, de um endofuncore duas tranformações naturais
satisfazendo as equivalências
A classe do tipo mônada é então definida
A
mu
implementação canônica da opção mônada:A
concat
funçãoé o
join
da mônada da lista.As implementações de
join
podem ser traduzidas do formulário de extensão usando a equivalênciaA conversão reversa do
mu
formulário para extensão é dada porPhilip Wadler: Mônadas para programação funcional
Simon L Peyton Jones, Philip Wadler: programação funcional imperativa
Jonathan MD Hill, Keith Clarke: Uma introdução à teoria da categoria, mônadas da teoria da categoria e sua relação com a programação funcional ´
Categoria Kleisli
Eugenio Moggi: Noções de computação e mônadas
O que uma mônada não é
de Generalizar mônadas a flechas por John Hughes
fonte
O que o mundo precisa é de outro post de mônada, mas acho que isso é útil na identificação de mônadas existentes na natureza.
fonte
http://code.google.com/p/monad-tutorial/ é um trabalho em andamento para abordar exatamente essa questão.
fonte
Deixe o "
{| a |m}
" abaixo representar alguns dados monádicos. Um tipo de dados que anuncia uma
:A função,
f
sabe como criar uma mônada, se ela tivessea
:Aqui vemos a função
f
,, tenta avaliar uma mônada, mas é repreendida.Funtion,,
f
encontra uma maneira de extrair oa
usando>>=
.Pouco
f
sabe, a mônada e>>=
estão em conluio.Mas sobre o que eles realmente falam? Bem, isso depende da mônada. Falar apenas no resumo tem uso limitado; você precisa ter alguma experiência com mônadas específicas para aprofundar o entendimento.
Por exemplo, o tipo de dados Talvez
tem uma instância de mônada que age da seguinte forma ...
Onde, se for o caso
Just a
Mas para o caso de
Nothing
Portanto, a mônada Maybe permite que uma computação continue se ela realmente contiver a
a
publicidade, mas interrompe a computação se não contiver . O resultado, no entanto, ainda é um pedaço de dados monádicos, embora não seja a saída def
. Por esse motivo, a mônada Maybe é usada para representar o contexto de falha.Mônadas diferentes se comportam de maneira diferente. Listas são outros tipos de dados com instâncias monádicas. Eles se comportam da seguinte maneira:
Nesse caso, a função sabia como fazer uma lista a partir de sua entrada, mas não sabia o que fazer com entradas e listas extras. O vínculo
>>=
, ajudadof
pela combinação de várias saídas. Incluo este exemplo para mostrar que, embora>>=
seja responsável pela extraçãoa
, ele também tem acesso à eventual saída vinculada def
. De fato, ele nunca extrairá nenhum, aa
menos que saiba que a saída final tem o mesmo tipo de contexto.Existem outras mônadas usadas para representar diferentes contextos. Aqui estão algumas caracterizações de mais alguns. A
IO
mônada na verdade não tema
, mas conhece um cara e vai conseguir issoa
para você. AState st
mônada tem um estoque secreto dost
qual passará paraf
debaixo da mesa, mesmo quef
apenas tenha pedido umaa
. AReader r
mônada é semelhante aState st
, embora só permitaf
verr
.O ponto em tudo isso é que qualquer tipo de dado declarado como Mônada está declarando algum tipo de contexto para extrair um valor da Mônada. O grande ganho de tudo isso? Bem, é fácil o suficiente para calcular um cálculo com algum tipo de contexto. No entanto, pode ficar confuso ao reunir vários cálculos carregados de contexto. As operações da mônada cuidam de resolver as interações do contexto para que o programador não precise.
Observe que esse uso
>>=
facilita a bagunça, afastando parte da autonomiaf
. Ou seja, no caso acima,Nothing
por exemplo,f
não consegue mais decidir o que fazer no caso deNothing
; está codificado>>=
. Essa é a troca. Se fosse necessáriof
decidir o que fazer no caso deNothing
,f
deveria ter sido uma função deMaybe a
paraMaybe b
. Nesse caso,Maybe
ser mônada é irrelevante.Observe, no entanto, que às vezes um tipo de dados não exporta seus construtores (olhando para o IO) e, se queremos trabalhar com o valor anunciado, temos poucas opções, a não ser trabalhar com sua interface monádica.
fonte
Uma mônada é uma coisa usada para encapsular objetos que mudam de estado. É frequentemente encontrado em idiomas que, de outra forma, não permitem que você tenha um estado modificável (por exemplo, Haskell).
Um exemplo seria para E / S de arquivo.
Você seria capaz de usar uma mônada para E / S de arquivo para isolar a natureza do estado de alteração apenas no código que usava a mônada. O código dentro da Mônada pode efetivamente ignorar a mudança do estado do mundo fora da Mônada - isso facilita muito o raciocínio sobre o efeito geral do seu programa.
fonte