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ónadae u
, es decir, usar alguna expresión del formulariou *> ... <* u
como usted describió? Por qué no podemos encontrar alguna otra combinación inteligente y complicado defmap
,return
yjoin
, de modo que consigamos algo más? También es un buen movimiento considerar los morfismos aplicativos. Puede ser más fácil demostrar la propiedad análoga para los morfismos aplicativos que para los morfismos mónada. (¿Los únicos morfismos aplicativos que son aplicativamente naturales son los morfismos de identidad?)join _
puede conducir a un noid
resultado, y el # 3 es la única forma en que no conducirá aid
una regresión infinita. (2) EncendidoApplicative
: informalmente, si su única flecha de Kleisli esreturn
, no está utilizando la potencia extra queMonad
trae, por lo que también podría trabajarApplicative
. (3) Sí, la propiedad análoga se cumple para los morfismos aplicativos. La parte de mi argumento que comienza con la forma canónica, que es autónoma, debería ser suficiente como prueba.