Uma mônada é apenas um monóide na categoria de endofunitores, qual é o problema?


723

Quem primeiro disse o seguinte?

Uma mônada é apenas um monóide na categoria de endofunitores, qual é o problema?

E, em uma nota menos importante, isso é verdade e, em caso afirmativo, você poderia dar uma explicação (espero que possa ser entendida por alguém que não tenha muita experiência com Haskell)?


14
Veja "Categorias para o matemático que trabalha"
Don Stewart

19
Você não precisa entender isso para usar mônadas em Haskell. De uma perspectiva prática, eles são apenas uma maneira inteligente de passar pelo "estado" através de algum encanamento subterrâneo.
starblue

1
Também gostaria de adicionar este excelente post no blog: stephendiehl.com/posts/monads.html Ele não responde diretamente à pergunta, mas, na minha opinião, Stephen faz um excelente trabalho de amarrar categorias e mônadas em Haskell. Se você leu as respostas acima, isso deve ajudar a unificar as duas maneiras de analisar isso.
Ben Ford

3
Mais precisamente "Para qualquer categoria C, a categoria [C, C] de seus endofunitores tem uma estrutura monoidal induzida pela composição. Um objeto monóide em [C, C] é uma mônada em C." - de en.wikipedia.org/wiki/Monoid_%28category_theory%29. Veja en.wikipedia.org/wiki/Monad_%28category_theory%29 para obter a definição de mônada na teoria das categorias.

1
@Dmitry Um functor é uma função entre categorias, com algumas restrições a serem bem comportadas. Um endofuncor em uma categoria C é apenas um functor de C para ele mesmo. Data.Functor é uma classe de tipo de endofunctors na categoria Hask . Como uma categoria consiste em objetos e morfismos, um functor precisa mapear ambos. Para uma instância f do Data.Functor, o mapa nos objetos (tipos de haskell) é f em si e o mapa nos morfismos (funções de haskell) é fmap.
Matthijs 29/10

Respostas:


796

Esse fraseado em particular é de James Iry, de sua divertida História Breve, Incompleta e Muito Errada de Linguagens de Programação , na qual ele atribui ficcionalmente a Philip Wadler.

A citação original é de Saunders Mac Lane, em Categories for the Working Mathematician , um dos textos fundamentais da Teoria das Categorias. Aqui está o contexto , que é provavelmente o melhor lugar para aprender exatamente o que isso significa.

Mas vou dar uma facada. A frase original é esta:

No total, uma mônada em X é apenas um monóide na categoria de endofunitores de X, com o produto × substituído pela composição dos endofunitores e a unidade definida pelo endofuncor de identidade.

X aqui é uma categoria. Endofuncionadores são functores de uma categoria para si (que geralmente é tudo Functor s no que diz respeito a programadores funcionais, já que eles lidam principalmente com apenas uma categoria; a categoria de tipos - mas eu discordo). Mas você pode imaginar outra categoria que é a categoria de "endofuncionadores em X ". Essa é uma categoria em que os objetos são endofuncionadores e os morfismos são transformações naturais.

E desses endofunidores, alguns deles podem ser mônadas. Quais são as mônadas? Exatamente os que são monoidais em um sentido particular. Em vez de especificar o mapeamento exato de mônadas a monoides (já que Mac Lane faz isso muito melhor do que eu esperava), colocarei suas respectivas definições lado a lado e deixarei você comparar:

Um monóide é ...

  • Um conjunto, S
  • Uma operação, •: S × S → S
  • Um elemento de S , e: 1 → S

... satisfazendo estas leis:

  • (um • b) c = • • um (b • c) , para todos um , b e c em S
  • e • a = a • e = a , para todos os a em S

Uma mônada é ...

  • Um endofuncor, T: X → X (em Haskell, um construtor de tipos do tipo * -> *com uma Functorinstância)
  • Uma transformação natural, μ: T × T → T , onde × significa composição do funcionamento ( μ é conhecido como joinem Haskell)
  • Uma transformação natural, η: I → T , onde eu sou o identificador final da identidade em X ( η é conhecido como returnem Haskell)

... satisfazendo estas leis:

  • μ ∘ Tμ = μ ∘ μT
  • μ ∘ Tη = μ ∘ ηT = 1 (a transformação natural da identidade)

