Enquanto explico para alguém o que é uma classe de tipo X, luto para encontrar bons exemplos de estruturas de dados que são exatamente X.
Então, peço exemplos para:
- Um construtor de tipo que não é um Functor.
- Um construtor de tipo que é um Functor, mas não Aplicável.
- Um construtor de tipo que é um Candidato, mas não é uma Mônada.
- Um construtor de tipo que é uma Mônada.
Eu acho que existem muitos exemplos de Mônada em todos os lugares, mas um bom exemplo de Mônada com alguma relação com exemplos anteriores poderia completar o quadro.
Eu procuro exemplos que sejam semelhantes entre si, diferindo apenas em aspectos importantes por pertencer à classe de tipo particular.
Se alguém conseguisse esgueirar-se para um exemplo de Arrow em algum lugar nessa hierarquia (é entre Applicativo e Mônada?), Isso seria ótimo também!
haskell
monads
functor
applicative
Rotsor
fonte
fonte
* -> *
) para o qual não existe um adequadofmap
?a -> String
não é um functor.a -> String
é um functor matemático, mas não um HaskellFunctor
, para ser claro.(a -> b) -> f b -> f a
Respostas:
Um construtor de tipo que não é um Functor:
Você pode criar um functor contravariante, mas não um functor (covariante). Tente escrever
fmap
e você irá falhar. Observe que a versão contravariante do functor é revertida:Um construtor de tipo que é um functor, mas não é aplicável:
Eu não tenho um bom exemplo. Existe
Const
, mas, idealmente, eu gostaria de um não-monóide concreto e não consigo pensar em nenhum. Todos os tipos são basicamente numéricos, enumerações, produtos, somas ou funções quando você faz isso. Você pode ver abaixo pigworker e eu discordo sobre seData.Void
é umMonoid
;Como
_|_
é um valor legal em Haskell e, de fato, o único valor legal deData.Void
, isso atende às regras do Monoid. Não tenho certeza do queunsafeCoerce
tem a ver com isso, porque seu programa não tem mais garantia de não violar a semântica de Haskell assim que você usar qualquerunsafe
função.Consulte o Wiki da Haskell para obter um artigo sobre as funções inferiores ( link ) ou inseguras ( link ).
Gostaria de saber se é possível criar esse tipo de construtor usando um sistema de tipo mais rico, como Agda ou Haskell com várias extensões.
Um construtor de tipo que é um Candidato, mas não uma Mônada:
Você pode criar um aplicativo com algo como:
Mas se você fizer uma mônada, poderá obter uma incompatibilidade de dimensão. Suspeito que exemplos como esse sejam raros na prática.
Um construtor de tipo que é uma Mônada:
Sobre as setas:
Perguntar onde está uma flecha nessa hierarquia é como perguntar que tipo de forma "vermelho" é. Observe a incompatibilidade de tipos:
mas,
fonte
Either a
um exemplo para o último caso, pois é mais fácil de entender.ZipList
._|_
habita todos os tipos em *, mas o objetivoVoid
é que você precise se curvar para construir um ou destrua seu valor. É por isso que não é uma instância de Enum, Monoid, etc. Se você já tem um, eu estou feliz que você mash-los juntos (dando-lhe umSemigroup
), masmempty
, mas eu não dão ferramentas para construir explicitamente um valor do tipoVoid
emvoid
. Você precisa carregar a arma, apontá-la para o pé e puxar o gatilho.Meu estilo pode ser apertado pelo meu telefone, mas aqui vai.
não pode ser um Functor. Se fosse, teríamos
e a lua seria feita de queijo verde.
Entretanto
é um functor
mas não pode ser aplicável, ou teríamos
e Green seria feito com queijo Moon (o que pode realmente acontecer, mas apenas mais tarde à noite).
(Observação extra :,
Void
como emData.Void
é um tipo de dados vazio. Se você tentarundefined
provar que é um Monoid, usareiunsafeCoerce
para provar que não é.)Com alegria,
é aplicável de várias maneiras, por exemplo, como Dijkstra gostaria,
mas não pode ser uma mônada. Para ver por que não, observe que o retorno deve ser constante
Boo True
ouBoo False
, e, portanto, quenão pode segurar.
Ah sim, eu quase esqueci
é uma mônada. Role o seu próprio.
Avião para pegar ...
fonte
mempty
._|_
Acredito que as outras respostas tenham perdido alguns exemplos simples e comuns:
Um construtor de tipo que é um Functor, mas não um Aplicativo. Um exemplo simples é um par:
Mas não há como definir sua
Applicative
instância sem impor restrições adicionaisr
. Em particular, não há como definirpure :: a -> (r, a)
para um arbitrárior
.Um construtor de tipo que é um Candidato, mas não é uma Mônada. Um exemplo conhecido é o ZipList . (É uma
newtype
lista que agrupa e fornece umaApplicative
instância diferente para eles.)fmap
é definido da maneira usual. Maspure
e<*>
são definidos comoassim
pure
cria uma lista infinita, repetindo o valor dado, e<*>
fecha uma lista de funções com uma lista de valores - aplica-se i função -ésimo para i -simo elemento. (O padrão<*>
on[]
produz todas as combinações possíveis de aplicação da i- ésima função ao j- ésimo elemento.) Mas não há uma maneira sensata de definir uma mônada (veja este post ).Como as setas se encaixam na hierarquia de functor / aplicativo / mônada? Veja Idiomas inconscientes, flechas são meticulosas, mônadas são promíscuas por Sam Lindley, Philip Wadler e Jeremy Yallop. MSFP 2008. (Eles chamam expressões de funções aplicadoras ).
fonte
((,) r)
acontece com um functor que não é um aplicativo; mas isso ocorre apenas porque geralmente não é possível definirpure
tudo der
uma vez. É, portanto, uma peculiaridade da concisão de linguagem, de tentar definir uma coleção (infinita) de functores aplicativos com uma definição depure
e<*>
; nesse sentido, não parece haver nada matematicamente profundo sobre esse contra-exemplo, pois, para qualquer concretor
,((,) r)
pode ser transformado em um functor aplicativo. Pergunta: Você consegue pensar em um functor CONCRETE que deixa de ser um aplicativo?Um bom exemplo para um construtor de tipos que não é um functor é
Set
: Você não pode implementarfmap :: (a -> b) -> f a -> f b
, porque sem uma restrição adicional,Ord b
você não pode construirf b
.fonte
fmap
é apenas uma questão de linguagem / implementação. Além disso, é possível envolverSet
a mônada de continuação, que faz dela uma mônada com todas as propriedades que esperamos, veja esta pergunta (embora eu não tenha certeza se isso pode ser feito com eficiência).b
pode ser indecidível, nesse caso, você não pode definirfmap
!Functor
instância com aOrd
restrição, mas isso pode ser possível com uma definição diferenteFunctor
ou com melhor suporte ao idioma. Na verdade, com o ConstraintKinds , é possível definir uma classe de tipo que pode ser parametrizada dessa maneira.ord
restrição, o fato de que aSet
não pode conter entradas duplicadas significa quefmap
poderia alterar o contexto. Isso viola a lei da associatividade.Eu gostaria de propor uma abordagem mais sistemática para responder a essa pergunta e também mostrar exemplos que não usam truques especiais, como os valores "inferiores" ou tipos de dados infinitos ou algo assim.
Quando os construtores de tipo falham em ter instâncias de classe de tipo?
Em geral, há duas razões pelas quais um construtor de tipos pode falhar ao ter uma instância de uma determinada classe de tipos:
Exemplos do primeiro tipo são mais fáceis do que aqueles do segundo tipo, porque, para o primeiro tipo, precisamos apenas verificar se é possível implementar uma função com uma determinada assinatura de tipo, enquanto que, para o segundo tipo, somos obrigados a provar que nenhuma implementação possivelmente poderia satisfazer as leis.
Exemplos específicos
Um construtor de tipo que não pode ter uma instância de functor porque o tipo não pode ser implementado:
Este é um contrafunctor, não um functor, com relação ao parâmetro type
a
, porquea
em uma posição contravariante. É impossível implementar uma função com assinatura de tipo(a -> b) -> F z a -> F z b
.Um construtor de tipo que não é um functor legal, mesmo que a assinatura de tipo
fmap
possa ser implementada:O aspecto curioso deste exemplo é que nós podemos implementar
fmap
do tipo correto, mesmo queF
não pode ser um functor porque ele usaa
em uma posição contravariant. Portanto, essa implementaçãofmap
mostrada acima é enganosa - mesmo tendo a assinatura de tipo correta (acredito que essa seja a única implementação possível dessa assinatura de tipo), as leis do functor não são cumpridas. Por exemplo,fmap id
≠id
, porquelet (Q(f,_)) = fmap id (Q(read,"123")) in f "456"
é123
, maslet (Q(f,_)) = id (Q(read,"123")) in f "456"
é456
.De fato,
F
é apenas um profunctor, - não é um functor nem um contrafunctor.Um functor legal que não é aplicável porque a assinatura de tipo
pure
não pode ser implementada: pegue a mônada do Writer(a, w)
e remova a restrição quew
deve ser um monóide. É então impossível construir um valor do tipo(a, w)
fora dea
.Um functor que não é aplicativo porque a assinatura tipo de
<*>
impossibilidade de aplicação:data F a = Either (Int -> a) (String -> a)
.Um functor que não é um aplicativo legal, mesmo que os métodos da classe de tipo possam ser implementados:
O construtor de tipos
P
é um functor porque ele usaa
apenas em posições covariantes.A única implementação possível da assinatura de tipo
<*>
é uma função que sempre retornaNothing
:Mas essa implementação não satisfaz a lei de identidade para functores aplicativos.
Applicative
mas não um,Monad
porque a assinatura do tipobind
não pode ser implementada.Eu não conheço nenhum desses exemplos!
Applicative
mas não é,Monad
porque as leis não podem ser satisfeitas, mesmo que a assinatura do tipobind
possa ser implementada.Este exemplo gerou bastante discussão, portanto, é seguro dizer que provar que esse exemplo está correto não é fácil. Mas várias pessoas verificaram isso independentemente por métodos diferentes. Consulte Is `data PoE a = Empty | Emparelhar aa` uma mônada? para discussão adicional.
É um tanto complicado provar que não existe um
Monad
caso legal . A razão para o comportamento não-monádico é que não há uma maneira natural de implementarbind
quando uma funçãof :: a -> B b
pode retornarNothing
ouJust
para valores diferentes dea
.Talvez seja mais claro considerar
Maybe (a, a, a)
, que também não é uma mônada, e tentar implementarjoin
isso. Descobriremos que não há uma maneira intuitivamente razoável de implementarjoin
.Nos casos indicados por
???
, parece claro que não podemos produzir deJust (z1, z2, z3)
maneira razoável e simétrica dentre seis valores diferentes de tipoa
. Certamente poderíamos escolher algum subconjunto arbitrário desses seis valores - por exemplo, sempre levar o primeiro não vazioMaybe
- mas isso não satisfaria as leis da mônada. O retornoNothing
também não satisfará as leis.bind
- mas falha nas leis de identidade.A mônada usual de árvore (ou "uma árvore com galhos em forma de functor") é definida como
Esta é uma mônada livre sobre o functor
f
. A forma dos dados é uma árvore em que cada ponto de ramificação é um "functor-ful" de subárvores. A árvore binária padrão seria obtida comtype f a = (a, a)
.Se modificarmos essa estrutura de dados, fazendo também as folhas na forma do functor
f
, obteremos o que chamo de "semimonada" - elabind
satisfaz as leis de naturalidade e associatividade, mas seupure
método falha em uma das leis de identidade. "Semimonados são semigrupos na categoria de endofuncionadores, qual é o problema?" Esta é a classe de tipoBind
.Para simplificar, defino o
join
método em vez debind
:O enxerto de ramo é padrão, mas o enxerto de folhas não é padrão e produz a
Branch
. Isso não é um problema para a lei de associatividade, mas quebra uma das leis de identidade.Quando os tipos polinomiais têm instâncias de mônada?
Nenhum dos functores
Maybe (a, a)
eMaybe (a, a, a)
pode receber umaMonad
instância legal , embora sejam obviamenteApplicative
.Esses functores não têm truques -
Void
nembottom
em nenhum lugar, nem em preguiça / rigidez complicada, nem em estruturas infinitas nem em restrições de classe de tipo. AApplicative
instância é completamente padrão. As funçõesreturn
ebind
podem ser implementadas para esses functores, mas não satisfazem as leis da mônada. Em outras palavras, esses functores não são mônadas porque falta uma estrutura específica (mas não é fácil entender exatamente o que está faltando). Como exemplo, uma pequena alteração no functor pode transformá-lo em uma mônada:data Maybe a = Nothing | Just a
é uma mônada. Outro functor semelhantedata P12 a = Either a (a, a)
também é uma mônada.Construções para mônadas polinomiais
Em geral, aqui estão algumas construções que produzem
Monad
s legais a partir de tipos polinomiais. Em todas essas construções,M
há uma mônada:type M a = Either c (w, a)
ondew
está algum monóidetype M a = m (Either c (w, a))
ondem
está qualquer mônada ew
é qualquer monóidetype M a = (m1 a, m2 a)
ondem1
em2
qualquer mônadatype M a = Either a (m a)
ondem
tem mônadaA primeira construção é
WriterT w (Either c)
, a segunda construção éWriterT w (EitherT c m)
. A terceira construção é um produto de mônadas empure @M
termos de componentes : é definido como o produto em termos de componentes depure @m1
epure @m2
, ejoin @M
é definido pela omissão de dados de produtos cruzados (por exemplo,m1 (m1 a, m2 a)
é mapeado param1 (m1 a)
omissão da segunda parte da tupla):A quarta construção é definida como
Eu verifiquei que todas as quatro construções produzem mônadas legais.
I conjecturar que não existem outras construções para monads polinomiais. Por exemplo, o functor
Maybe (Either (a, a) (a, a, a, a))
não é obtido através de nenhuma dessas construções e, portanto, não é monádico. No entanto,Either (a, a) (a, a, a)
é monádico, porque é isomorfo ao produto de três mônadasa
,a
, eMaybe a
. Além disso,Either (a,a) (a,a,a,a)
é monádico porque é isomórfico ao produto dea
eEither a (a, a, a)
.As quatro construções mostradas acima nos permitem obter qualquer soma de qualquer número de produtos de qualquer número de
a
's, por exemplo,Either (Either (a, a) (a, a, a, a)) (a, a, a, a, a))
e assim por diante. Todos esses tipos de construtores terão (pelo menos uma)Monad
instância.Resta saber, é claro, que casos de uso podem existir para essas mônadas. Outra questão é que as
Monad
instâncias derivadas pelas construções 1-4 geralmente não são exclusivas. Por exemplo, o construtor de tipostype F a = Either a (a, a)
pode receber umaMonad
instância de duas maneiras: pela construção 4 usando a mônada(a, a)
e pela construção 3 usando o isomorfismo do tipoEither a (a, a) = (a, Maybe a)
. Novamente, encontrar casos de uso para essas implementações não é imediatamente óbvio.Uma pergunta permanece - dado um tipo de dados polinomial arbitrário, como reconhecer se ele tem uma
Monad
instância. Não sei como provar que não há outras construções para mônadas polinomiais. Não acho que exista nenhuma teoria até agora para responder a essa pergunta.fonte
B
é uma mônada. Você pode dar um contra-exemplo para esse vínculoPair x y >>= f = case (f x, f y) of (Pair x' _,Pair _ y') -> Pair x' y' ; _ -> Empty
?f
tal quef x
éEmpty
masf y
é umPair
e, no próximo passo, ambos sãoPair
. Eu verifiquei manualmente que as leis não se aplicam a esta implementação ou a qualquer outra implementação. Mas é um bom trabalho fazer isso. Eu gostaria que houvesse uma maneira mais fácil de descobrir isso!Maybe
porqueMaybe
não contém valores diferentesa
para se preocupar.Monad
instância consiste em funçõesreturn
ebind
que satisfazem leis. Existem duas implementaçõesreturn
e 25 implementaçõesbind
que se encaixam nos tipos necessários. Você pode mostrar, por cálculo direto, que nenhuma das implementações atende às leis. Para reduzir a quantidade de trabalho necessária, eu useijoin
em vez debind
e usaram as leis de identidade pela primeira vez. Mas tem sido um bom trabalho.Traversable
é necessário.m (Either a (m a))
é transformado usandopure @m
emm (Either (m a) (m a))
. Então trivialmenteEither (m a) (m a) -> m a
, e podemos usarjoin @m
. Essa foi a implementação para a qual eu verifiquei as leis.