Al explicarle a alguien qué es un tipo de clase X, lucho por 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 Aplicativo.
- Un constructor de tipo que es un aplicativo, pero no es una mónada.
- Un constructor de tipo 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 la imagen.
Busco ejemplos que serían similares entre sí, que difieren solo en aspectos importantes para pertenecer a la clase de tipo particular.
Si uno pudiera obtener un ejemplo de Arrow en algún lugar de esta jerarquía (¿es entre Applicative y Monad?), ¡Eso también sería genial!
                    
                        haskell
                                monads
                                functor
                                applicative
                                
                    
                    
                        Rotsor
fuente
                
                fuente

* -> *) para el cual no existe un adecuadofmap?a -> Stringno es un functor.a -> Stringes un functor matemático, pero no un HaskellFunctor, para ser claros.(a -> b) -> f b -> f aRespuestas:
Un constructor de tipos que no es un Functor:
Puede hacer un functor contravariante, pero no un functor (covariante). Intenta escribir
fmapy fracasarás. Tenga en cuenta que la versión del fundente contravariante se invierte:Un constructor de tipos que es un functor, pero no Aplicativo:
No tengo un buen ejemplo. Hay
Const, pero idealmente me gustaría un no monoide concreto y no puedo pensar en ninguno. Todos los tipos son básicamente numéricos, enumeraciones, productos, sumas o funciones cuando se llega al final. Puede ver a continuación el trabajador porcino y no estoy de acuerdo sobre siData.Voides unMonoid;Dado que
_|_es un valor legal en Haskell, y de hecho es el único valor legal deData.Voideste, cumple con las reglas de Monoid. No estoy seguro de quéunsafeCoercetiene que ver con esto, porque su programa ya no garantiza que no violará la semántica de Haskell tan pronto como utilice cualquierunsafefunción.Consulte la Wiki de Haskell para obtener un artículo sobre la parte inferior ( enlace ) o las funciones inseguras ( enlace ).
Me pregunto si es posible crear un constructor de este tipo utilizando un sistema de tipos más rico, como Agda o Haskell con varias extensiones.
Un constructor de tipos que es Aplicativo, pero no Mónada:
Puede hacer un aplicativo con algo como:
Pero si lo convierte en una mónada, podría obtener un desajuste de dimensión. 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 el tipo de desajuste:
pero,
fuente
Either acomo un ejemplo para el último caso, ya que es más fácil de entender.ZipList._|_habita cada tipo en *, pero el puntoVoides que deberías inclinarte hacia atrás para construir uno o has destruido su valor. Es por eso que no es una instancia de Enum, Monoid, etc. Si ya tiene uno, me complace dejarlos juntarlos (dándole unSemigroup) peromempty, pero no le doy herramientas para construir explícitamente un valor de tipoVoidenvoid. Tienes que cargar el arma, apuntarla a tu pie y apretar el gatillo tú mismo.Mi estilo puede estar limitado por mi teléfono, pero aquí va.
No puede ser un Functor. Si fuera así, tendríamos
y la luna estaría hecha de queso verde.
mientras tanto
es un functor
pero no puede ser aplicativo, o tendríamos
y Green estaría hecho de queso Moon (que en realidad puede suceder, pero solo más tarde en la noche).
(Nota adicional:
VoidcomoData.Voides un tipo de datos vacío. Si intenta usarloundefinedpara demostrar que es un Monoide, lo usaréunsafeCoercepara demostrar que no lo es).Alegremente
es aplicativo de muchas maneras, por ejemplo, como lo diría Dijkstra,
pero no puede ser una mónada. Para ver por qué no, observe que el retorno debe ser constante
Boo TrueoBoo False, y de ahí queno puede sostenerse.
Oh si, casi me olvido
es una mónada Ruede el suyo.
Avión para atrapar ...
fuente
mempty._|_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:
Pero no hay forma de definir su
Applicativeinstancia sin imponer restricciones adicionalesr. En particular, no hay forma de definirpure :: a -> (r, a)un arbitrarior.Un constructor de tipo que es un aplicativo, pero no es una mónada. Un ejemplo bien conocido es ZipList . (Es una
newtypeenvoltura de listas y proporciona unaApplicativeinstancia diferente para ellos).fmapse define de la manera habitual. Peropurey<*>se definen comopor lo que
purecrea una lista infinita repitiendo el valor dado y<*>cremalleras una lista de funciones con una lista de valores - se aplica i función -ésimo al i -ésimo elemento. (El estándar<*>en[]produce todas las combinaciones posibles de aplicar la función i - ésima al elemento j -ésimo). Pero no hay una manera sensata de cómo definir una mónada (ver esta publicación ).¿Cómo encajan las flechas en la jerarquía de fundador / aplicativo / mónada? Ver modismos son ajenos, las flechas son meticulosas, las mónadas son promiscuas por Sam Lindley, Philip Wadler, Jeremy Yallop. MSFP 2008. (Ellos llaman modismos de los ficticios aplicativos ). El resumen:
fuente
((,) r)es un functor que no es un aplicativo; pero esto es solo porque generalmente no se puede definirpurepara todosra la vez. Por lo tanto, es un capricho de la concisión del lenguaje, de tratar de definir una colección (infinita) de functores aplicativos con una definición depurey<*>; en este sentido, no parece haber nada matemáticamente profundo en este contraejemplo, ya que, para cualquier concretor,((,) r)puede convertirse en un aplicador aplicador. Pregunta: ¿Puedes pensar en un functor de HORMIGÓN que no sea aplicativo?Un buen ejemplo para un constructor de tipos que no es un functor es
Set: No puede implementarfmap :: (a -> b) -> f a -> f b, porque sin una restricción adicionalOrd bno puede construirf b.fuente
fmapes solo un problema de idioma / implementación. Además, es posible ajustarseSeta la mónada de continuación, lo que la convierte en una mónada con todas las propiedades que esperaríamos, vea esta pregunta (aunque no estoy seguro de si se puede hacer de manera eficiente).bpuede ser indecidible, en ese caso no se puede definirfmap!Functorinstancia con laOrdrestricción, pero podría ser posible con una definición diferenteFunctoro mejor soporte de idioma. En realidad, con ConstraintKinds es posible definir una clase de tipo que se pueda parametrizar de esta manera.ordrestricción, el hecho de que aSetno pueda contener entradas duplicadas significa quefmappodría alterar el contexto. Esto viola la ley de asociatividad.Me gustaría proponer un enfoque más sistemático para responder esta pregunta, y también mostrar ejemplos que no usen ningún truco especial como los valores "inferiores" o los tipos de datos infinitos o algo así.
¿Cuándo los constructores de tipos no tienen instancias de clase de tipo?
En general, hay dos razones por las que un constructor de tipos podría no tener una instancia de una clase de tipo determinada:
Los ejemplos del primer tipo son más fáciles que los del segundo tipo porque para el primer tipo, solo necesitamos verificar si uno puede implementar una función con una firma de tipo dada, mientras que para el segundo tipo, debemos demostrar que no hay implementación posiblemente podría satisfacer las leyes.
Ejemplos específicos
Un constructor de tipos que no puede tener una instancia de functor porque el tipo no se puede implementar:
Este es un contrafunctor, no un functor, con respecto al parámetro tipo
a, porque estáaen una posición contravariante. Es imposible implementar una función con firma de tipo(a -> b) -> F z a -> F z b.Un constructor de tipos que no es un functor legal aunque
fmapse pueda implementar la firma de tipo de :El aspecto curioso de este ejemplo es que podemos poner en práctica
fmapdel tipo correcto a pesar de queFno es posible que sea un funtor, ya que utilizaaen una posición contravariante. Por lo tanto, esta implementación que sefmapmuestra arriba es engañosa, a pesar de que tiene la firma de tipo correcta (creo que esta es la única implementación posible de esa firma de tipo), las leyes de functor no están satisfechas. Por ejemplo,fmap id≠id, porquelet (Q(f,_)) = fmap id (Q(read,"123")) in f "456"es123, perolet (Q(f,_)) = id (Q(read,"123")) in f "456"es456.De hecho,
Fes solo un profunctor, no es ni un functor ni un contrafunctor.Un functor legal que no es aplicativo porque la firma de tipo de
pureno se puede implementar: tome la mónada Writer(a, w)y elimine la restricción quewdebería ser un monoide. Entonces es imposible construir un valor de tipo a(a, w)partir dea.Un funtor aplicativo que no es porque el tipo de firma
<*>no pueden ser implementadas:data F a = Either (Int -> a) (String -> a).Un functor que no es legalmente aplicativo a pesar de que se pueden implementar los métodos de clase de tipo:
El constructor de tipos
Pes un functor porqueasolo se usa en posiciones covariantes.La única implementación posible de la firma de tipo de
<*>es una función que siempre devuelveNothing:Pero esta implementación no satisface la ley de identidad para los actores aplicativos.
Applicativepero no unMonadporque la firma de tipo debindno se puede implementar.¡No conozco ninguno de esos ejemplos!
Applicativepero noMonadporque las leyes no se pueden cumplir a pesar de quebindse puede implementar la firma de tipo .Este ejemplo ha generado bastante discusión, por lo que es seguro decir que demostrar que este ejemplo es correcto no es fácil. Pero varias personas han verificado esto independientemente por diferentes métodos. Ver Es `datos PoE a = Vacío | Par aa` una mónada? para discusión adicional.
Es algo engorroso demostrar que no hay una
Monadinstancia legal . La razón del comportamiento no monádico es que no hay una forma natural de implementarbindcuándo una funciónf :: a -> B bpodría regresarNothingoJustpara diferentes valores dea.Quizás sea más claro considerarlo
Maybe (a, a, a), que tampoco es una mónada, y tratar de implementarlojoin. Uno encontrará que no hay una forma intuitiva razonable de implementaciónjoin.En los casos indicados por
???, parece claro que no podemos producirJust (z1, z2, z3)de manera razonable y simétrica a partir de seis valores diferentes de tipoa. Ciertamente podríamos elegir algún subconjunto arbitrario de estos seis valores, por ejemplo, siempre tomar el primer no vacíoMaybe, pero esto no satisfaría las leyes de la mónada. RegresarNothingtampoco satisfará las leyes.bind, pero no cumple con las leyes de identidad.La mónada usual en forma de árbol (o "un árbol con ramas en forma de functor") se define como
Esta es una mónada gratis sobre el functor
f. La forma de los datos es un árbol donde cada punto de ramificación es un "functor-ful" de subárboles. El árbol binario estándar se obtendría contype f a = (a, a).Si modificamos esta estructura de datos haciendo también las hojas en forma de functor
f, obtenemos lo que yo llamo una "semimonada",bindque cumple con las leyes de naturalidad y asociatividad, pero supuremétodo falla una de las leyes de identidad. "Los semimonads son semigrupos en la categoría de endofunctores, ¿cuál es el problema?" Esta es la clase de tipoBind.Por simplicidad, defino el
joinmétodo en lugar debind:El injerto de rama es estándar, pero el injerto de hoja no es estándar y produce a
Branch. Esto no es un problema para la ley de asociatividad, pero rompe una de las leyes de identidad.¿Cuándo los tipos polinómicos tienen instancias de mónada?
Ninguno de los funtores
Maybe (a, a)yMaybe (a, a, a)se puede dar un legítimoMonadejemplo, a pesar de que son obviamenteApplicative.Estos functores no tienen trucos,
Voidni enbottomningún lado, ni pereza / rigidez engañosas, ni estructuras infinitas, ni restricciones de clase de tipo. LaApplicativeinstancia es completamente estándar. Las funcionesreturnybindse pueden implementar para estos functores pero no satisfarán las leyes de la mónada. En otras palabras, estos functores no son mónadas porque falta una estructura específica (pero no es fácil entender qué es exactamente lo que falta). Como ejemplo, un pequeño cambio en el functor puede convertirlo en una mónada:data Maybe a = Nothing | Just aes una mónada. Otro functor similardata P12 a = Either a (a, a)es también una mónada.Construcciones para mónadas polinómicas
En general, aquí hay algunas construcciones que producen
Monads legales a partir de tipos polinomiales. En todas estas construcciones,Mes una mónada:type M a = Either c (w, a)dondewhay un monoidetype M a = m (Either c (w, a))dondemhay alguna mónada ywes cualquier monoidetype M a = (m1 a, m2 a)dondem1ym2son mónadastype M a = Either a (m a)¿Dóndemestá alguna mónada?La primera construcción es
WriterT w (Either c), la segunda construcción esWriterT w (EitherT c m). La tercera construcción es un producto de mónadaspure @Mpor componentes : se define como el producto por componentes depure @m1ypure @m2, yjoin @Mse define omitiendo datos de productos cruzados (por ejemplo,m1 (m1 a, m2 a)se asignam1 (m1 a)omitiendo la segunda parte de la tupla):La cuarta construcción se define como
He comprobado que las cuatro construcciones producen mónadas legales.
Yo conjeturo que no hay otras construcciones para mónadas polinómicas. Por ejemplo, el functor
Maybe (Either (a, a) (a, a, a, a))no se obtiene a través de ninguna de estas construcciones y, por lo tanto, no es monádico. Sin embargo,Either (a, a) (a, a, a)es monádica porque es isomorfo al producto de tres mónadasa,a, yMaybe a. Además,Either (a,a) (a,a,a,a)es monádico porque es isomorfo al producto deayEither a (a, a, a).Las cuatro construcciones mostradas arriba nos permitirán obtener cualquier suma de cualquier número de productos de cualquier número de
a's, por ejemplo,Either (Either (a, a) (a, a, a, a)) (a, a, a, a, a))etc. Todos los constructores de tipo tendrán (al menos una)Monadinstancia.Queda por ver, por supuesto, qué casos de uso podrían existir para tales mónadas. Otro problema es que las
Monadinstancias derivadas a través de las construcciones 1-4 en general no son únicas. Por ejemplo, al constructor de tipostype F a = Either a (a, a)se le puede dar unaMonadinstancia de dos maneras: mediante la construcción 4 con la mónada(a, a)y mediante la construcción 3 con el isomorfismo de tipoEither a (a, a) = (a, Maybe a). Nuevamente, encontrar casos de uso para estas implementaciones no es inmediatamente obvio.Queda una pregunta: dado un tipo de datos polinómico arbitrario, cómo reconocer si tiene una
Monadinstancia. No sé cómo demostrar que no hay otras construcciones para mónadas polinómicas. No creo que exista ninguna teoría hasta ahora para responder a esta pregunta.fuente
Bes una mónada. ¿Puedes dar un contraejemplo a este enlacePair x y >>= f = case (f x, f y) of (Pair x' _,Pair _ y') -> Pair x' y' ; _ -> Empty?ftal quef xesEmptyperof yes unPair, y en el siguiente paso ambos lo sonPair. Verifiqué a mano que las leyes no son válidas para esta implementación o para cualquier otra implementación. Pero es bastante trabajo hacer eso. ¡Ojalá hubiera una manera más fácil de resolver esto!MaybeporqueMaybeno contiene valores diferentes de losaque preocuparse.Monadinstancia consta de funcionesreturnybindque satisfacen las leyes. Hay dos implementacionesreturny 25 implementacionesbindque se ajustan a los tipos requeridos. Puede mostrar mediante cálculo directo que ninguna de las implementaciones cumple con las leyes. Para reducir la cantidad de trabajo requerido, utilicé enjoinlugar debindy usé primero las leyes de identidad. Pero ha sido un buen trabajo.Traversablesea necesario.m (Either a (m a))se transforma usandopure @menm (Either (m a) (m a)). Luego trivialmenteEither (m a) (m a) -> m a, y podemos usarjoin @m. Esa fue la implementación por la cual revisé las leyes.