Com um pouco de estrabismo, você poderá ver que essas duas definições são instâncias do mesmo conceito abstrato .


21
obrigado pela explicação e obrigado pelo artigo Breve, incompleto e muito errado histórico das linguagens de programação. Eu pensei que poderia ser de lá. Verdadeiramente uma das maiores peças de humor da programação.
Roman A. Taycher

6
@ Jonathan: Na formulação clássica de um monóide, × significa o produto cartesiano de conjuntos. Você pode ler mais sobre isso aqui: en.wikipedia.org/wiki/Cartesian_product , mas a idéia básica é que um elemento de S × T é um par (S, T) , onde s ∈ S e t ∈ T . Portanto, a assinatura do produto monoidal : S × S -> S nesse contexto significa simplesmente uma função que recebe 2 elementos de S como entrada e produz outro elemento de S como saída.
Tom Crockett

12
@TahirHassan - Na generalidade da teoria das categorias, lidamos com "objetos" opacos em vez de conjuntos, e, portanto, não há noção a priori de "elementos". Mas se você pensar na categoria Conjunto, onde os objetos são conjuntos e as setas são funções, os elementos de qualquer conjunto S estão em correspondência um-a-um com as funções de qualquer conjunto de elementos único em S. Ou seja, para qualquer elemento e de S , há exatamente uma função f: 1 -> S , onde 1 é qualquer conjunto de um elemento ... (continuação)
Tom Crockett

12
Os conjuntos de 1 elemento @TahirHassan são, eles próprios, especializações da noção teórica por categoria mais geral de "objetos terminais": um objeto terminal é qualquer objeto de uma categoria para a qual exista exatamente uma seta de qualquer outro objeto (você pode verificar se isso vale para conjuntos de 1 elemento em Set ). Na teoria das categorias, os objetos terminais são simplesmente referidos como 1 ; eles são únicos até o isomorfismo, de modo que não faz sentido distingui-los. Portanto, agora temos uma descrição puramente teórica por categoria de "elementos de S " para qualquer S : são apenas as setas de 1 a S !
Tom Crockett

7
@TahirHassan - Para colocar isso em termos de Haskell, pense no fato de que se Sfor um tipo, tudo o que você pode fazer ao escrever uma função f :: () -> Sé escolher algum termo específico do tipo S(um "elemento", se desejar) e retornar isso ... você não recebeu informações reais sobre o argumento, portanto não há como alterar o comportamento da função. Portanto, fdeve ser uma função constante que apenas retorna a mesma coisa todas as vezes. ()("Unidade") é o objeto terminal da categoria Hask , e não é coincidência que exista exatamente 1 valor (não divergente) que o habita.
Tom Crockett

532

Intuitivamente, acho que o que o vocabulário matemático sofisticado está dizendo é o seguinte:

Monoid

Um monóide é um conjunto de objetos e um método para combiná-los. Monoides conhecidos são:

  • números que você pode adicionar
  • listas que você pode concatenar
  • define que você pode unir

Existem exemplos mais complexos também.

Além disso, todo monóide tem uma identidade , que é o elemento "no-op" que não tem efeito quando você o combina com outra coisa:

  • 0 + 7 == 7 + 0 == 7
  • [] ++ [1,2,3] == [1,2,3] ++ [] == [1,2,3]
  • {} união {maçã} == {maçã} união {} == {maçã}

Finalmente, um monóide deve ser associativo . (você pode reduzir uma longa sequência de combinações da maneira que quiser, desde que não altere a ordem dos objetos da esquerda para a direita). A adição está OK ((5 + 3) +1 == 5+ (3+ 1)), mas a subtração não é ((5-3) -1! = 5- (3-1)).

Mônada

Agora, vamos considerar um tipo especial de conjunto e uma maneira especial de combinar objetos.

Objetos

Suponha que seu conjunto contenha objetos de um tipo especial: funções . E essas funções têm uma assinatura interessante: elas não carregam números para números ou cadeias para cadeias. Em vez disso, cada função carrega um número para uma lista de números em um processo de duas etapas.

  1. Computar 0 ou mais resultados
  2. Combine esses resultados em uma única resposta de alguma forma.

