¿Cómo se pronuncian estas funciones en la clase de tipos Applicative?
(<*>) :: f (a -> b) -> f a -> f b
(*>) :: f a -> f b -> f b
(<*) :: f a -> f b -> f a
(Es decir, si no fueran operadores, ¿cómo se llamarían?)
Como nota al margen, si pudieras cambiar el nombre pure
a algo más amigable para los no matemáticos, ¿cómo lo llamarías?
pure
podría sermakeApplicative
.pure
sugerencia como respuesta y te votaréRespuestas:
Saber sus matemáticas, o no, es en gran parte irrelevante aquí, creo. Como probablemente sepa, Haskell toma prestados algunos fragmentos de terminología de varios campos de las matemáticas abstractas, sobre todo la teoría de categorías , de donde obtenemos functores y mónadas. El uso de estos términos en Haskell difiere un poco de las definiciones matemáticas formales, pero por lo general están lo suficientemente cerca como para ser buenos términos descriptivos de todos modos.
La
Applicative
clase de tipo se encuentra en algún lugar entreFunctor
yMonad
, por lo que uno esperaría que tuviera una base matemática similar. La documentación delControl.Applicative
módulo comienza con:Hmm.
No es tan pegadizo como
Monad
, creo.Básicamente, todo esto se reduce a que
Applicative
no corresponde a ningún concepto que sea particularmente interesante matemáticamente, por lo que no hay términos prefabricados que capturen la forma en que se usa en Haskell. Por lo tanto, deje las matemáticas a un lado por ahora.Si queremos saber cómo llamarlo
(<*>)
, puede ser útil saber qué significa básicamente.Entonces, ¿qué pasa
Applicative
, de todos modos, y por qué lo llamamos así?En la
Applicative
práctica, lo que equivale a una forma de convertir funciones arbitrarias en aFunctor
. Considere la combinación deMaybe
(posiblemente el tipo de datos no trivial más simpleFunctor
) yBool
(del mismo modo, el tipo de datos no trivial más simple).La función
fmap
nos permite pasarnot
de trabajarBool
a trabajarMaybe Bool
. ¿Pero y si queremos levantarnos(&&)
?Bueno, ¡eso no es lo que queremos en absoluto ! De hecho, es bastante inútil. Podemos tratar de ser inteligentes y colarse otra
Bool
enMaybe
por la parte posterior ...... pero eso no es bueno. Por un lado, está mal. Por otra parte, es feo . Podríamos seguir intentándolo, pero resulta que no hay forma de levantar una función de múltiples argumentos para trabajar en un arbitrario
Functor
. ¡Molesto!Por otro lado, podríamos hacerlo fácilmente si usamos
Maybe
laMonad
instancia de:Ahora, traducir una función simple es muy complicado, por lo que
Control.Monad
proporciona una función para hacerlo automáticamenteliftM2
. El 2 en su nombre se refiere al hecho de que trabaja en funciones de exactamente dos argumentos; existen funciones similares para funciones de 3, 4 y 5 argumentos. Estas funciones son mejores , pero no perfectas, y especificar el número de argumentos es feo y torpe.Lo que nos lleva al artículo que introdujo la clase de tipo Aplicativo . En él, los autores hacen esencialmente dos observaciones:
Functor
es algo muy natural de hacerMonad
La aplicación de función normal se escribe mediante una simple yuxtaposición de términos, por lo que para hacer que la "aplicación elevada" sea lo más simple y natural posible, el artículo presenta operadores infijos para sustituir la aplicación, elevados en
Functor
y una clase de tipo para proporcionar lo que se necesita para eso. .Todo lo cual nos lleva al siguiente punto:
(<*>)
simplemente representa la aplicación de la función, entonces, ¿por qué pronunciarla de manera diferente a como lo hace el "operador de yuxtaposición" de espacios en blanco?Pero si eso no es muy satisfactorio, podemos observar que el
Control.Monad
módulo también proporciona una función que hace lo mismo para las mónadas:Donde
ap
es, por supuesto, la abreviatura de "aplicar". Dado que cualquieraMonad
puede serApplicative
, yap
necesita solo el subconjunto de características presentes en este último, quizás podamos decir que si(<*>)
no fuera un operador, debería llamarseap
.También podemos abordar las cosas desde la otra dirección. La
Functor
operación de elevación se llamafmap
porque es una generalización de lamap
operación en listas. ¿Qué tipo de función en las listas funcionaría(<*>)
? Hay lo queap
hace en las listas, por supuesto, pero eso no es particularmente útil por sí solo.De hecho, existe una interpretación quizás más natural para las listas. ¿Qué le viene a la mente cuando mira la siguiente firma de tipo?
Hay algo tan tentador en la idea de alinear las listas en paralelo, aplicando cada función de la primera al elemento correspondiente de la segunda. Desafortunadamente para nuestro viejo amigo
Monad
, esta simple operación viola las leyes de las mónadas si las listas tienen diferentes longitudes. Pero funciona bienApplicative
, en cuyo caso se(<*>)
convierte en una forma de encadenar una versión generalizada dezipWith
, así que quizás podamos imaginarnos llamarlofzipWith
.Esta idea de cremallera en realidad nos trae un círculo completo. ¿Recuerda lo anterior de las matemáticas, sobre los functores monoidales? Como su nombre indica, estas son una forma de combinar la estructura de monoides y functores, los cuales son clases de tipos familiares de Haskell:
¿Cómo se verían estos si los pones en una caja juntos y la agitas un poco? De
Functor
mantendremos la idea de una estructura independiente de su parámetro de tipo , y deMonoid
mantendremos la forma general de las funciones:No queremos asumir que hay una manera de crear un verdadero "vacío"
Functor
, y no podemos conjurar un valor de un tipo arbitrario, así que arreglaremos el tipo demfEmpty
comof ()
.Tampoco queremos forzar la
mfAppend
necesidad de un parámetro de tipo consistente, así que ahora tenemos esto:¿Para qué es el tipo de resultado
mfAppend
? Tenemos dos tipos arbitrarios de los que no sabemos nada, por lo que no tenemos muchas opciones. Lo más sensato es mantener ambos:En ese punto
mfAppend
ahora es claramente una versión generalizada dezip
en listas, y podemos reconstruirApplicative
fácilmente:Esto también nos muestra que
pure
está relacionado con el elemento de identidad de aMonoid
, por lo que otros buenos nombres para él podrían ser cualquier cosa que sugiera un valor unitario, una operación nula o algo así.Eso fue largo, para resumir:
(<*>)
es sólo una aplicación de función modificada, por lo que puede leerla como "ap" o "aplicar", o elírsela por completo como lo haría con una aplicación de función normal.(<*>)
también generaliza de forma aproximadazipWith
en listas, por lo que puede leerlo como "zipfmap
functor con" , de manera similar a leer como "mapear un functor con".El primero está más cerca de la intención de la
Applicative
clase de tipo, como sugiere el nombre, así que eso es lo que recomiendo.De hecho, recomiendo el uso liberal y la no pronunciación de todos los operadores de aplicaciones levantados :
(<$>)
, que eleva una función de un solo argumento a unaFunctor
(<*>)
, que encadena una función de múltiples argumentos a través de unaApplicative
(=<<)
, que une una función que ingresa aMonad
en un cálculo existenteLos tres son, en el fondo, solo una aplicación de función normal, un poco condimentada.
fuente
Applicative
y el estilo idiomático funcional que promueve no reciben suficiente amor, así que no pude resistir la oportunidad de ensalzar un poco sus virtudes como un medio para explicar cómo (no) pronuncio(<*>)
.Applicative
's! Algo parecido[| f a b c d |]
(como sugiere el artículo original). Entonces no necesitaríamos el<*>
combinador y se referiría a dicha expresión como un ejemplo de "aplicación de función en un contexto functorial"Monad
. OFunctor
oMonoid
cualquier otra cosa que tenga un término bien establecido que involucre menos de tres adjetivos. "Aplicativo" es simplemente un nombre poco inspirador, aunque razonablemente descriptivo, colocado en algo que más bien lo necesitaba.Como no tengo la ambición de mejorar la respuesta técnica de CA McCann , abordaré la más esponjosa:
Como alternativa, especialmente porque no hay fin al constante llanto lleno de angustia y traición contra la
Monad
versión, llamada "return
", propongo otro nombre, que sugiere su función de una manera que puede satisfacer el más imperativo de los programadores imperativos. , y el más funcional de ... bueno, es de esperar, todo el mundo puede quejarse sobre la misma:inject
.Toma un valor. "Inyectar" en el
Functor
,Applicative
,Monad
, o lo que usted quiera. Voto por "inject
" y aprobé este mensaje.fuente
inject
es un nombre excelente y probablemente mejor que el mío, aunque como nota al margen menor, "inyectar" se usa en, creo, Smalltalk y Ruby para un método de plegado a la izquierda de algún tipo. Sin embargo, nunca entendí esa elección de nombre ...inject
en Ruby y Smalltalk se usa porque es como si estuvieras "inyectando" un operador entre cada elemento de la lista. Al menos, así siempre lo pensé.foldr
. (Reemplaza(:)
y[]
, donde(:)
toma 2 argumentos y[]
es una constante, porfoldr (+) 0 (1:2:3:[])
lo tanto ↝1+2+3+0
.) EnBool
es soloif
-then
-else
(dos constantes, elija una) y paraMaybe
que se llamemaybe
... Haskell no tiene un solo nombre / función para esto, ya que todos tienen diferentes tipos (en general, la eliminación es solo recursión / inducción)En breve:
<*>
puede llamarlo aplicar . Por lo queMaybe f <*> Maybe a
se puede pronunciar como aplicarMaybe f
sobreMaybe a
.Puede cambiar el nombre
pure
aof
, como hacen muchas bibliotecas de JavaScript. En JS puede crear un archivoMaybe
conMaybe.of(a)
.Además, la wiki de Haskell tiene una página sobre la pronunciación de los operadores de idiomas aquí.
fuente
Fuente: Programación de Haskell de First Principles , por Chris Allen y Julie Moronuki
fuente