La categoría de conjuntos es tanto monoidal cartesiana como monoidal cocartesiana. Los tipos de isomorfismos canónicos que presencian estas dos estructuras monoidales se enumeran a continuación:
type x + y = Either x y
type x × y = (x, y)
data Iso a b = Iso { fwd :: a -> b, bwd :: b -> a }
eassoc :: Iso ((x + y) + z) (x + (y + z))
elunit :: Iso (Void + x) x
erunit :: Iso (x + Void) x
tassoc :: Iso ((x × y) × z) (x × (y × z))
tlunit :: Iso (() × x) x
trunit :: Iso (x × ()) x
A los fines de esta pregunta, defino que soy Alternative
un functor monoidal laxo desde Hask bajo el Either
tensor hasta Hask bajo el (,)
tensor (y no más):
class Functor f => Alt f
where
union :: f a × f b -> f (a + b)
class Alt f => Alternative f
where
nil :: () -> f Void
Las leyes son solo las de un functor monoidal laxo.
Asociatividad:
fwd tassoc >>> bimap id union >>> union
=
bimap union id >>> union >>> fmap (fwd eassoc)
Unidad izquierda:
fwd tlunit
=
bimap nil id >>> union >>> fmap (fwd elunit)
Unidad correcta:
fwd trunit
=
bimap id nil >>> union >>> fmap (fwd erunit)
Aquí se explica cómo recuperar las operaciones más familiares para la Alternative
clase de tipos en términos de los mapas de coherencia de la codificación del functor monoidal laxo:
(<|>) :: Alt f => f a -> f a -> f a
x <|> y = either id id <$> union (Left <$> x, Right <$> y)
empty :: Alternative f => f a
empty = absurd <$> nil ()
Defino Filterable
funtores ser oplax funtores monoidales de Hask bajo el Either
tensor a Hask bajo el (,)
tensor:
class Functor f => Filter f
where
partition :: f (a + b) -> f a × f b
class Filter f => Filterable f
where
trivial :: f Void -> ()
trivial = const ()
Teniendo para sus leyes solo leyes de functor monoidal laxas hacia atrás:
Asociatividad:
bwd tassoc <<< bimap id partition <<< partition
=
bimap partition id <<< partition <<< fmap (bwd eassoc)
Unidad izquierda:
bwd tlunit
=
bimap trivial id <<< partition <<< fmap (bwd elunit)
Unidad correcta:
bwd trunit
=
bimap id trivial <<< partition <<< fmap (bwd erunit)
La definición de funciones de filtro y estándar como mapMaybe
y filter
en términos de la codificación del functor monoidal oplax se dejó como ejercicio para el lector interesado:
mapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b
mapMaybe = _
filter :: Filterable f => (a -> Bool) -> f a -> f a
filter = _
La pregunta es esta: ¿es cada Alternative
Monad
también Filterable
?
Podemos tetris tetris nuestro camino a una implementación:
instance (Alternative f, Monad f) => Filter f
where
partition fab = (fab >>= either return (const empty), fab >>= either (const empty) return)
Pero, ¿es esta implementación siempre legal? ¿A veces es legal (para alguna definición formal de "a veces")? Las pruebas, contraejemplos y / o argumentos informales serían muy útiles. Gracias.
Filterable
leyes son bastante débiles). @AsadSaeeduddin ¡Considera adquirir algunas habilidades interactivas de demostración de teoremas para que puedas extender la mentalidad de "usar tipos, no tu cerebro" a las pruebas también!Respuestas:
Aquí va un argumento que apoya ampliamente su hermosa idea.
Primera parte: mapMaybe
Mi plan aquí es replantear el problema en términos de
mapMaybe
, con la esperanza de que hacerlo nos lleve a un terreno más familiar. Para hacerlo,Either
usaré algunas funciones de utilidad que cambian:(Tomé los primeros tres nombres de relude , y el cuarto de errores . Por cierto, ofertas de errores
maybeToRight
yrightToMaybe
comonote
yhush
respectivamente, enControl.Error.Util
).Como notó,
mapMaybe
se puede definir en términos departition
:De manera crucial, también podemos dar la vuelta:
Esto sugiere que tiene sentido reformular sus leyes en términos de
mapMaybe
. Con las leyes de identidad, hacerlo nos da una gran excusa para olvidarnos por completo detrivial
:En cuanto a la asociatividad, podemos usar
rightToMaybe
yleftToMaybe
dividir la ley en tres ecuaciones, una para cada componente que obtenemos de las particiones sucesivas:Parametricity significa
mapMaybe
es agnóstico con respecto a losEither
valores que estamos tratando aquí. Siendo así, podemos usar nuestro pequeño arsenal deEither
isomorfismos para barajar las cosas y mostrar que [I] es equivalente a [II], y [III] es equivalente a [V]. Ahora tenemos tres ecuaciones:La parametricidad nos permite tragar la
fmap
in [I]:Eso, sin embargo, es simplemente ...
... que es equivalente a la ley de conservación / identidad de Witherable 's
Filterable
:Eso
Filterable
también tiene una ley de composición:¿Podemos también derivar esto de nuestras leyes? Comencemos desde [III] y, una vez más, hagamos que la parametricidad haga su trabajo. Este es más complicado, así que lo escribiré en su totalidad:
En la otra dirección:
(Nota: mientras
maybeToRight () . rightToMaybe :: Either a b -> Either () b
no lo estéid
, en la derivación arriba los valores de la izquierda se descartarán de todos modos, por lo que es justo tacharlo como si lo fueraid
).Por lo tanto, [III] es equivalente a la ley de composición de witherable 's
Filterable
.En este punto, podemos usar la ley de composición para tratar [IV]:
Esto es suficiente para mostrar que su clase equivale a una formulación bien establecida de
Filterable
, que es un resultado muy bueno. Aquí hay un resumen de las leyes:Como señalan los doctores , estas son leyes de functor para un functor desde Kleisli Maybe hasta Hask .
Segunda parte: alternativa y mónada
Ahora podemos abordar su pregunta real, que era sobre mónadas alternativas. Su implementación propuesta de
partition
fue:Siguiendo mi plan más amplio, pasaré a la
mapMaybe
presentación:Y así podemos definir:
O, en una ortografía sin puntos:
Algunos párrafos anteriores, noté que las
Filterable
leyes dicen quemapMaybe
es el mapeo de morfismo de un functor desde Kleisli Maybe hasta Hask . Dado que la composición de los functores es un functor, y(=<<)
el mapeo de morfismo de un functor de Kleisli f a Hask ,(maybe empty return .)
el mapeo de morfismo de un functor de Kleisli Quizás a Kleisli f es suficiente paramapMaybeAM
ser legal. Las leyes de functor relevantes son:Esta ley de identidad es válida, así que centrémonos en la composición uno:
Por lo tanto,
mapMaybeAM
es legal si esmaybe empty return . g =<< empty = empty
por algunog
. Ahora, siempty
se define comoabsurd <$> nil ()
, como lo ha hecho aquí, podemos probarlof =<< empty = empty
para cualquierf
:Intuitivamente, si
empty
está realmente vacío (como debe ser, dada la definición que estamos usando aquí), no habrá valores paraf
aplicar, porf =<< empty
lo que no puede resultar en nada más queempty
.Un enfoque diferente aquí sería investigar la interacción de las clases
Alternative
yMonad
. Da la casualidad que hay una clase de mónadas alternativas:MonadPlus
. En consecuencia, un rediseñadomapMaybe
podría verse así:Si bien existen diversas opiniones sobre qué conjunto de leyes es el más apropiado
MonadPlus
, una de las leyes que nadie parece objetar es ...... que es precisamente la propiedad de la
empty
que estábamos discutiendo algunos párrafos anteriores. La legalidad demmapMaybe
sigue inmediatamente de la ley cero izquierda.(Por cierto,
Control.Monad
proporcionamfilter :: MonadPlus m => (a -> Bool) -> m a -> m a
, que coincide con elfilter
que podemos definir usandommapMaybe
).En resumen:
Sí, la implementación es legal. Esta conclusión depende del
empty
hecho de que esté vacío, como debería, o de la mónada alternativa relevante que sigue laMonadPlus
ley de cero a la izquierda , que se reduce a casi lo mismo.Vale la pena enfatizar que
Filterable
no está subsumidoMonadPlus
, como podemos ilustrar con los siguientes contraejemplos:ZipList
: filtrable, pero no una mónada. LaFilterable
instancia es la misma que para las listas, a pesar deAlternative
que es diferente.Map
: filtrable, pero ni mónada ni aplicativo. De hecho,Map
ni siquiera puede ser aplicativo porque no hay una implementación sensata depure
. Sin embargo, tiene el suyoempty
.MaybeT f
: mientras que sus instanciasMonad
y deben ser una mónada, y una definición aislada necesitaría al menos , la instancia solo requiere (cualquier cosa se puede filtrar si desliza una capa en ella).Alternative
f
empty
Applicative
Filterable
Functor f
Maybe
Tercera parte: vacía
En este punto, uno todavía podría preguntarse qué tan importante es un papel
empty
o quénil
realmente juegaFilterable
. No es un método de clase y, sin embargo, la mayoría de las instancias parecen tener una versión sensata del mismo.De lo único que podemos estar seguros es que, si el tipo filtrable tiene habitantes, al menos uno de ellos será una estructura vacía, porque siempre podemos tomar a cualquier habitante y filtrar todo:
La existencia de
chop
, sin embargo, no significa que habrá un solonil
valor vacío, o quechop
siempre dará el mismo resultado. Consideremos, por ejemplo,MaybeT IO
cuyoFilterable
ejemplo podría ser considerado como una forma de censurar los resultados deIO
los cálculos. La instancia es perfectamente legal, aunquechop
puede producirMaybeT IO Void
valores distintos que conllevanIO
efectos arbitrarios .En una nota final, usted ha aludido a la posibilidad de trabajar con fuertes funtores monoidales, por lo que
Alternative
yFilterable
están vinculados al hacerunion
/partition
ynil
/trivial
isomorfismos. Tenerunion
ypartition
como inversos mutuos es concebible pero bastante limitante, dado queunion . partition
descarta cierta información sobre la disposición de los elementos para una gran parte de las instancias. En cuanto al otro isomorfismo,trivial . nil
es trivial, peronil . trivial
es interesante porque implica que solo hay unf Void
valor único , algo que vale para una parte considerable deFilterable
instancias. Sucede que hay unaMonadPlus
versión de esta condición. Si exigimos eso, para cualquieru
...... y luego sustituimos la
mmapMaybe
parte dos, obtenemos:Esta propiedad se conoce como la ley de cero a la derecha
MonadPlus
, aunque hay buenas razones para impugnar su condición de ley de esa clase en particular.fuente
Monad
eAlternative
instancias"?MonadPlus
(posiblemente visto a través de la caracterización de casi semired ) establece una conexión entreAlternative
yMonad
que el "adorno monoidal sin adornos de Hask-(,)
with- a Hask-with-Either
" no ofrece. En cualquier caso, todavía me pregunto si hay algo más profundo sobre cómoempty
, que parece extraño a laFilterable
definición simple y, sin embargo, se siente bastante apropiado, aparece aquí.empty
es parte de la definición deAlternative
. ElAlternative
yFilterable
puede estar más estrechamente relacionado al exigir queunion
sea el inverso departition
(y viceversa), y quetrivial
sea el inverso denil
(y viceversa). Esto es lo que se llama un "functor monoidal fuerte".Filterable
instancias comunes , esa propiedad sería demasiado fuerte, ya quepartition
puede ser con pérdidas. Por ejemplo,(union . partition) [L 7, R 2, L 1, R 6]
es[L 7, L 1, R 2, R 6]
. La partetrivial
/nil
finalmente se reduciría a tener un solof Void
valor, lo que parece más aceptable. EnMonadPlus
términos, corresponde a la ley de derecho cero impugnadam >> mzero = mzero
, que, por ejemplo, es válida para listas pero no para analizadores.empty
. Esta es posiblemente la actualización final :)