Exemplos:

  • 1 -> [1] (basta quebrar a entrada)
  • 1 -> [] (descarte a entrada, coloque o nada em uma lista)
  • 1 -> [2] (adicione 1 à entrada e envolva o resultado)
  • 3 -> [4, 6] (adicione 1 à entrada e multiplique a entrada por 2 e agrupe os vários resultados )

Combinando Objetos

Além disso, nossa maneira de combinar funções é especial. Uma maneira simples de combinar função é composição : vamos pegar nossos exemplos acima e compor cada função consigo mesma:

  • 1 -> [1] -> [[1]] (enrole a entrada duas vezes)
  • 1 -> [] -> [] (descarte a entrada, embrulhe o nada em uma lista, duas vezes)
  • 1 -> [2] -> [UH-OH! ] (não podemos "adicionar 1" a uma lista! ")
  • 3 -> [4, 6] -> [UH-OH! ] (não podemos adicionar 1 a uma lista!)

Sem aprofundar a teoria dos tipos, o ponto é que você pode combinar dois números inteiros para obter um número inteiro, mas nem sempre é possível compor duas funções e obter uma função do mesmo tipo. (Funções com o tipo a -> a serão compostas, mas a-> [a] não será.)

Então, vamos definir uma maneira diferente de combinar funções. Quando combinamos duas dessas funções, não queremos "dobrar" os resultados.

Aqui está o que fazemos. Quando queremos combinar duas funções F e G, seguimos esse processo (chamado de ligação ):

  1. Calcule os "resultados" de F, mas não os combine.
  2. Calcule os resultados da aplicação de G a cada um dos resultados de F separadamente, produzindo uma coleção de coleção de resultados.
  3. Achate a coleção de dois níveis e combine todos os resultados.

De volta aos nossos exemplos, vamos combinar (vincular) uma função consigo mesma usando esta nova maneira de "vincular" funções:

  • 1 -> [1] -> [1] (enrole a entrada duas vezes)
  • 1 -> [] -> [] (descarte a entrada, embrulhe o nada em uma lista, duas vezes)
  • 1 -> [2] -> [3] (adicione 1, adicione 1 novamente e envolva o resultado.)
  • 3 -> [4,6] -> [5,8,7,12] (adicione 1 à entrada e também multiplique a entrada por 2, mantendo os dois resultados, depois faça tudo novamente nos dois resultados e, em seguida, finalize a final resulta em uma lista.)

Essa maneira mais sofisticada de combinar funções é associativa (seguindo como a composição das funções é associativa quando você não está fazendo o material de embalagem sofisticado).

Amarrando tudo junto,

  • mônada é uma estrutura que define uma maneira de combinar (os resultados de) funções,
  • analogamente a como um monóide é uma estrutura que define uma maneira de combinar objetos,
  • onde o método de combinação é associativo,
  • e onde existe um 'No-op' especial que pode ser combinado com qualquer coisa para resultar em algo inalterado.

Notas

Existem várias maneiras de "agrupar" os resultados. Você pode fazer uma lista, um conjunto ou descartar tudo, exceto o primeiro resultado, observando se não há resultados, anexar um sidecar de estado, imprimir uma mensagem de log, etc.

Eu brinquei um pouco com as definições na esperança de transmitir a idéia essencial intuitivamente.

Simplifiquei um pouco as coisas insistindo que nossa mônada opera em funções do tipo a -> [a] . De fato, as mônadas trabalham em funções do tipo a -> mb , mas a generalização é uma espécie de detalhe técnico que não é o principal insight.


22
Esta é uma boa explicação de como todas as mônadas constituem uma categoria (a categoria Kleisli é o que você está demonstrando - há também a categoria Eilenberg-Moore). Mas, devido ao fato de que você não pode compor quaisquer duas setas Kleisli a -> [b]e c -> [d](você só pode fazer isso se b= c), este não chega a descrever um monoid. Na verdade, é a operação de nivelamento que você descreveu, em vez da composição da função, que é o "operador monóide".
Tom Crockett

6
É verdade que se você limitasse uma mônada a apenas um tipo, ou seja, se você permitisse apenas as setas Kleisli do formulário a -> [a], isso seria um monóide (porque você reduziria a categoria Kleisli a um único objeto e qualquer categoria de apenas um objeto é por definição um monóide!), mas não capturaria toda a generalidade da mônada.
Tom Crockett

5
Na última nota, é bom lembrar que a -> [a] é apenas a -> [] a. ([] também é apenas um construtor de tipos.) E, portanto, não pode ser visto apenas como um -> mb, mas [] é realmente uma instância da classe Monad.
Evi1M4chine 31/03

8
Esta é a melhor e mais grokkable explicação das mônadas e seu histórico matemático de monoides que encontrei em literalmente semanas. Isto é o que deve ser impresso em todos os livros de Haskell quando se trata de mônadas, de mãos dadas. UPVOTE! Talvez obtenha mais informações, que as mônadas são realizadas como instâncias de classe tipográfica parametrizadas, envolvendo o que quer que sejam colocadas em haskell, no post. (Pelo menos é assim que eu entendia agora Me corrija se eu estiver errado Sé.. Haskell.org/haskellwiki/What_a_Monad_is_not )
sjas

1
Isso é fantástico - é a única explicação que entendi bem o suficiente para poder explicar para outra pessoa ... Mas ainda não entendo por que essa é uma maneira valiosa de pensar em algo. :(
Adam Barnes

84

Primeiro, as extensões e bibliotecas que vamos usar:

{-# LANGUAGE RankNTypes, TypeOperators #-}

import Control.Monad (join)

Destes, RankNTypesé o único que é absolutamente essencial para o abaixo. Certa vez, escrevi uma explicação de RankNTypesque algumas pessoas parecem ter achado úteis , então vou me referir a isso.

Citando a excelente resposta de Tom Crockett , temos:

Uma mônada é ...

  • Um endofuncor, T: X -> X
  • Uma transformação natural, μ: T × T -> T , onde × significa composição do funcionamento
  • Uma transformação natural, η: I -> T , onde eu sou o identificador final da identidade em X

... satisfazendo estas leis:

  • μ (μ (T × T) × T)) = μ (T × μ (T × T))
  • μ (η (T)) = T = μ (T (η))

Como traduzimos isso para o código Haskell? Bem, vamos começar com a noção de uma transformação natural :

-- | A natural transformations between two 'Functor' instances.  Law:
--
-- > fmap f . eta g == eta g . fmap f
--
-- Neat fact: the type system actually guarantees this law.
--
newtype f :-> g =
    Natural { eta :: forall x. f x -> g x }

Um tipo da forma f :-> gé análogo a um tipo de função, mas, em vez de pensar nela como uma função entre dois tipos (do tipo *), pense nele como um morfismo entre dois functores (cada um do tipo * -> *). Exemplos:

listToMaybe :: [] :-> Maybe
listToMaybe = Natural go
    where go [] = Nothing
          go (x:_) = Just x

maybeToList :: Maybe :-> []
maybeToList = Natural go
    where go Nothing = []
          go (Just x) = [x]

reverse' :: [] :-> []
reverse' = Natural reverse

Basicamente, em Haskell, transformações naturais são funções de algum tipo f xpara outro tipo, de g xmodo que a xvariável type é "inacessível" para o chamador. Por exemplo, sort :: Ord a => [a] -> [a]não pode ser transformado em uma transformação natural, porque é "exigente" sobre quais tipos podemos instanciar a. Uma maneira intuitiva que costumo usar para pensar sobre isso é o seguinte:

  • Um functor é uma maneira de operar o conteúdo de algo sem tocar na estrutura .
  • Uma transformação natural é uma maneira de operar na estrutura de algo sem tocar ou olhar para o conteúdo .

Agora, com isso fora do caminho, vamos abordar as cláusulas da definição.

A primeira cláusula é "um endofuncor, T: X -> X ". Bem, todo mundo Functorem Haskell é um endofuncor no que as pessoas chamam de "categoria Hask", cujos objetos são do tipo Haskell (do tipo *) e cujos morfismos são funções de Haskell. Parece uma afirmação complicada, mas na verdade é muito trivial. Tudo o que isso significa é que a Functor f :: * -> *fornece os meios de construir um tipo f a :: *para any a :: *e uma função a fmap f :: f a -> f bpartir de any f :: a -> b, e que eles obedecem às leis do functor.

Segunda cláusula: o Identityfunctor em Haskell (que acompanha a plataforma, para que você possa importá-lo) é definido desta maneira:

newtype Identity a = Identity { runIdentity :: a }

instance Functor Identity where
    fmap f (Identity a) = Identity (f a)

Portanto, a transformação natural η: I -> T da definição de Tom Crockett pode ser escrita dessa maneira para qualquer Monadinstância t:

return' :: Monad t => Identity :-> t
return' = Natural (return . runIdentity)

Terceira cláusula: A composição de dois functores em Haskell pode ser definida desta maneira (que também vem com a Plataforma):

newtype Compose f g a = Compose { getCompose :: f (g a) }

-- | The composition of two 'Functor's is also a 'Functor'.
instance (Functor f, Functor g) => Functor (Compose f g) where
    fmap f (Compose fga) = Compose (fmap (fmap f) fga)

Portanto, a transformação natural μ: T × T -> T da definição de Tom Crockett pode ser escrita assim:

join' :: Monad t => Compose t t :-> t
join' = Natural (join . getCompose)

A afirmação de que este é um monóide na categoria de endofunitores significa que Compose(parcialmente aplicado apenas aos dois primeiros parâmetros) é associativo, e esse Identityé o seu elemento de identidade. Ou seja, que os seguintes isomorfismos são válidos:

  • Compose f (Compose g h) ~= Compose (Compose f g) h
  • Compose f Identity ~= f
  • Compose Identity g ~= g

Isso é muito fácil de provar porque, Composee Identityambos são definidos como newtype, e os Relatórios Haskell definem a semântica newtypecomo um isomorfismo entre o tipo que está sendo definido e o tipo de argumento para o newtypeconstrutor de dados do. Então, por exemplo, vamos provar Compose f Identity ~= f:

Compose f Identity a
    ~= f (Identity a)                 -- newtype Compose f g a = Compose (f (g a))
    ~= f a                            -- newtype Identity a = Identity a
Q.E.D.

No Naturalnovo tipo, não consigo descobrir o que a (Functor f, Functor g)restrição está fazendo. Você poderia explicar?
dfeuer

@dfeuer Realmente não está fazendo nada essencial.
Luis Casillas

1
@LuisCasillas Eu removi essas Functorrestrições, pois elas não parecem necessárias. Se você não concordar, sinta-se à vontade para adicioná-los novamente.
Lambda Fairy

Você pode elaborar o que significa formalmente que o produto dos functores seja tomado como composição? Em particular, quais são os morfismos de projeção para a composição de funções? Meu palpite é que o produto é definido apenas para um functor F contra ele mesmo, F x F e somente quando joiné definido. E esse joiné o morfismo da projeção. Mas eu não tenho certeza.
Tksfz 01/04

6

Nota: Não, isso não é verdade. Em algum momento, houve um comentário sobre essa resposta do próprio Dan Piponi dizendo que a causa e o efeito aqui eram exatamente o oposto: ele escreveu seu artigo em resposta à piada de James Iry. Mas parece ter sido removido, talvez por alguma ordem compulsiva.

Abaixo está a minha resposta original.


É bem possível que Iry tenha lido From Monoids to Monads , um post no qual Dan Piponi (sigfpe) deriva mônadas de monoides em Haskell, com muita discussão sobre a teoria das categorias e menção explícita à "categoria dos endofuncionais em Hask ". De qualquer forma, qualquer pessoa que se pergunte o que significa uma mônada ser um monóide na categoria de endofuncionadores pode se beneficiar da leitura dessa derivação.


1
"Talvez por alguma ordem compulsiva" - ou, como nos referimos com carinho a eles neste site, um moderador :-).
Halfer 19/04

6

Cheguei a este post com o intuito de entender melhor a inferência da citação infame da Teoria da Categoria para o Matemático de Trabalho de Mac Lane .

Ao descrever o que é algo, geralmente é igualmente útil descrever o que não é.

O fato de Mac Lane usar a descrição para descrever uma Mônada pode implicar que ela descreve algo único para as Mônadas. Tenha paciencia comigo. Para desenvolver uma compreensão mais ampla da afirmação, acredito que precisa ficar claro que ele não está descrevendo algo que é exclusivo das mônadas; a declaração descreve igualmente Aplicante e Setas, entre outros. Pela mesma razão, podemos ter dois monoides em Int (soma e produto), podemos ter vários monoides em X na categoria de endofunitores. Mas há ainda mais semelhanças.

Tanto a Mônada quanto a Requerente atendem aos critérios:

  • endo => qualquer flecha ou morfismo que começa e termina no mesmo lugar
  • functor => qualquer flecha ou morfismo entre duas categorias

    (por exemplo, no dia a dia Tree a -> List b, mas na categoria Tree -> List)

  • monóide => objeto único; ou seja, um tipo único, mas neste contexto, apenas em relação à camada externa; então, não podemos ter Tree -> Listapenas List -> List.

A instrução usa "Category of ..." Isso define o escopo da instrução. Como um exemplo, a Categoria Functor descreve o âmbito de f * -> g *, ou seja, Any functor -> Any functor, por exemplo, Tree * -> List *ou Tree * -> Tree *.

O que uma declaração categórica não especifica descreve onde tudo é permitido .

Nesse caso, dentro dos functores, * -> *aka a -> bnão é especificado, o que significa Anything -> Anything including Anything else. Como minha imaginação pula para Int -> String, ela também inclui Integer -> Maybe Int, ou mesmo para Maybe Double -> Either String Intonde a :: Maybe Double; b :: Either String Int.

Portanto, a declaração vem da seguinte forma:

  • escopo do functor :: f a -> g b(ou seja, qualquer tipo parametrizado para qualquer tipo parametrizado)
  • endo + functor :: f a -> f b(ou seja, qualquer tipo parametrizado para o mesmo tipo parametrizado) ... disse de forma diferente,
  • um monóide na categoria de endofuncor

Então, onde está o poder dessa construção? Para apreciar toda a dinâmica, eu precisava ver que os desenhos típicos de um monóide (objeto único com o que parece uma flecha de identidade :: single object -> single object) não conseguem ilustrar que tenho permissão para usar uma flecha parametrizada com qualquer número de valores de monóide, do objeto de um tipo permitido no Monoid. A definição de equivalência da seta endo, ~ identity ignora o valor do tipo do functor e o tipo e o valor da camada mais interna de "carga útil". Assim, a equivalência retorna trueem qualquer situação em que os tipos funcionais correspondam (por exemplo, Nothing -> Just * -> Nothingé equivalente a Just * -> Just * -> Just *porque são os dois Maybe -> Maybe -> Maybe).

Barra lateral: ~ fora é conceitual, mas é o símbolo mais à esquerda f a. Também descreve o que "Haskell" lê primeiro (figura grande); então Type é "fora" em relação a um Type Value. O relacionamento entre as camadas (uma cadeia de referências) na programação não é fácil de se relacionar em Categoria. A categoria de conjunto é usada para descrever tipos (int, strings, talvez int etc.), que inclui a categoria de functor (tipos parametrizados). A cadeia de referência: Tipo de Functor, valores do Functor (elementos do conjunto desse Functor, por exemplo, Nothing, Just) e, por sua vez, tudo o que cada valor do functor aponta. Em Categoria, o relacionamento é descrito de maneira diferente, por exemplo, return :: a -> m aé considerada uma transformação natural de um Functor para outro Functor, diferente de qualquer coisa mencionada até agora.

De volta ao thread principal, no geral, para qualquer produto tensor definido e um valor neutro, a instrução acaba descrevendo uma construção computacional incrivelmente poderosa nascida de sua estrutura paradoxal:

  • por fora, aparece como um único objeto (por exemplo, :: List); estático
  • mas por dentro, permite muita dinâmica
    • qualquer número de valores do mesmo tipo (por exemplo, Empty | ~ NonEmpty) como forragem para funções de qualquer aridade. O produto tensorial reduzirá qualquer número de entradas para um único valor ... para a camada externa (~ foldque não diz nada sobre a carga útil)
    • gama infinita de tanto o tipo e os valores para a camada mais interna

Em Haskell, esclarecer a aplicabilidade da declaração é importante. O poder e a versatilidade dessa construção não têm absolutamente nada a ver com uma mônada em si . Em outras palavras, a construção não depende do que torna uma mônada única.

Ao tentar descobrir se o código deve ser construído com um contexto compartilhado para suportar cálculos que dependem um do outro, em comparação com cálculos que podem ser executados em paralelo, essa infame declaração, tanto quanto descreve, não é um contraste entre a escolha de Aplicável, Setas e Mônadas, mas é uma descrição de quanto elas são iguais. Para a decisão em mãos, a declaração é discutível.

Isso geralmente é mal compreendido. A declaração continua a descrever join :: m (m a) -> m acomo o produto tensorial do endofuncor monoidal. No entanto, não articula como, no contexto desta declaração, (<*>)também poderia ter sido escolhido. É realmente um exemplo de seis / meia dúzia. A lógica para combinar valores é exatamente igual; a mesma entrada gera a mesma saída de cada uma (diferente dos monoides Sum e Product para Int porque eles geram resultados diferentes ao combinar Ints).

Então, para recapitular: Um monóide na categoria de endofunitores descreve:

   ~t :: m * -> m * -> m *
   and a neutral value for m *

(<*>)e (>>=)ambos fornecem acesso simultâneo aos dois mvalores para calcular o valor de retorno único. A lógica usada para calcular o valor de retorno é exatamente a mesma. Se não fossem as diferentes formas das funções que eles parametrizam ( f :: a -> bversus k :: a -> m b) e a posição do parâmetro com o mesmo tipo de retorno da computação (ou seja, a -> b -> bversus b -> a -> bpara cada um respectivamente), suspeito que poderíamos ter parametrizado a lógica monoidal, o produto tensorial, para reutilização em ambas as definições. Como um exercício para explicar, tente e implemente ~t, e você termina (<*>)e (>>=)depende de como decide defini-lo forall a b.

Se meu último ponto é no mínimo conceitualmente verdadeiro, ele explica a diferença precisa e única computacional entre Aplicável e Mônada: as funções que eles parametrizam. Em outras palavras, a diferença é externa à implementação dessas classes de tipos.

Concluindo, em minha própria experiência, a citação infame de Mac Lane forneceu um ótimo meme "goto", um guia para eu fazer referência enquanto navegava na Categoria para entender melhor os idiomas usados ​​em Haskell. Ele consegue capturar o escopo de uma poderosa capacidade de computação tornada maravilhosamente acessível em Haskell.

No entanto, há uma ironia em como eu entendi errado a aplicabilidade da declaração fora da mônada, e o que espero transmitir aqui. Tudo o que ele descreve acaba sendo o mesmo entre Aplicável e Mônadas (e Flechas, entre outros). O que não diz é precisamente a pequena mas útil distinção entre eles.

- E


5

As respostas aqui fazem um excelente trabalho na definição de monóides e mônadas, no entanto, elas ainda não parecem responder à pergunta:

E, em uma nota menos importante, isso é verdade e, em caso afirmativo, você poderia dar uma explicação (espero que possa ser entendida por alguém que não tenha muita experiência com Haskell)?

O cerne da questão que falta aqui é a noção diferente de "monóide", a chamada categorização mais precisamente - a de monóide em uma categoria monoidal. Infelizmente, o próprio livro de Mac Lane o torna muito confuso :

No total, uma mônada Xé apenas um monóide na categoria de endofunitores de X, com o produto ×substituído pela composição dos endofunitores e a unidade definida pelo endofuncor de identidade.

Confusão principal

Por que isso é confuso? Porque não define o que é "monóide na categoria de endofunitores" de X. Em vez disso, essa sentença sugere levar um monóide dentro do conjunto de todos os endofunitores junto com a composição do functor como operação binária e o functor de identidade como uma unidade monoidal. O que funciona perfeitamente bem e transforma em monóide qualquer subconjunto de endofunitores que contém o functor de identidade e é fechado na composição do functor.

No entanto, essa não é a interpretação correta, que o livro deixa de esclarecer nessa fase. Uma Mônada fé um endofuncor fixo , não um subconjunto de endofuncores fechado sob composição. Uma construção comum é a utilização fpara gerar uma monóide, tendo o conjunto de todas as kcomposições vezes de f^k = f(f(...))de fcom ele mesmo, incluindo k=0que corresponde à identidade f^0 = id. E agora o conjunto Sde todos esses poderes para todos k>=0é de fato um monóide "com o produto × substituído pela composição dos endofunitores e pela unidade definida pelo endofuncor de identidade".

E ainda:

  • Este monóide Spode ser definido para qualquer functor fou mesmo literalmente para qualquer auto-mapa de X. É o monóide gerado por f.
  • A estrutura monoidal Sdada pela composição do functor e pelo functor de identidade não tem nada a ver com fser ou não ser uma mônada.

E para tornar as coisas mais confusas, a definição de "monóide na categoria monoidal" vem mais adiante neste livro, como você pode ver no índice . E, no entanto, entender essa noção é absolutamente crítico para entender a conexão com as mônadas.

Categorias monoidais (estritas)

Indo para o capítulo VII sobre monóides (que vem depois do capítulo VI sobre mônadas), encontramos a definição da chamada categoria monoidal estrita como tripla (B, *, e), onde Bé uma categoria, *: B x B-> Bum bifunctor (functor em relação a cada componente com outro componente fixo) ) e eé um objeto de unidade que Bsatisfaz as leis de associatividade e de unidade:

