Creo que ya has agotado todas las posibilidades interesantes. Cualquier Monad m => m a -> m a
función que podamos definir se verá inevitablemente así:
e :: forall m a. Monad m => m a -> m a
e u = u >>= k
where
k :: a -> m a
k = _
En particular, si k = return
, e = id
. Para e
no serlo id
, k
debe usarlo u
de una manera no trivial (por ejemplo, k = const u
y k = flip fmap u . const
equivaldrá a sus dos intentos). Sin embargo, en tal caso, los u
efectos se duplicarán, lo e
que conducirá a no ser un morfismo de mónada para una serie de opciones de mónada m
. Siendo así, el único morfismo de la mónada es totalmente polimórfico en la mónada id
.
Hagamos el argumento más explícito.
En aras de la claridad, cambiaré a la join
/ return
/ fmap
presentación por un momento. Queremos implementar:
e :: forall m a. Monad m => m a -> m a
e u = _
¿Con qué podemos llenar el lado derecho? La elección más obvia es u
. Por sí mismo, eso significa e = id
que no parece interesante. Sin embargo, dado que también tenemos join
, return
y fmap
, existe la opción de razonar inductivamente, con u
el caso base. Digamos que tenemos algunos v :: m a
, construidos utilizando los medios que tenemos a mano. Además de v
sí mismo, tenemos las siguientes posibilidades:
join (return v)
, que es v
y por lo tanto no nos dice nada nuevo;
join (fmap return v)
, que también lo es v
; y
join (fmap (\x -> fmap (f x) w) v)
, para otro w :: m a
construido de acuerdo con nuestras reglas, y algunos f :: a -> a -> a
. (Agregar m
capas al tipo de f
, como en a -> a -> m a
, y join
s extra para eliminarlas no llevaría a ninguna parte, ya que entonces tendríamos que mostrar la procedencia de esas capas, y las cosas finalmente se reducirían a los otros casos).
El único caso interesante es el # 3. En este punto, tomaré un atajo:
join (fmap (\x -> fmap (f x) w) v)
= v >>= \x -> fmap (f x) w
= f <$> v <*> w
Cualquier u
lado no derecho, por lo tanto, puede expresarse en la forma f <$> v <*> w
, con v
y w
siendo una u
o más iteraciones de este patrón, llegando eventualmente a u
s en las hojas. Sin embargo, las expresiones de aplicación de este tipo tienen una forma canónica, obtenida mediante el uso de las leyes de aplicación para reasociar todos los usos de (<*>)
la izquierda, que en este caso debe verse así ...
c <$> u <*> ... <*> u
... con los puntos suspensivos representando cero o más ocurrencias adicionales de u
separados por <*>
, y c
en a -> ... -> a -> a
función de la aridad apropiada. Como a
es completamente polimórfico, c
debe, por paramétrica, ser una const
función similar que elija uno de sus argumentos. Siendo así, cualquier expresión de este tipo puede reescribirse en términos de (<*)
y (*>)
...
u *> ... <* u
... con los puntos suspensivos representando cero o más ocurrencias adicionales de u
separados por uno *>
o por otro <*
, no existiendo *>
a la derecha de a <*
.
Volviendo al inicio, todas las id
implementaciones no candidatas deben tener este aspecto:
e u = u *> ... <* u
También queremos e
ser un morfismo mónada. Como consecuencia, también debe ser un morfismo aplicativo. En particular:
-- (*>) = (>>) = \u v -> u >>= \_ -> v
e (u *> v) = e u *> e v
Es decir:
(u *> v) *> ... <* (u >* v) = (u *> ... <* u) *> (v *> ... <* v)
Ahora tenemos un camino claro hacia un contraejemplo. Si usamos las leyes aplicables para convertir ambos lados a la forma canónica, (todavía) terminaremos con u
sy s intercaladas v
en el lado izquierdo, y con todas las v
s después de todas las u
s en el lado derecho. Eso significa que la propiedad no se mantendrá para mónadas como IO
, State
o Writer
, independientemente de cuántos (*>)
y (<*)
hay e
, o exactamente qué valores son elegidos por las const
funciones similares en ambos lados. Una demostración 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
se duplicarán necesariamente a menos quee = id
? (También podríamos escribire u = do _ <- u; _ <- u; _ <- u; u
y combinar másu
efectos). ¿Cómo podemos describir matemáticamente que "un valor monádicop :: m a
tiene múltiples efectos copiadosu :: m a
? Y luego, ¿cómo podemos demostrar que los efectos duplicados (triplicados, etc.)u
necesariamente conducen a violaciones de leyes de morfismo de mónada