A categoria de conjuntos é monoidal cartesiano e cocartesiano monoidal. Os tipos de isomorfismos canônicos que testemunham essas duas estruturas monoidais estão listados abaixo:
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
Para os fins desta pergunta, defino Alternative
ser um functor monoidal frouxo de Hask sob o Either
tensor para Hask sob o (,)
tensor (e não mais):
class Functor f => Alt f
where
union :: f a × f b -> f (a + b)
class Alt f => Alternative f
where
nil :: () -> f Void
As leis são justas para um relaxador monoidal relaxado.
Associatividade:
fwd tassoc >>> bimap id union >>> union
=
bimap union id >>> union >>> fmap (fwd eassoc)
Unidade esquerda:
fwd tlunit
=
bimap nil id >>> union >>> fmap (fwd elunit)
Unidade direita:
fwd trunit
=
bimap id nil >>> union >>> fmap (fwd erunit)
Aqui está como recuperar as operações mais familiares para a Alternative
classe de tipos em termos dos mapas de coerência da codificação laxante de função monoidal:
(<|>) :: 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
functors ser oplax functors monoidais de Hask sob o Either
tensor para Hask sob o (,)
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 ()
Tendo por suas leis um pouco atrasadas leis de função monoidal relaxadas:
Associatividade:
bwd tassoc <<< bimap id partition <<< partition
=
bimap partition id <<< partition <<< fmap (bwd eassoc)
Unidade esquerda:
bwd tlunit
=
bimap trivial id <<< partition <<< fmap (bwd elunit)
Unidade direita:
bwd trunit
=
bimap id trivial <<< partition <<< fmap (bwd erunit)
Definindo funções padrão de filtro-y como mapMaybe
e filter
em termos do codificador oplax monoidal deixado como um exercício para o leitor interessado:
mapMaybe :: Filterable f => (a -> Maybe b) -> f a -> f b
mapMaybe = _
filter :: Filterable f => (a -> Bool) -> f a -> f a
filter = _
A questão é esta: é tudo Alternative
Monad
também Filterable
?
Podemos digitar tetris para uma implementação:
instance (Alternative f, Monad f) => Filter f
where
partition fab = (fab >>= either return (const empty), fab >>= either (const empty) return)
Mas essa implementação é sempre legal? Às vezes é legal (para alguma definição formal de "às vezes")? Provas, contra-exemplos e / ou argumentos informais seriam todos muito úteis. Obrigado.
Filterable
leis são bastante fracas). @AsadSaeeduddin Considere adquirir algumas habilidades de prova do teorema interativo para que você possa estender a mentalidade "tipos de uso, não o cérebro" a provas também!Respostas:
Aqui vai um argumento que apoia amplamente sua bela ideia.
Parte um: mapMaybe
Meu plano aqui é reafirmar o problema em termos de
mapMaybe
, esperando que isso nos leve a um terreno mais familiar. Para fazer isso, usarei algumasEither
funções do utilitário -juggling:(Peguei os três primeiros nomes de relude e o quarto de erros . A propósito, os erros oferecem
maybeToRight
erightToMaybe
comonote
ehush
respectivamente, emControl.Error.Util
.)Como você observou,
mapMaybe
pode ser definido em termos departition
:Fundamentalmente, também podemos fazer o contrário:
Isso sugere que faz sentido reformular suas leis em termos de
mapMaybe
. Com as leis de identidade, isso nos dá uma ótima desculpa para esquecer completamentetrivial
:Quanto à associatividade, podemos usar
rightToMaybe
eleftToMaybe
dividir a lei em três equações, uma para cada componente que obtemos das partições sucessivas:Parametridade significa
mapMaybe
é agnóstico em relação aosEither
valores com os quais estamos lidando aqui. Sendo assim, podemos usar nosso pequeno arsenal deEither
isomorfismos para embaralhar as coisas e mostrar que [I] é equivalente a [II] e [III] é equivalente a [V]. Agora, temos três equações:A parametridade nos permite engolir o
fmap
in [I]:Isso, no entanto, é simplesmente ...
... o que equivale à lei de conservação / identidade da da witherable 's
Filterable
:Que
Filterable
também tem uma lei de composição:Também podemos derivar este de nossas leis? Vamos começar de [III] e, mais uma vez, a parametridade faz seu trabalho. Este é mais complicado, então vou anotá-lo na íntegra:
Na outra direção:
(Observação: enquanto
maybeToRight () . rightToMaybe :: Either a b -> Either () b
não estiverid
, na derivação acima os valores à esquerda serão descartados de qualquer maneira, portanto, é justo eliminá-lo como se fosseid
.)Assim [III] é equivalente à lei composição de witherable s'
Filterable
.Neste ponto, podemos usar a lei de composição para lidar com [IV]:
Basta mostrar que sua classe equivale a uma formulação bem estabelecida de
Filterable
, o que é um resultado muito bom. Aqui está um resumo das leis:Como observam os documentos ocultáveis , essas são leis de um functor de Kleisli Maybe a Hask .
Parte dois: Alternativa e Mônada
Agora podemos responder à sua pergunta real, que era sobre mônadas alternativas. Sua implementação proposta
partition
foi:Seguindo meu plano mais amplo, mudarei para a
mapMaybe
apresentação:E assim podemos definir:
Ou, em uma ortografia sem ponto:
Alguns parágrafos acima, observei que as
Filterable
leis dizem quemapMaybe
é o mapeamento morfístico de um functor de Kleisli Maybe a Hask . Dado que a composição de functors é um functor, e(=<<)
é o mapeamento morphism de um functor de Kleisli f a Hask ,(maybe empty return .)
sendo o mapeamento morphism de um functor de Kleisli Talvez a Kleisli f suficiente paramapMaybeAM
ser legal. As leis relevantes do functor são:Essa lei de identidade é válida, então vamos nos concentrar na composição:
Portanto,
mapMaybeAM
é lícito semaybe empty return . g =<< empty = empty
for o casog
. Agora, seempty
for definido comoabsurd <$> nil ()
, como você fez aqui, podemos provar que,f =<< empty = empty
para qualquerf
:Intuitivamente, se
empty
estiver realmente vazio (como deve ser, dada a definição que estamos usando aqui), não haverá valoresf
a serem aplicados e, portanto,f =<< empty
não poderá resultar em nada além deempty
.Uma abordagem diferente aqui seria analisar a interação das classes
Alternative
eMonad
. Quando isso acontece, há uma classe para monads alternativas:MonadPlus
. Por conseguinte, um reestilizadomapMaybe
pode ser assim:Embora existam opiniões variadas sobre qual conjunto de leis é mais apropriado
MonadPlus
, uma das leis para as quais ninguém parece se opor é ...... que é precisamente a propriedade de
empty
discutirmos alguns parágrafos acima. A legalidade demmapMaybe
segue imediatamente a partir da lei zero esquerda.(Aliás,
Control.Monad
fornecemfilter :: MonadPlus m => (a -> Bool) -> m a -> m a
, que corresponde aofilter
que podemos definir usandommapMaybe
.)Em suma:
Sim, a implementação é legal. Esta conclusão depende do
empty
fato de estar vazio, como deveria, ou da mônada alternativa relevante após o zero à esquerdaMonadPlus
lei , que se resume a praticamente a mesma coisa.Vale ressaltar que
Filterable
não é subsumido porMonadPlus
, como podemos ilustrar com os seguintes contra-exemplos:ZipList
: filtrável, mas não uma mônada. AFilterable
instância é igual à das listas, mesmo queAlternative
diferente.Map
: filtrável, mas nem mônada nem aplicativo. De fato,Map
nem sequer pode ser aplicável porque não há uma implementação sensata dopure
. No entanto, tem o seu próprioempty
.MaybeT f
: enquanto suas instânciasMonad
eAlternative
precisamf
ser uma mônada, e umaempty
definição isolada precisaria de pelo menosApplicative
, aFilterable
instância requer apenasFunctor f
(tudo se torna filtrável se vocêMaybe
inserir uma camada nela).Parte três: vazia
Nesse ponto, ainda podemos nos perguntar o tamanho de um papel
empty
, ounil
, realmente desempenhaFilterable
. Não é um método de classe e, no entanto, a maioria das instâncias parece ter uma versão sensata dele por aí.A única coisa que podemos ter certeza é que, se o tipo filtrável tiver habitantes, pelo menos um deles será uma estrutura vazia, porque sempre podemos pegar qualquer habitante e filtrar tudo:
A existência de
chop
, embora não signifique que haverá um úniconil
valor vazio, ou quechop
sempre dará o mesmo resultado. Considere, por exemplo,MaybeT IO
cujaFilterable
instância possa ser pensada como uma maneira de censurar os resultados dosIO
cálculos. A instância é perfeitamente lícita, emborachop
possa produzirMaybeT IO Void
valores distintos que carregamIO
efeitos arbitrários .Em uma nota final, de ter aludido a possibilidade de trabalhar com fortes functors monoidais, de modo que
Alternative
eFilterable
estão ligados por fazerunion
/partition
enil
/trivial
isomorfismos. Terunion
epartition
como inverso mútuo é concebível, mas bastante limitador, dado queunion . partition
descarta algumas informações sobre o arranjo dos elementos para uma grande parcela de instâncias. Quanto ao outro isomorfismo,trivial . nil
é trivial, masnil . trivial
é interessante, pois implica que há apenas um únicof Void
valor, algo que vale para uma parcela considerável deFilterable
instâncias. Acontece que existe umaMonadPlus
versão dessa condição. Se exigirmos isso, para qualqueru
...... e, em seguida, substitua o
mmapMaybe
da parte dois, obtemos:Essa propriedade é conhecida como lei zero correta
MonadPlus
, embora haja boas razões para contestar seu status como uma lei dessa classe específica.fonte
Monad
eAlternative
"?MonadPlus
(possivelmente visto através da caracterização quase semicondutora ) estabelece uma conexão entreAlternative
eMonad
que o "função monoidal sem adornos de caracterização Hask-com-(,)
para Hask-com-Either
" não oferece. De qualquer forma, ainda estou me perguntando se há algo mais profundo sobre comoempty
, que parece estranho àFilterable
definição simples e ainda parece bastante adequado, aparece aqui.empty
faz parte da definição deAlternative
. OAlternative
eFilterable
pode ser mais intimamente relacionado, exigindo queunion
seja o inverso departition
(e vice-versa), e quetrivial
seja o inverso denil
(e vice-versa). Isso é chamado de "forte função monoidal".Filterable
instâncias comuns , essa propriedade seria muito forte, poispartition
pode ser com perdas. Por exemplo,(union . partition) [L 7, R 2, L 1, R 6]
é[L 7, L 1, R 2, R 6]
. A partetrivial
/nil
acabaria resumindo-se a ter apenas umf Void
valor, o que parece mais favorável. EmMonadPlus
termos, corresponde à lei do direito zero contestadam >> mzero = mzero
, que, por exemplo, vale para listas, mas não para analisadores.empty
. Esta é possivelmente a atualização final :)