Saltar al contenido

¿Cuál es la relación entre unir y unir?

Te recomendamos que revises esta respuesta en un ambiente controlado antes de enviarlo a producción, saludos.

Solución:

Una mónada se puede definir en términos de:

  • return :: a -> m a
  • bind :: m a -> (a -> m b) -> m b

o alternativamente en términos de:

  • return :: a -> m a
  • fmap :: (a -> b) -> m a -> m b
  • join :: m (m a) -> m a

A tus preguntas:

  1. No, no podemos definir fmap en términos de join, ya que de lo contrario podríamos eliminar fmap de la segunda lista anterior.
  2. No, “cada mónada es un funtor” es una declaración sobre las mónadas en general, independientemente de si define su mónada específica en términos de bind o en términos de join y fmap. Es más fácil entender la declaración si ve la segunda definición, pero eso es todo.
  3. Sí, bind es más “poderoso” que join. Es exactamente tan “poderoso” como join y fmap combinado, si quiere decir con “poderoso” que tiene la capacidad de definir una mónada (siempre en combinación con return).

Para una intuición, vea, por ejemplo, esta respuesta: bind le permite combinar o encadenar estrategias / planes / cálculos (que están en un contexto) juntos. Como ejemplo, usemos el Maybe contexto (o Maybe monada):

λ: let plusOne x = Just (x + 1)
λ: Just 3 >>= plusOne
Just 4

fmap también le permite encadenar los cálculos en un contexto juntos, pero a costa de aumentar el anidamiento con cada paso.[1]

λ: fmap plusOne (Just 3)
Just (Just 4)

Es por eso que necesitas join: aplastar dos niveles de anidación en uno. Recordar:

join :: m (m a) -> m a

Tener solo el paso de aplastamiento no te lleva muy lejos. También necesitas fmap tener una mónada – y return, cual es Just en el ejemplo anterior.

[1]: fmap y (>>=) no tome sus dos argumentos en el mismo orden, pero no deje que eso lo confunda.

Hay un [definition] de fmap en términos de join?

No, no lo hay. Eso se puede demostrar al intentar hacerlo. Supongamos que se nos da un constructor de tipo arbitrario T, y funciones:

returnT :: a -> T a
joinT :: T (T a) -> T a

Solo a partir de estos datos, queremos definir:

fmapT :: (a -> b) -> T a -> T b

Así que dibujémoslo:

fmapT :: (a -> b) -> T a -> T b
fmapT f ta = tb
    where
    tb = undefined  -- tb :: T b

Necesitamos obtener un valor de tipo T b de alguna manera. ta :: T a por sí solo no funcionará, por lo que necesitamos funciones que produzcan T b valores. Los únicos dos candidatos son joinT y returnT. joinT no ayuda:

fmapT :: (a -> b) -> T a -> T b
fmapT f ta = joinT ttb
    where
    ttb = undefined  -- ttb :: T (T b)

Simplemente patea la lata por el camino, como si necesitara un T (T b) El valor en estas circunstancias no mejora.

Podríamos intentar returnT en lugar de:

fmapT :: (a -> b) -> T a -> T b
fmapT f ta = returnT b
    where
    b = undefined  -- b :: b

Ahora necesitamos un b valor. Lo único que puede darnos uno es f:

fmapT :: (a -> b) -> T a -> T b
fmapT f ta = returnT (f a)
    where
    a = undefined  -- a :: a

Y ahora estamos estancados: nada puede darnos un a. Hemos agotado todas las posibilidades disponibles, por lo que fmapT no se puede definir en tales términos.

Una digresión: no sería suficiente hacer trampa usando una función como esta:

extractT :: T a -> a

Con un extractT, podríamos intentar a = extractT ta, llevando a:

fmapT :: (a -> b) -> T a -> T b
fmapT f ta = returnT (f (extractT ta))

Sin embargo, no es suficiente para fmapT para tener el tipo correcto: también debe seguir las leyes de functor. En particular, fmapT id = id debe aguantar. Con esta definición, fmapT id es returnT . extractT, que, en general, no es id (la mayoría de los functores que son instancias de ambos Monad y Comonad sirven como ejemplos).


¿Es “cada mónada es un funtor” una conclusión cuando se usa bind (en la definición de una mónada), pero una suposición al usar join?

“Toda mónada es un funtor” es una suposición o, más precisamente, parte de la definición de mónada. Para elegir una ilustración arbitraria, aquí está Emily Riehl, Teoría de categorías en contexto, pag. 154:

Definición 5.1.1. A monada en una categoría C consta de

  • un endofunctor T : C → C,

  • a unidad transformación natural η : 1CT, y

  • a multiplicación transformación natural μ :T2T,

