Os candidatos compõem, as mônadas não.
O que significa a afirmação acima? E quando um é preferível ao outro?
haskell
functional-programming
monads
monad-transformers
applicative
faltando faktor
fonte
fonte
Applicative
s são, na verdade, uma família inteira deMonad
s, a saber, um para cada "forma" de estrutura possível.ZipList
não é aMonad
, masZipList
s de comprimento fixo são.Reader
é um caso especial conveniente (ou geral?) em que o tamanho da "estrutura" é fixado como a cardinalidade do tipo de ambiente.Reader
mônada até o isomorfismo. Depois de fixar a forma de um contêiner, ele codifica efetivamente uma função a partir de posições, como um memorando. Peter Hancock chama esses functores de "Naperian", pois eles obedecem às leis dos logaritmos.Respostas:
Se compararmos os tipos
temos uma pista do que separa os dois conceitos. Isso
(s -> m t)
no tipo de(>>=)
mostra que um valor ems
pode determinar o comportamento de um cálculo emm t
. Mônadas permitem interferência entre as camadas de valor e computação. O(<*>)
operador não permite essa interferência: os cálculos da função e do argumento não dependem de valores. Isso realmente incomoda. Compararque usa o resultado de algum efeito para decidir entre dois cálculos (por exemplo, lançar mísseis e assinar um armistício), enquanto
que usa o valor de
ab
para escolher entre os valores de dois cálculosat
eaf
, tendo realizado ambos, talvez com efeito trágico.A versão monádica depende essencialmente do poder extra de
(>>=)
escolher um cálculo a partir de um valor, e isso pode ser importante. No entanto, apoiar esse poder torna as mônadas difíceis de compor. Se tentarmos construir 'double-bind'chegamos até aqui, mas agora nossas camadas estão todas misturadas. Temos um
n (m (n t))
, então precisamos nos livrar do exteriorn
. Como diz Alexandre C, podemos fazer isso se tivermos um adequadopermutar o
n
interior ejoin
o outron
.O mais fraco 'dupla aplicação' é muito mais fácil de definir
porque não há interferência entre as camadas.
Da mesma forma, é bom reconhecer quando você realmente precisa da potência extra de
Monad
s e quando pode se safar com a estrutura de computação rígida que oApplicative
suporta.Observe, a propósito, que embora compor mônadas seja difícil, pode ser mais do que você precisa. O tipo
m (n v)
indica computação comm
-effects e, em seguida, computação comn
-effects para um -valuev
, em quem
-effects termina antes den
-effects iniciar (daí a necessidade deswap
). Se você deseja apenas intercalarm
-effects comn
-effects, então composição talvez seja pedir demais!fonte
m
en
você sempre pode escrever um transformador de mônadamt
e operarn (m t)
usandomt n t
? Então você sempre pode compor mônadas, só é mais complicado, usando transformadores?data Free f x = Ret x | Do (f (Free f x))
, entãodata (:+:) f g x = Inl (f x) | Tnr (g x)
, e considereFree (m :+: n)
. Isso atrasa a escolha de como executar as intercalações.Maybe
isso significa que um inícioNothing
suprimirá a avaliaçãoa
de um posterior / subsequenteJust a
. Isso está correto?Mônadas fazer compor, mas o resultado pode não ser uma mônada. Em contraste, a composição de dois aplicativos é necessariamente um aplicativo. Suspeito que a intenção da declaração original era que "A aplicatividade compõe, enquanto a monadilidade não". Reformulado, "
Applicative
está fechado na composição, eMonad
não está."fonte
Se você tiver aplicativos
A1
eA2
, o tipodata A3 a = A3 (A1 (A2 a))
também será aplicativo (você pode escrever uma instância desse tipo de forma genérica).Por outro lado, se você tiver mônadas
M1
eM2
o tipodata M3 a = M3 (M1 (M2 a))
não for necessariamente uma mônada (não há implementação genérica sensata para>>=
oujoin
para a composição).Um exemplo pode ser o tipo
[Int -> a]
(aqui compomos um construtor de tipo[]
com(->) Int
, ambos os quais são mônadas). Você pode escrever facilmenteE isso se generaliza para qualquer aplicativo:
Mas não há uma definição sensata de
Se você não está convencido disso, considere esta expressão:
O comprimento da lista retornada deve ser definido em pedra antes que um inteiro seja fornecido, mas o comprimento correto dela depende do inteiro fornecido. Portanto, nenhuma
join
função correta pode existir para este tipo.fonte
IO
sem umMonad
seria muito difícil de programar. :)Compondo mônadas, http://web.cecs.pdx.edu/~mpj/pubs/RR-1004.pdf
fonte
swap : N M a -> M N a
ContT r m a
não ém (Cont r a)
nemCont r (m a)
, eStateT s m a
é aproximadamenteReader s (m (Writer s a))
.swap
implica que a composição permite que os dois "cooperem" de alguma forma. Além disso, observe quesequence
é um caso especial de "troca" para algumas mônadas. Então éflip
, na verdade.swap :: N (M x) -> M (N x)
, parece-me que você pode usarreturns
(adequadamentefmap
ped) para inserir umM
na frente e umN
atrás, partindo de eN (M x) -> M (N (M (N x)))
, em seguida, usar ojoin
do composto para obter o seuM (N x)
.A solução de lei distributiva l: MN -> NM é suficiente
para garantir a monadicidade do NM. Para ver isso você precisa de uma unidade e um mult. Vou me concentrar no mult (a unidade é unit_N unitM)
Isso não garante que o MN seja uma mônada.
A observação crucial, no entanto, entra em jogo quando você tem soluções de direito distributivo
assim, LM, LN e MN são mônadas. A questão que surge é se LMN é uma mônada (seja por
(MN) L -> L (MN) ou por N (LM) -> (LM) N
Temos estrutura suficiente para fazer esses mapas. No entanto, como Eugenia Cheng observa , precisamos de uma condição hexagonal (que equivale a uma apresentação da equação de Yang-Baxter) para garantir a monadicidade de qualquer construção. Na verdade, com a condição hexagonal, as duas mônadas diferentes coincidem.
fonte