
[ad_1]
Quando um programa precisa trabalhar com diferentes sistemas de unidades, é melhor usar consistentemente um sistema para todos os cálculos internos e converter para outro sistema de saída, se necessário. Seguir rigidamente esta convenção pode prevenir bugs, como aquele que causou a queda do Mars Climate Orbiter.
Por exemplo, talvez você precise trabalhar em graus e radianos. Seria sensato fazer todos os cálculos em radianos, porque é isso que as bibliotecas de software esperam, e produzir os resultados em graus, porque é isso que os humanos esperam.
Agora suponha que você tenha uma função que pega um comprimento e o dobra, e outra função pega um comprimento e o triplica. Ambas as funções medem o comprimento em quilômetros, mas imprimem o resultado em milhas.
Você gostaria que a composição das duas funções multiplicasse um comprimento por seis. E, como antes, a composição receberia uma velocidade em quilômetros e retornaria uma velocidade em milhas.
Veja como poderíamos implementar isso mal.
miles_per_km = 5/8 # approx def double(length_km): return 2*length_km*miles_per_km def triple(length_km): return 3*length_km*miles_per_km length_km = 8 d = double(length_km) print("Double: ", d) t = triple(d) print("Triple: ", t)
isso imprime
Double: 10.0 Triple: 18.75
A segunda saída deve ser 30, não 18,5. O resultado está errado porque convertemos de quilômetros para milhas duas vezes. A implementação correta seria algo como o seguinte.
miles_per_km = 0.6213712 def double(length_km): d = 2*length_km print("Double: ", d*miles_per_km) return d def triple(length_km): t = 3*length_km print("Triple: ", t*miles_per_km) return t length_km = 8 d = double(length_km) t = triple(d)
Isso imprime o resultado correto.
Double: 10.0 Triple: 30.0
Em termos abstratos, não queremos a composição de f e g ser simplesmente g ∘ f.
Nós temos uma função f a partir de x para Y que consideramos ser nossa função principal e uma função T que traduz a saída. Dizer f dobra sua entrada e T traduz de quilômetros para milhas. Deixar f* ser a função que leva x para TYou seja, a combinação de f e tradução.
Agora pegue outra função g a partir de Y para Z e definir g* como a função que leva Y para TZ. Queremos a composição de f* e g* ser
g* ∘ f* = T ∘ g ∘ f.
No exemplo acima, queremos converter de quilômetros para milhas apenas uma vez. É exatamente isso que a composição de Kleisli faz. (“Kleisli” rima com “altamente.”)
A composição de Kleisli é conceitualmente simples. Depois de entender o que é, provavelmente você pode pensar em momentos em que era o que você queria, mas não tinha um nome para isso.
Escrever código para encapsular a composição Kleisli requer alguma infraestrutura (ou seja, monads), e isso é um pouco complicado, mas a ideia do que você está tentando alcançar não é. Observe no exemplo acima, o que as funções imprimem não é o que elas retornam; as instruções de impressão são uma espécie de canal lateral. Essa é a marca de uma mônada.
categorias Kleisli
As coisas sobre as quais falamos são formalizadas em termos de categorias Kleisli. Você começa com uma categoria C e definir outra categoria que tenha os mesmos objetos que C faz, mas tem uma noção diferente de composição, ou seja, composição de Kleisli.
Dada uma mônada T sobre Ca categoria Kleisli CT tem os mesmos objetos que C. Uma flecha f* a partir de x para Y dentro CT corresponde a uma seta f a partir de x para TY dentro C. Em símbolos,
homCT(x, Y) = HomC(x, TY).
A motivação do Sr. Kleisli para definir suas categorias foi responder a uma questão mais teórica – se todas as mônadas surgem de adjunções – mas, de forma mais prática, podemos pensar nas categorias de Kleisli como uma forma de formalizar uma variação na composição da função.
Postagens relacionadas
[ad_2]
Source link