para que los siguientes diagramas conmuten en CC: [diagrams of the monad laws]

Por tanto, una mónada implica un endofunctor por definición. Para un constructor de tipo Haskell T que instancia Monad, el mapeo de objetos de ese endofunctor es T sí mismo, y el mapeo del morfismo es su fmap. Ese T será un Functor instancia, y por lo tanto tendrá una fmap, está, en Haskell contemporáneo, garantizado por Applicative (y, por extensión, Functor) siendo una superclase de Monad.

Sin embargo, ¿es esa toda la historia? En lo que respecta a Haskell. lo sabemos liftM existe, y también que en un pasado no muy lejano Functor no era una superclase de Monad. ¿Son esos dos hechos meros haskellismos? No exactamente. En el papel clásico Nociones de computación y mónadasEugenio Moggi desentierra la siguiente definición (p. 3):

Definición 1.2 ([Man76]) A Kleisli triple sobre una categoría C es un triple (T, η, _ *), dónde T : Obj (C) → Obj (C), ηA : A → TA por A ∈ Obj (C), f *: TA → TB por f: A → TB y se cumplen las siguientes ecuaciones:

  • ηA* = idejército de reserva
  • ηA; f * = f por f: A → TB
  • F*; g * = (f; g *) * por f: A → TB y g: B → TC

El detalle importante aquí es que T se presenta como un mero mapeo de objetos en la categoría C, y no como endofunctor en C. Trabajando en el Hask categoría, que equivale a tomar un constructor de tipos T sin presuponer que es un Functor ejemplo. En código, podríamos escribir eso como:

class KleisliTriple t where
    return :: a -> t a
    (=<<) :: (a -> t b) -> t a -> t b

-- (return =<<) = id
-- (f =<<) . return = f
-- (g =<<) . (f =<<) = ((g =<<) . f =<<)

Dejando de lado el enlace, esa es la definición de pre-AMP de Monad en Haskell. Como era de esperar, el artículo de Moggi no tarda mucho en mostrar que "existe una correspondencia uno a uno entre las triples y las mónadas de Kleisli" (p. 5), estableciendo a lo largo del camino que T puede extenderse a un endofunctor (en Haskell, ese paso equivale a definir el mapeo de morfismo liftM f m = return . f =<< m, y luego mostrarlo sigue las leyes de functor).

Con todo, si escribe definiciones legales de return y (>>=) sin presuponer fmap, de hecho obtiene una implementación legal de Functor Como consecuencia. "Existe una correspondencia biunívoca entre las tripletas y las mónadas de Kleisli" es una consecuencia de la definición de triple de Kleisli, mientras que "una mónada implica un endofunctor" es parte de la definición de mónada. Es tentador considerar si sería más exacto describir lo que hizo Haskellers al escribir Monad casos como "establecer un triple de Klesili" en lugar de "establecer una mónada", pero me abstendré por temor a quedar atrapado en la pedantería terminológica, y en cualquier caso, ahora que Functores una superclase de Monad no hay ninguna razón práctica para preocuparse por eso.


Es bind más "poderoso" que join? ¿Y qué significaría "más poderoso"?

¡Pregunta capciosa!

Tomada al pie de la letra, la respuesta sería sí, en la medida en que, junto con return, (>>=) hace posible implementar fmap (vía liftM, como se señaló anteriormente), mientras que join no lo hace. Sin embargo, no creo que valga la pena insistir en esta distinción. ¿Porque? Por las leyes de las mónadas. Al igual que no tiene sentido hablar de un legal(>>=) sin presuponer return, no tiene sentido hablar de un join sin presionar returnyfmap.

Uno podría tener la impresión de que estoy dando demasiado peso a las leyes al usarlas para atar Monad y Functor De este modo. Está true que hay casos de leyes que involucran a dos clases, y que solo se aplican a tipos que las instancian a ambas. Foldable proporciona un buen ejemplo de eso: podemos encontrar la siguiente ley en el Traversable documentación:

Las instancias de superclase deben satisfacer lo siguiente: [...]

En el Foldable ejemplo, foldMap debe ser equivalente a transversal con un functor aplicativo constante (foldMapDefault).

Que esta ley específica no siempre se aplique no es un problema, porque no la necesitamos para caracterizar lo que Foldable es (las alternativas incluyen "a Foldable es un contenedor del que podemos extraer una secuencia de elementos ", y" un Foldable es un contenedor que se puede convertir al monoide libre en su tipo de elemento "). Sin embargo, con las leyes de la mónada, no es así: el significado de la clase está indisolublemente ligado a las tres leyes de la mónada.

Valoraciones y reseñas

Si sostienes algún recelo o capacidad de medrar nuestro enunciado eres capaz de añadir una acotación y con placer lo leeremos.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *