Como você pronuncia essas funções na typeclass Applicative:
(<*>) :: f (a -> b) -> f a -> f b
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
(Ou seja, se eles não fossem operadores, como poderiam ser chamados?)
Como uma observação lateral, se você pudesse renomear pure
para algo mais amigável para não matemáticos, como você o chamaria?
pure
pode sermakeApplicative
.pure
sugestão como uma resposta e eu irei votar a favor de vocêRespostas:
Saber sua matemática, ou não, é irrelevante aqui, eu acho. Como você provavelmente sabe, Haskell empresta alguns bits de terminologia de vários campos da matemática abstrata, mais notavelmente a Teoria das Categorias , de onde obtemos functores e mônadas. O uso desses termos em Haskell diverge um pouco das definições matemáticas formais, mas eles geralmente são próximos o suficiente para serem bons termos descritivos de qualquer maneira.
A
Applicative
classe de tipo fica em algum lugar entreFunctor
eMonad
, portanto, seria de se esperar que tivesse uma base matemática semelhante. A documentação doControl.Applicative
módulo começa com:Hmm.
Não tão cativante quanto
Monad
, eu acho.Tudo isso basicamente se resume a que
Applicative
não corresponde a nenhum conceito que seja particularmente interessante matematicamente, então não há termos prontos por aí que capturem a forma como é usado em Haskell. Portanto, deixe a matemática de lado por enquanto.Se quisermos saber como chamá-
(<*>)
lo, talvez seja útil saber o que significa basicamente.Então, o que há com
Applicative
, de qualquer maneira, e por que não podemos chamá-lo assim?Applicative
Na prática, o que equivale a uma maneira de elevar funções arbitrárias a umFunctor
. Considere a combinação deMaybe
(sem dúvida o não trivial mais simplesFunctor
) eBool
(da mesma forma o tipo de dados não trivial mais simples).A função
fmap
permite-nos levantarnot
de trabalhar emBool
para trabalhar emMaybe Bool
. Mas e se quisermos levantar(&&)
?Bem, não é isso que queremos de forma alguma ! Na verdade, é praticamente inútil. Podemos tentar ser inteligente e sneak outro
Bool
paraMaybe
através da parte traseira ...... mas isso não é bom. Por um lado, está errado. Por outro lado, é feio . Poderíamos continuar tentando, mas descobrimos que não há como levantar uma função de vários argumentos para trabalhar em um arbitrário
Functor
. Irritante!Por outro lado, poderíamos fazer isso facilmente se
Maybe
usássemos aMonad
instância de:Agora, é muito complicado traduzir uma função simples - é por isso que
Control.Monad
fornece uma função para fazer isso automaticamenteliftM2
,. O 2 em seu nome se refere ao fato de que ele funciona em funções de exatamente dois argumentos; funções semelhantes existem para funções de 3, 4 e 5 argumentos. Essas funções são melhores , mas não perfeitas, e especificar o número de argumentos é feio e desajeitado.O que nos leva ao artigo que introduziu a classe do tipo Aplicativa . Nele, os autores fazem essencialmente duas observações:
Functor
é uma coisa muito natural de se fazerMonad
A aplicação da função normal é escrita pela simples justaposição de termos, então para tornar a "aplicação elevada" o mais simples e natural possível, o artigo apresenta operadores infixados para substituir a aplicação, elevada para o
Functor
, e uma classe de tipo para fornecer o que é necessário para isso .Tudo isso nos leva ao seguinte ponto:
(<*>)
simplesmente representa a aplicação da função - então por que pronunciá-lo de forma diferente do que você faz com o "operador de justaposição" de espaço em branco?Mas se isso não for muito satisfatório, podemos observar que o
Control.Monad
módulo também fornece uma função que faz a mesma coisa para mônadas:Onde
ap
, é claro, é a abreviação de "aplicar". Como qualquer umMonad
pode serApplicative
, eap
precisa apenas do subconjunto de recursos presentes neste último, talvez possamos dizer que, se(<*>)
não fosse um operador, deveria ser chamadoap
.Também podemos abordar as coisas de outra direção. A
Functor
operação de levantamento é chamadafmap
porque é uma generalização damap
operação em listas. Que tipo de função nas listas funcionaria(<*>)
? Há o queap
funciona nas listas, é claro, mas isso não é particularmente útil por si só.Na verdade, talvez haja uma interpretação mais natural para listas. O que vem à mente quando você olha a seguinte assinatura de tipo?
Há algo muito tentador na ideia de alinhar as listas em paralelo, aplicando cada função da primeira ao elemento correspondente da segunda. Infelizmente para nosso velho amigo
Monad
, essa operação simples viola as leis da mônada se as listas tiverem comprimentos diferentes. Mas vale uma multaApplicative
, caso em que(<*>)
se torna uma forma de encadear uma versão generalizada dezipWith
, então talvez possamos imaginar chamá-lafzipWith
?Esta ideia compactada realmente nos traz um círculo completo. Lembra daquela coisa de matemática anterior, sobre functores monoidais? Como o nome sugere, essas são uma forma de combinar a estrutura de monoides e functores, ambos os quais são classes do tipo Haskell familiares:
Como eles ficariam se você os colocasse em uma caixa e sacudisse um pouco? A partir de
Functor
, manteremos a ideia de uma estrutura independente de seu parâmetro de tipo e deMonoid
manteremos a forma geral das funções:Não queremos presumir que existe uma maneira de criar um verdadeiro "vazio"
Functor
e não podemos conjurar um valor de um tipo arbitrário, portanto, corrigiremos o tipo demfEmpty
asf ()
.Também não queremos forçar
mfAppend
a necessidade de um parâmetro de tipo consistente, então agora temos isto:Qual é o tipo de resultado
mfAppend
? Temos dois tipos arbitrários dos quais nada sabemos, portanto, não temos muitas opções. O mais sensato é apenas manter ambos:Nesse ponto
mfAppend
agora é claramente uma versão generalizada dezip
nas listas, e podemos reconstruirApplicative
facilmente:Isso também nos mostra que
pure
está relacionado ao elemento de identidade de aMonoid
, então outros nomes bons para ele podem ser qualquer coisa que sugira um valor unitário, uma operação nula ou algo parecido.Isso foi demorado, para resumir:
(<*>)
é apenas um aplicativo de função modificada, então você pode lê-lo como "ap" ou "aplicar", ou elimina-o inteiramente da maneira que faria com um aplicativo de função normal.(<*>)
também generalizazipWith
em listas, de modo que você pode lê-lo como "zip functores com", similarmente a lerfmap
como "mapear um functor com".O primeiro está mais próximo da intenção da
Applicative
classe de tipo - como o nome sugere - então é isso que eu recomendo.Na verdade, eu encorajo o uso liberal e a não pronúncia de todos os operadores de aplicativos suspensos :
(<$>)
, que transforma uma função de argumento único em umFunctor
(<*>)
, que encadeia uma função com vários argumentos por meio de umApplicative
(=<<)
, que vincula uma função que insere umMonad
em um cálculo existenteTodos os três são, no fundo, apenas aplicações de funções regulares, um pouco apimentadas.
fonte
Applicative
e o estilo idiomático funcional que ele promove não conseguem amor suficiente, então não resisti à chance de exaltar um pouco suas virtudes como um meio de explicar como eu (não) me pronuncio(<*>)
.Applicative
's! Algo assim[| f a b c d |]
(como sugerido pelo artigo original). Então não precisaríamos do<*>
combinador e você se referiria a tal expressão como um exemplo de "aplicação de função em um contexto funcional"Monad
. OuFunctor
ouMonoid
ou qualquer outra coisa que tenha um termo bem estabelecido envolvendo menos de três adjetivos. "Aplicativo" é meramente um nome pouco inspirador, embora razoavelmente descritivo, colocado em algo que precisava de um.Como não tenho ambições de melhorar a resposta técnica do CA McCann , vou abordar a mais fofa:
Como alternativa, especialmente porque não há fim para as constantes angústias e traições gritadas contra a
Monad
versão, chamada "return
", proponho outro nome, que sugere sua função de uma forma que pode satisfazer o mais imperativo dos programadores imperativos e, o mais funcional de ... bem, espero que, todos podem reclamar o mesmo sobre:inject
.Pegue um valor. "Inject"-lo no
Functor
,Applicative
,Monad
, ou que-ter-você. Eu voto em "inject
" e aprovei esta mensagem.fonte
inject
é um nome excelente e provavelmente melhor que o meu, embora como uma observação secundária, "injetar" seja usado em - eu acho - Smalltalk e Ruby para algum tipo de método de dobra à esquerda. Eu nunca entendi essa escolha de nome, porém ...inject
em Ruby & Smalltalk é usado porque é como se você estivesse "injetando" um operador entre cada elemento da lista. Pelo menos, é assim que sempre pensei nisso.foldr
. (Você substitui(:)
e[]
, onde(:)
recebe 2 args e[]
é uma constante, portanto,foldr (+) 0 (1:2:3:[])
↝1+2+3+0
.)Bool
Ele é apenasif
-then
-else
(duas constantes, escolha uma) e paraMaybe
isso é chamadomaybe
... Haskell não tem um único nome / função para isso, pois todos têm tipos diferentes (em geral, elim é apenas recursão / indução)Em resumo:
<*>
você pode chamá-lo de aplicável . Portanto,Maybe f <*> Maybe a
pode ser pronunciado como aplicar aoMaybe f
longoMaybe a
.Você pode renomear
pure
paraof
, como muitas bibliotecas JavaScript fazem. Em JS você pode criar umMaybe
comMaybe.of(a)
.Além disso, o wiki de Haskell tem uma página sobre a pronúncia dos operadores de idioma aqui
fonte
Fonte: Haskell Programming from First Principles , de Chris Allen e Julie Moronuki
fonte