(a * b) * c = a * (b * c)
a * e = e * a = a

para quaisquer objetos a,b,cde B, e as mesmas identidades para qualquer morphisms a,b,ccom esubstituídas por id_e, o morfismo identidade e. Agora é instrutivo observar que, no nosso caso de interesse, onde Bestá a categoria de endofundeiros Xcom transformações naturais como morfismos, *composição de functor e efunctor de identidade, todas essas leis são cumpridas, como podem ser verificadas diretamente.

O que vem depois no livro é a definição da categoria monoidal "relaxada" , em que as leis mantêm apenas algumas transformações naturais fixas que satisfazem as chamadas relações de coerência , o que, no entanto, não é importante para os nossos casos das categorias de endofuncores.

Monoides em categorias monoidais

Finalmente, na seção 3 "Monoids" do capítulo VII, a definição real é dada:

Um monóide cem uma categoria monoidal (B, *, e)é um objeto Bcom duas setas (morfismos)

mu: c * c -> c
nu: e -> c

fazendo 3 diagramas comutativos. Lembre-se que, no nosso caso, estes são morphisms na categoria de endofunctors, que são transformações naturais correspondentes a precisão joine returnpara uma mônada. A conexão se torna ainda mais clara quando tornamos a composição *mais explícita, substituindo c * cpor c^2onde cestá nossa mônada.

