¿Son isomorfos?
Sí, son isomórficos en Haskell. Vea ¿Cuál es la diferencia entre Fix, Mu y Nu en el paquete de esquema de recursión de Ed Kmett para algunos comentarios adicionales?
Si es así, ¿cómo lo demuestras?
Comencemos definiendo funciones para realizar las conversiones:
muToFix :: Mu f -> Fix f
muToFix (Mu s) = s Fix
fixToMu :: Functor f => Fix f -> Mu f
fixToMu t = Mu (\alg -> cata alg t)
Para mostrar que esas funciones son testigos de un isomorfismo, debemos mostrar que:
muToFix . fixToMu = id
fixToMu . muToFix = id
Desde Fixy hacia atrás
Una de las direcciones del isomorfismo es algo más directa que la otra:
muToFix (fixToMu t) = t
muToFix (fixToMu t) -- LHS
muToFix (Mu (\f -> cata f t))
(\f -> cata f t) Fix
cata Fix t -- See below.
t -- LHS = RHS
El pasaje final anterior, cata Fix t = tse puede verificar a través de la definición de cata:
cata :: Functor f => (f a -> a) -> Fix f -> a
cata alg = alg . fmap (cata alg) . unfix
cata Fix t, entonces, es Fix (fmap (cata Fix) (unfix t)). Podemos usar la inducción para mostrar que debe ser t, al menos para un finito t(se vuelve más sutil con estructuras infinitas; vea el apéndice al final de esta respuesta). Hay dos posibilidades a considerar:
unfix t :: f (Fix f)está vacío, no tiene posiciones recursivas para cavar. En ese caso, debe ser igual fmap absurd zpara algunos z :: f Void, y por lo tanto:
cata Fix t
Fix (fmap (cata Fix) (unfix t))
Fix (fmap (cata Fix) (fmap absurd z))
Fix (fmap (cata Fix . absurd) z)
-- fmap doesn't do anything on an empty structure.
Fix (fmap absurd z)
Fix (unfix t)
t
unfix tno está vacío. En ese caso, al menos sabemos que fmap (cata Fix)no puede hacer nada más que aplicar cata Fixen las posiciones recursivas. La hipótesis de inducción aquí es que hacerlo dejará esas posiciones sin cambios. Entonces tenemos:
cata Fix t
Fix (fmap (cata Fix) (unfix t))
Fix (unfix t) -- Induction hypothesis.
t
(En última instancia, cata Fix = ides un corolario de Fix :: f (Fix f) -> Fix xser un álgebra F inicial. Recurrir directamente a ese hecho en el contexto de esta prueba probablemente sería demasiado atajo).
Desde Muy hacia atrás
Dado muToFix . fixToMu = id, para demostrar que fixToMu . muToFix = ides suficiente probar:
Tomemos la segunda opción y revisemos las definiciones relevantes:
newtype Mu f = Mu (forall a. (f a -> a) -> a)
fixToMu :: Functor f => Fix f -> Mu f
fixToMu t = Mu (\alg -> cata alg t)
fixToMuser surjective, entonces, significa que, dado cualquier específico Functor f, todas las funciones de tipo forall a. (f a -> a) -> apueden definirse como \alg -> cata alg t, para algunos específicos t :: Fix f. La tarea, entonces, se convierte en catalogar las forall a. (f a -> a) -> afunciones y ver si todas ellas pueden expresarse de esa forma.
¿Cómo podríamos definir una forall a. (f a -> a) -> afunción sin apoyarnos fixToMu? No importa qué, debe implicar el uso del f a -> aálgebra suministrada como argumento para obtener un aresultado. La ruta directa lo estaría aplicando a algún f avalor. Una advertencia importante es que, dado que aes polimórfico, debemos ser capaces de conjurar dicho f avalor para cualquier elección de a. Esa es una estrategia factible siempre fque existan valores. En ese caso, podemos hacer:
fromEmpty :: Functor f => f Void -> forall a. (f a -> a) -> a
fromEmpty z = \alg -> alg (fmap absurd z)
Para aclarar la notación, definamos un tipo de cosas que podemos usar para definir forall a. (f a -> a) -> afunciones:
data Moo f = Empty (f Void)
fromMoo :: Functor f => Moo f -> forall a. (f a -> a) -> a
fromMoo (Empty z) = \alg -> alg (fmap absurd z)
Además de la ruta directa, solo hay otra posibilidad. Dado que fes un Functor, si de alguna manera tenemos un f (Moo f)valor, podemos aplicar el álgebra dos veces, la primera aplicación está debajo de la fcapa externa , vía fmapy fromMoo:
fromLayered :: Functor f => f (Moo f) -> forall a. (f a -> a) -> a
fromLayered u = \alg -> alg (fmap (\moo -> fromMoo moo alg) u)
Teniendo en cuenta que también podemos hacer forall a. (f a -> a) -> ade f (Moo f)los valores, que tiene sentido para agregarlos como un caso de Moo:
data Moo f = Empty (f Void) | Layered (f (Moo f))
En consecuencia, fromLayeredse puede incorporar a fromMoo:
fromMoo :: Functor f => Moo f -> forall a. (f a -> a) -> a
fromMoo = \case
Empty z -> \alg -> alg (fmap absurd z)
Layered u -> \alg -> alg (fmap (\moo -> fromMoo moo alg) u)
Tenga en cuenta que, al hacerlo, hemos pasado furtivamente de la aplicación algbajo una fcapa a la aplicación recursiva algbajo un número arbitrario de fcapas.
A continuación, podemos observar f Voidque se puede inyectar un valor en el Layeredconstructor:
emptyLayered :: Functor f => f Void -> Moo f
emptyLayered z = Layered (fmap absurd z)
Eso significa que en realidad no necesitamos el Emptyconstructor:
newtype Moo f = Moo (f (Moo f))
unMoo :: Moo f -> f (Moo f)
unMoo (Moo u) = u
¿Qué pasa con el Emptycaso fromMoo? La única diferencia entre los dos casos es que, en el Emptycaso, tenemos en absurdlugar de \moo -> fromMoo moo alg. Como todas las Void -> afunciones son absurd, tampoco necesitamos un Emptycaso separado :
fromMoo :: Functor f => Moo f -> forall a. (f a -> a) -> a
fromMoo (Moo u) = \alg -> alg (fmap (\moo -> fromMoo moo alg) u)
Un posible ajuste cosmético es voltear los fromMooargumentos, por lo que no necesitamos escribir el argumento fmapcomo lambda:
foldMoo :: Functor f => (f a -> a) -> Moo f -> a
foldMoo alg (Moo u) = alg (fmap (foldMoo alg) u)
O, más libre de puntos:
foldMoo :: Functor f => (f a -> a) -> Moo f -> a
foldMoo alg = alg . fmap (foldMoo alg) . unMoo
En este punto, una segunda mirada a nuestras definiciones sugiere que es necesario cambiar el nombre:
newtype Fix f = Fix (f (Fix f))
unfix :: Fix f -> f (Fix f)
unfix (Fix u) = u
cata :: Functor f => (f a -> a) -> Fix f -> a
cata alg = alg . fmap (cata alg) . unfix
fromFix :: Functor f => Fix f -> forall a. (f a -> a) -> a
fromFix t = \alg -> cata alg t
Y ahí está: todas las forall a. (f a -> a) -> afunciones tienen la forma \alg -> cata alg tpara algunos t :: Fix f. Por lo tanto, fixToMues surjective, y tenemos el isomorfismo deseado.
Apéndice
En los comentarios, se planteó una pregunta pertinente sobre la aplicabilidad del argumento de inducción en la cata Fix t = tderivación. Como mínimo, las leyes fundadoras y la paramétrica aseguran que fmap (cata Fix)no creará trabajo adicional (por ejemplo, no aumentará la estructura ni introducirá posiciones recursivas adicionales para profundizar), lo que justifica por qué entrar en las posiciones recursivas es todo lo que importa en el paso inductivo de la derivación. Siendo así, si se ttrata de una estructura finita, el caso base de un vacío f (Fix t)finalmente se alcanzará, y todo está claro. tSin embargo, si permitimos ser infinitos, podemos seguir descendiendo sin cesar, fmapdespués fmapdespués fmap, sin llegar nunca al caso base.
Sin embargo, la situación con estructuras infinitas no es tan horrible como podría parecer al principio. La pereza, que es lo que hace viables las estructuras infinitas en primer lugar, nos permite consumir estructuras infinitas perezosamente:
GHCi> :info ListF
data ListF a b = Nil | Cons a b
-- etc.
GHCi> ones = Fix (Cons 1 ones)
GHCi> (\(Fix (Cons a _)) -> a) (cata Fix ones)
1
GHCi> (\(Fix (Cons _ (Fix (Cons a _)))) -> a) (cata Fix ones)
1
Si bien la sucesión de posiciones recursivas se extiende infinitamente, podemos detenernos en cualquier punto y obtener resultados útiles de los ListFcontextos funerarios circundantes . Tales contextos, vale la pena repetir, no se ven afectados fmap, por lo que cualquier segmento finito de la estructura que podamos consumir no se verá afectado cata Fix.
Este aplazamiento refleja cómo la pereza, como se menciona en esta discusión otra parte, la pereza colapsa la distinción entre los puntos fijos Mu, Fixy Nu. Sin holgazanería, Fixno es suficiente para codificar una curión productiva y, por lo tanto, tenemos que cambiar al Numayor punto fijo. Aquí hay una pequeña demostración de la diferencia:
GHCi> :set -XBangPatterns
GHCi> -- Like ListF, but strict in the recursive position.
GHCi> data SListF a b = SNil | SCons a !b deriving Functor
GHCi> ones = Nu (\() -> SCons 1 ()) ()
GHCi> (\(Nu c a) -> (\(SCons a _) -> a) (c a)) ones
1
GHCi> ones' = Fix (SCons 1 ones')
GHCi> (\(Fix (SCons a _)) -> a) ones'
^CInterrupted.