Para respeitar os leitores rápidos, começo primeiro por uma definição precisa, continuo com uma explicação mais rápida e simples em inglês e depois passo para exemplos.
Aqui está uma definição concisa e precisa, ligeiramente reformulada:
Uma mônada (em ciência da computação) é formalmente um mapa que:
envia todo tipo Xde alguma linguagem de programação para um novo tipo T(X)(chamado "tipo de T-computações com valores em X");
equipado com uma regra para compor duas funções da forma
f:X->T(Y)e g:Y->T(Z)para uma função g∘f:X->T(Z);
de uma maneira associativa no sentido evidente e unital em relação a uma determinada função unitária chamada pure_X:X->T(X), a ser considerada como tendo um valor para a computação pura que simplesmente retorna esse valor.
Portanto, em palavras simples, uma mônada é uma regra para passar de qualquer tipo Xpara outro tipoT(X) , e uma regra para passar de duas funções f:X->T(Y)e g:Y->T(Z)(que você gostaria de compor, mas não pode) para uma nova funçãoh:X->T(Z) . Que, no entanto, não é a composição em sentido matemático estrito. Estamos basicamente "dobrando" a composição da função ou redefinindo como as funções são compostas.
Além disso, exigimos que a regra de composição da mônada satisfaça os axiomas matemáticos "óbvios":
- Associatividade : Compor
fcom ge depois com h(de fora) deve ser o mesmo que compor gcom he depois com f(de dentro).
- Propriedade unital : a composição
fda função de identidade de ambos os lados deve render f.
Novamente, em palavras simples, não podemos simplesmente enlouquecer redefinindo nossa composição de funções como gostamos:
- Primeiro, precisamos da associatividade para poder compor várias funções em uma linha
f(g(h(k(x))), por exemplo , e não nos preocupar em especificar os pares de funções de composição da ordem. Como a regra da mônada apenas prescreve como compor um par de funções , sem esse axioma, precisaríamos saber qual par é composto primeiro e assim por diante. (Observe que é diferente da propriedade de comutatividade que fcompôs com gera a mesma que gcompôs com f, o que não é necessário).
- E segundo, precisamos da propriedade unital, que é simplesmente dizer que as identidades compõem trivialmente a maneira como as esperamos. Assim, podemos refatorar funções com segurança sempre que essas identidades puderem ser extraídas.
Então, novamente em resumo: uma mônada é a regra das funções de extensão e composição de tipos que satisfazem os dois axiomas - associatividade e propriedade unital.
Em termos práticos, você deseja que a mônada seja implementada para você pelo idioma, compilador ou estrutura que cuidaria das funções de composição para você. Portanto, você pode se concentrar em escrever a lógica de sua função, em vez de se preocupar em como a execução deles é implementada.
É basicamente isso, em poucas palavras.
Sendo matemático profissional, prefiro evitar chamar ha "composição" de fe g. Porque matematicamente, não é. Chamá-lo de "composição" pressupõe incorretamente que hé a verdadeira composição matemática, o que não é. Nem sequer é determinado exclusivamente por fe g. Em vez disso, é o resultado da nova "regra de composição" de nossa mônada. O que pode ser totalmente diferente da composição matemática real, mesmo que exista!
Para torná-lo menos seco, deixe-me tentar ilustrá-lo pelo exemplo que estou anotando em seções pequenas, para que você possa pular direto ao ponto.
Exceção lançada como exemplos da Mônada
Suponha que desejemos compor duas funções:
f: x -> 1 / x
g: y -> 2 * y
Mas f(0)não está definido, portanto, uma exceção eé lançada. Então, como você pode definir o valor composicional g(f(0))? Lance uma exceção novamente, é claro! Talvez o mesmo e. Talvez uma nova exceção atualizada e1.
O que exatamente acontece aqui? Primeiro, precisamos de novos valores de exceção (diferentes ou iguais). Você pode chamá-los nothingou nullqualquer outra coisa, mas a essência permanece a mesma - eles devem ser novos valores, por exemplo, não deve ser um numberem nosso exemplo aqui. Prefiro não ligar para eles nullpara evitar confusão com como nullpode ser implementado em qualquer idioma específico. Da mesma forma, prefiro evitar, nothingporque muitas vezes está associado a null, o que, em princípio, é o que nulldeve fazer; no entanto, esse princípio geralmente é desviado por quaisquer razões práticas.
O que é exceção exatamente?
Este é um assunto trivial para qualquer programador experiente, mas gostaria de soltar algumas palavras apenas para extinguir qualquer worm de confusão:
Exceção é um objeto que encapsula informações sobre como ocorreu o resultado inválido da execução.
Isso pode variar desde jogar fora todos os detalhes e retornar um único valor global (como NaNou null) ou gerar uma longa lista de logs ou o que exatamente aconteceu, enviá-lo para um banco de dados e replicar por toda a camada de armazenamento de dados distribuídos;)
A diferença importante entre esses dois exemplos extremos de exceção é que, no primeiro caso, não há efeitos colaterais . No segundo existem. O que nos leva à questão (de mil dólares):
As exceções são permitidas em funções puras?
Resposta mais curta : Sim, mas apenas quando eles não causam efeitos colaterais.
Resposta mais longa. Para ser pura, a saída da sua função deve ser determinada exclusivamente por sua entrada. Portanto, alteramos nossa função fenviando 0para o novo valor abstrato eque chamamos de exceção. Garantimos que o valor enão contenha informações externas que não sejam determinadas exclusivamente por nossa entrada, o que é x. Então, aqui está um exemplo de exceção sem efeito colateral:
e = {
type: error,
message: 'I got error trying to divide 1 by 0'
}
E aqui está um com efeito colateral:
e = {
type: error,
message: 'Our committee to decide what is 1/0 is currently away'
}
Na verdade, só tem efeitos colaterais se essa mensagem puder mudar no futuro. Mas se é garantido que nunca mude, esse valor se tornará previsível de forma única e, portanto, não haverá efeito colateral.
Para torná-lo ainda mais idiota. Uma função que retorna 42sempre é claramente pura. Mas se alguém louco decide criar 42uma variável com esse valor alterado, a mesma função deixa de ser pura sob as novas condições.
Observe que estou usando a notação literal de objeto por simplicidade para demonstrar a essência. Infelizmente, as coisas estão bagunçadas em linguagens como JavaScript, onde errornão é um tipo que se comporta da maneira que queremos aqui em relação à composição de funções, enquanto tipos reais gostam nullou NaNnão se comportam dessa maneira, mas passam por algumas artificiais e nem sempre intuitivas digitar conversões.
Extensão de tipo
Como queremos variar a mensagem dentro de nossa exceção, estamos realmente declarando um novo tipo Epara todo o objeto de exceção e, então, é isso que maybe numberacontece, além de seu nome confuso, que deve ser do tipo numberou do novo tipo de exceção E, então é realmente a união number | Ede numbere E. Em particular, depende de como queremos construir E, o que não é sugerido nem refletido no nome maybe number.
O que é composição funcional?
É os matemáticos funções operação de tomada
f: X -> Ye g: Y -> Ze construção de sua composição como função h: X -> Zsatisfazendo h(x) = g(f(x)). O problema com esta definição ocorre quando o resultado f(x)não é permitido como argumento de g.
Em matemática, essas funções não podem ser compostas sem trabalho extra. A solução estritamente matemática para o exemplo acima de fe gé remover 0do conjunto de definições de f. Com esse novo conjunto de definições (novo tipo mais restritivo de x), ftorna-se passível de composição g.
No entanto, não é muito prático em programação restringir o conjunto de definições desse ftipo. Em vez disso, exceções podem ser usadas.
Ou como uma outra abordagem, os valores artificiais são criados como NaN, undefined, null, Infinityetc. Então você avaliar 1/0a Infinitye 1/-0a -Infinity. E, em seguida, force o novo valor de volta à sua expressão, em vez de lançar a exceção. Levando a resultados que você pode ou não achar previsível:
1/0 // => Infinity
parseInt(Infinity) // => NaN
NaN < 0 // => false
false + 1 // => 1
E estamos de volta aos números regulares prontos para seguir em frente;)
O JavaScript nos permite continuar executando expressões numéricas a qualquer custo, sem gerar erros, como no exemplo acima. Isso significa que também permite compor funções. Qual é exatamente o objetivo da mônada - é uma regra compor funções que satisfaçam os axiomas, conforme definido no início desta resposta.
Mas a regra da função de composição, decorrente da implementação do JavaScript para lidar com erros numéricos, é uma mônada?
Para responder a essa pergunta, tudo que você precisa é verificar os axiomas (deixados como exercício como parte da pergunta aqui;).
A exceção de lançamento pode ser usada para construir uma mônada?
De fato, uma mônada mais útil seria a regra que prescreve que, se flança exceção para alguns x, o mesmo ocorre com sua composição g. Além disso, torne a exceção Eglobalmente única, com apenas um valor possível ( objeto terminal na teoria das categorias). Agora, os dois axiomas são verificáveis instantaneamente e temos uma mônada muito útil. E o resultado é o que é conhecido como a mônada talvez .