¿Buenos ejemplos de No es un Functor/Functor/Aplicativo/Mónada?
Mientras le explico a alguien qué es una clase de tipo X, me cuesta encontrar buenos ejemplos de estructuras de datos que sean exactamente X.
Entonces, solicito ejemplos para:
- Un constructor de tipos que no es un Functor.
- Un constructor de tipos que es un Functor, pero no un Aplicativo.
- Un constructor de tipos que es un aplicativo, pero no una mónada.
- Un constructor de tipos que es una mónada.
Creo que hay muchos ejemplos de Monad en todas partes, pero un buen ejemplo de Monad con alguna relación con ejemplos anteriores podría completar el cuadro.
Busco ejemplos que sean similares entre sí, diferenciándose sólo en aspectos importantes para pertenecer a la clase de tipo particular.
Si uno pudiera lograr encontrar un ejemplo de Arrow en algún lugar de esta jerarquía (¿está entre Applicative y Monad?), ¡eso también sería genial!
Un constructor de tipos que no es un Functor:
newtype T a = T (a -> Int)
Puedes crear un funtor contravariante a partir de él, pero no un funtor (covariante). Intenta escribir fmap
y fracasarás. Tenga en cuenta que la versión del functor contravariante está invertida:
fmap :: Functor f => (a -> b) -> f a -> f b
contramap :: Contravariant f => (a -> b) -> f b -> f a
Un constructor de tipos que es un functor, pero no aplicativo:
No tengo un buen ejemplo. Lo hay Const
, pero lo ideal sería que fuera un monoide concreto y no se me ocurre ninguno. Todos los tipos son básicamente numéricos, enumeraciones, productos, sumas o funciones, en definitiva. Puede ver a continuación que Pigworker y yo no estamos de acuerdo sobre si Data.Void
es un Monoid
;
instance Monoid Data.Void where
mempty = undefined
mappend _ _ = undefined
mconcat _ = undefined
Dado que _|_
es un valor legal en Haskell y, de hecho, el único valor legal de Data.Void
, cumple con las reglas de Monoid. No estoy seguro de qué unsafeCoerce
tiene que ver con esto, porque ya no se garantiza que su programa no viole la semántica de Haskell tan pronto como utilice cualquier unsafe
función.
Consulte Haskell Wiki para obtener un artículo sobre funciones inferiores ( enlace ) o inseguras ( enlace ).
Me pregunto si es posible crear un constructor de tipos de este tipo utilizando un sistema de tipos más rico, como Agda o Haskell con varias extensiones.
Un constructor de tipos que es un aplicativo, pero no una mónada:
newtype T a = T {multidimensional array of a}
Puedes crear un aplicativo con algo como:
mkarray [(+10), (+100), id] <*> mkarray [1, 2]
== mkarray [[11, 101, 1], [12, 102, 2]]
Pero si la convierte en una mónada, podría producirse una discrepancia de dimensiones. Sospecho que ejemplos como este son raros en la práctica.
Un constructor de tipos que es una mónada:
[]
Acerca de las flechas:
Preguntar dónde se encuentra una flecha en esta jerarquía es como preguntar qué tipo de forma es "roja". Tenga en cuenta la discrepancia de tipos:
Functor :: * -> *
Applicative :: * -> *
Monad :: * -> *
pero,
Arrow :: * -> * -> *
Puede que mi estilo se vea limitado por mi teléfono, pero ahí va.
newtype Not x = Kill {kill :: x -> Void}
no puede ser un Funtor. Si lo fuera, tendríamos
kill (fmap (const ()) (Kill id)) () :: Void
y la Luna estaría hecha de queso verde.
Mientras tanto
newtype Dead x = Oops {oops :: Void}
es un funtor
instance Functor Dead where
fmap f (Oops corpse) = Oops corpse
pero no puede ser aplicable, o tendríamos
oops (pure ()) :: Void
y el Verde estaría hecho de queso Luna (lo que en realidad puede suceder, pero sólo más tarde en la noche).
(Nota adicional: Void
, ya que Data.Void
es un tipo de datos vacío. Si intenta usarlo undefined
para demostrar que es un Monoide, lo usaré unsafeCoerce
para demostrar que no lo es).
alegremente,
newtype Boo x = Boo {boo :: Bool}
es aplicable de muchas maneras, por ejemplo, como diría Dijkstra,
instance Applicative Boo where
pure _ = Boo True
Boo b1 <*> Boo b2 = Boo (b1 == b2)
pero no puede ser una mónada. Para ver por qué no, observe que el retorno debe ser constante Boo True
o Boo False
, y por lo tanto que
join . return == id
No es posible que aguante.
Oh sí, casi lo olvido
newtype Thud x = The {only :: ()}
es una mónada. Enrolla el tuyo.
Avión para coger...
Creo que las otras respuestas omitieron algunos ejemplos simples y comunes:
Un constructor de tipos que es un Functor pero no un Aplicativo. Un ejemplo simple es un par:
instance Functor ((,) r) where
fmap f (x,y) = (x, f y)
Pero no hay manera de definir su Applicative
instancia sin imponer restricciones adicionales r
. En particular, no hay forma de definir pure :: a -> (r, a)
un arbitrario r
.
Un constructor de tipos que es un aplicativo, pero no una mónada. Un ejemplo bien conocido es ZipList . (Es un newtype
sistema que envuelve listas y Applicative
les proporciona una instancia diferente).
fmap
se define de la forma habitual. Pero pure
y <*>
se definen como
pure x = ZipList (repeat x)
ZipList fs <*> ZipList xs = ZipList (zipWith id fs xs)
entonces pure
crea una lista infinita repitiendo el valor dado y <*>
comprime una lista de funciones con una lista de valores; aplica la i -ésima función al i -ésimo elemento. (El estándar <*>
produce []
todas las combinaciones posibles para aplicar la i -ésima función al j -ésimo elemento). Pero no existe una forma sensata de definir una mónada (consulte esta publicación ).
¿Cómo encajan las flechas en la jerarquía funtor/aplicativo/mónada? Ver Los modismos son ajenos, las flechas son meticulosas, las mónadas son promiscuas por Sam Lindley, Philip Wadler, Jeremy Yallop. MSFP 2008. (Lo llaman modismos de funtores aplicativos ). El resumen:
Revisamos la conexión entre tres nociones de computación: las mónadas de Moggi, las flechas de Hughes y los modismos de McBride y Paterson (también llamados functores aplicativos). Mostramos que los modismos son equivalentes a flechas que satisfacen el isomorfismo de tipo A ~> B = 1 ~> (A -> B) y que las mónadas son equivalentes a flechas que satisfacen el isomorfismo de tipo A ~> B = A -> (1 ~ >B). Además, los modismos se incrustan en flechas y las flechas se incrustan en mónadas.
Un buen ejemplo de un constructor de tipos que no es un funtor es Set
: No se puede implementar fmap :: (a -> b) -> f a -> f b
, porque sin una restricción adicional Ord b
no se puede construir f b
.