Finalmente, observe que os três diagramas comutativos (na definição de um monóide na categoria monoidal) são escritos para categorias monoidais gerais (não estritas), enquanto no nosso caso todas as transformações naturais que surgem como parte da categoria monoidal são na verdade identidades. Isso tornará os diagramas exatamente iguais aos da definição de mônada, completando a correspondência.

Conclusão

Em resumo, qualquer mônada é, por definição, um endofuncor, portanto, um objeto na categoria de endofunitores, em que o monádico joine os returnoperadores satisfazem a definição de um monóide nessa categoria monoidal (estrita) específica . Vice-versa, qualquer monóide na categoria monoidal de endofuncores é, por definição, um triplo que (c, mu, nu)consiste em um objeto e duas setas, por exemplo, transformações naturais em nosso caso, que cumprem as mesmas leis que uma mônada.

Finalmente, observe a principal diferença entre os monóides (clássicos) e os monóides mais gerais nas categorias monoidais. As duas setas mue nuacima não são mais uma operação binária e uma unidade em um conjunto. Em vez disso, você tem um endofunctor fixo c. A composição do functor *e o functor de identidade por si só não fornecem a estrutura completa necessária para a mônada, apesar dessa observação confusa no livro.

Outra abordagem seria para comparar com o monoid padrão Cde todas as auto-aplicações de um conjunto A, onde a operação binária é a composição, que pode ser visto para mapear o produto cartesiano padrão C x Cem C. Passando para o monóide categorizado, estamos substituindo o produto cartesiano xpela composição de functor *e a operação binária é substituída pela transformação natural mude c * cpara c, que é uma coleção de joinoperadores

join: c(c(T))->c(T)

para cada objeto T(digite na programação). E os elementos de identidade nos monoides clássicos, que podem ser identificados com imagens de mapas de um conjunto fixo de um ponto, são substituídos pela coleção dos returnoperadores

return: T->c(T) 

Mas agora não há mais produtos cartesianos, portanto não há pares de elementos e, portanto, nenhuma operação binária.


Então, qual é a sua resposta para a parte "isso é verdade" da pergunta? É verdade que uma mônada é um monóide na categoria de endofunitores? E se sim, qual é a relação entre a noção da teoria da categoria de um monóide e um monóide algébrico (um conjunto com uma multiplicação associativa e uma unidade)?
Alexander Belopolsky 28/03
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.