Eu acho que você já esgotou todas as possibilidades interessantes. Qualquer Monad m => m a -> m a
função que possamos definir será inevitavelmente assim:
e :: forall m a. Monad m => m a -> m a
e u = u >>= k
where
k :: a -> m a
k = _
Em particular, se k = return
, e = id
. Para e
não ser id
, k
deve usar de u
maneira não trivial (por exemplo, k = const u
e k = flip fmap u . const
corresponder a suas duas tentativas). Nesse caso, porém, os u
efeitos serão duplicados, levando e
a deixar de ser um morfismo da mônada para várias opções de mônada m
. Sendo assim, o único morfismo da mônada totalmente polimórfico na mônada é id
.
Vamos tornar o argumento mais explícito.
Por uma questão de clareza, mudarei para a apresentação join
/ return
/ fmap
por um momento. Queremos implementar:
e :: forall m a. Monad m => m a -> m a
e u = _
Com o que podemos preencher o lado direito? A escolha mais óbvia é u
. Por si só, isso significa e = id
, o que não parece interessante. No entanto, como também temos join
, return
e fmap
, existe a opção de raciocinar indutivamente, tendo u
como caso base. Digamos que temos alguns v :: m a
, construídos usando os meios que temos em mãos. Além de v
si, temos as seguintes possibilidades:
join (return v)
, que é v
e, portanto, não nos diz nada de novo;
join (fmap return v)
, o que também é v
; e
join (fmap (\x -> fmap (f x) w) v)
, para alguns outros w :: m a
criados de acordo com nossas regras, e alguns f :: a -> a -> a
. (Adicionar m
camadas ao tipo de f
, como em a -> a -> m a
e join
s extras para removê-las não levaria a lugar algum, pois teríamos que mostrar a procedência dessas camadas e as coisas acabariam se reduzindo para os outros casos.)
O único caso interessante é o nº 3. Neste ponto, vou pegar um atalho:
join (fmap (\x -> fmap (f x) w) v)
= v >>= \x -> fmap (f x) w
= f <$> v <*> w
Qualquer u
lado não- direito, portanto, pode ser expresso na forma f <$> v <*> w
, com v
e w
sendo uma u
ou mais iterações desse padrão, chegando finalmente a u
s nas folhas. Expressões aplicativas desse tipo, no entanto, têm uma forma canônica, obtida usando as leis aplicáveis para reassociar todos os usos da (<*>)
esquerda, que neste caso devem ser assim ...
c <$> u <*> ... <*> u
... com as reticências representando zero ou mais ocorrências adicionais de u
separadas por <*>
e c
sendo uma a -> ... -> a -> a
função da aridade apropriada. Como a
é totalmente polimórfico, c
deve, por parametridade, ser uma const
função semelhante a que escolhe um de seus argumentos. Sendo assim, qualquer expressão pode ser reescrita em termos de (<*)
e (*>)
...
u *> ... <* u
... com as reticências representando zero ou mais ocorrências adicionais de u
separadas por um *>
ou outro <*
, não havendo *>
o direito de a <*
.
Voltando ao início, todas as id
implementações não candidatas devem ter a seguinte aparência:
e u = u *> ... <* u
Também queremos e
ser um morfismo de mônada. Como conseqüência, também deve ser um morfismo aplicado. Em particular:
-- (*>) = (>>) = \u v -> u >>= \_ -> v
e (u *> v) = e u *> e v
Isso é:
(u *> v) *> ... <* (u >* v) = (u *> ... <* u) *> (v *> ... <* v)
Agora temos um caminho claro para um contra-exemplo. Se usarmos as leis aplicativas para converter ambos os lados para a forma canônica, vamos (ainda) acabar com intercalados u
s e v
s no lado esquerdo, e com todos v
s após todos os u
s no lado direito. Isso significa que a propriedade não se aplica a mônadas como IO
, State
ou Writer
, independentemente de quantas (*>)
e (<*)
existem e
, ou exatamente quais valores são escolhidos pelas const
funções semelhantes de ambos os lados. Uma demonstração rápida:
GHCi> e u = u *> u <* u -- Canonical form: const const <$> u <*> u <*> u
GHCi> e (print 1 *> print 2)
1
2
1
2
1
2
GHCi> e (print 1) *> e (print 2)
1
1
1
2
2
2
u
serão necessariamente duplicados, a menos quee = id
? (Também poderíamos escrevere u = do _ <- u; _ <- u; _ <- u; u
e combinar outrosu
efeitos.) Como podemos descrever matematicamente que "um valor monádicop :: m a
tem vários efeitos copiadosu :: m a
? E então, como podemos provar que os efeitos duplicados (triplicados, etc.)u
levam necessariamente a violações de Mônada leis morfismo?e u
, ou seja, usar alguma expressão do formuláriou *> ... <* u
como você descreveu? Por que não podemos encontrar alguma outra combinação inteligente e complicado defmap
,return
ejoin
, de modo que temos algo mais? Também é uma boa jogada considerar morfismos aplicativos. Pode ser mais fácil provar a propriedade análoga para morfismos aplicativos do que para morfismos de mônada. (Os morphisms única aplicativas que são applicatively natural são morphisms identidade?)join _
pode levar a um nãoid
resultado, e o nº 3 é a única maneira que não levará aid
uma regressão infinita. (2) AtivadoApplicative
: informalmente, se a sua única flecha Kleisli éreturn
, você não está usando a energia extra queMonad
traz, por isso também pode trabalharApplicative
. (3) Sim, a propriedade análoga vale para morfismos aplicativos. A parte do meu argumento que começa com a forma canônica, que é independente, deve ser suficiente como prova.