¿Cuál es la diferencia entre el punto (.)
y el signo de dólar ($)
?
Según tengo entendido, ambos son azúcar sintáctica por no necesitar usar paréntesis.
fuente
¿Cuál es la diferencia entre el punto (.)
y el signo de dólar ($)
?
Según tengo entendido, ambos son azúcar sintáctica por no necesitar usar paréntesis.
El $
operador es para evitar paréntesis. Cualquier cosa que aparezca después tendrá prioridad sobre cualquier cosa que ocurra antes.
Por ejemplo, supongamos que tiene una línea que dice:
putStrLn (show (1 + 1))
Si desea deshacerse de esos paréntesis, cualquiera de las siguientes líneas también haría lo mismo:
putStrLn (show $ 1 + 1)
putStrLn $ show (1 + 1)
putStrLn $ show $ 1 + 1
El propósito principal del .
operador no es evitar paréntesis, sino encadenar funciones. Le permite vincular la salida de lo que aparece a la derecha con la entrada de lo que aparece a la izquierda. Esto generalmente también produce menos paréntesis, pero funciona de manera diferente.
Volviendo al mismo ejemplo:
putStrLn (show (1 + 1))
(1 + 1)
no tiene una entrada y, por lo tanto, no se puede usar con el .
operador.show
puede tomar un Int
y devolver a String
.putStrLn
puede tomar un String
y devolver un IO ()
.Puedes encadenar show
para que te putStrLn
guste esto:
(putStrLn . show) (1 + 1)
Si son demasiados paréntesis para su gusto, elimínelos con el $
operador:
putStrLn . show $ 1 + 1
putStrLn . show . (+1) $ 1
sería equivalente. Tienes razón en que la mayoría (¿todos?) Los operadores infijos son funciones.map ($3)
. Quiero decir, también uso principalmente$
para evitar paréntesis, pero no es que eso sea todo lo que están ahí para hacer.map ($3)
Es una función de tipoNum a => [(a->b)] -> [b]
. Toma una lista de funciones que toman un número, aplica 3 a todas y recopila los resultados.Tienen diferentes tipos y diferentes definiciones:
($)
está destinado a reemplazar la aplicación de función normal pero con una precedencia diferente para ayudar a evitar paréntesis.(.)
es para componer dos funciones juntas para crear una nueva función.En algunos casos son intercambiables, pero esto no es cierto en general. El ejemplo típico donde están es:
==>
En otras palabras, en una cadena de
$
s, todos menos el final pueden ser reemplazados por.
fuente
x
fuera una función? ¿Puedes usarlo.
como el final?x
en este contexto, entonces sí, pero luego el "final" se aplicaría a algo diferentex
. Si no está aplicandox
, entonces no es diferente ax
ser un valor.También tenga en cuenta que
($)
es la función de identidad especializada para los tipos de función . La función de identidad se ve así:Mientras se
($)
ve así:Tenga en cuenta que he agregado paréntesis adicionales intencionalmente en la firma de tipo.
Los usos de
($)
usualmente se pueden eliminar agregando paréntesis (a menos que el operador se use en una sección). Por ejemplo: sef $ g x
conviertef (g x)
.Los usos de a
(.)
menudo son un poco más difíciles de reemplazar; Por lo general, necesitan una lambda o la introducción de un parámetro de función explícito. Por ejemplo:se convierte
se convierte
¡Espero que esto ayude!
fuente
($)
permite que las funciones se encadenen juntas sin agregar paréntesis para controlar el orden de evaluación:El operador de redacción
(.)
crea una nueva función sin especificar los argumentos:El ejemplo anterior es posiblemente ilustrativo, pero en realidad no muestra la conveniencia de usar composición. Aquí hay otra analogía:
Si solo usamos el tercero una vez, podemos evitar nombrarlo usando una lambda:
Finalmente, la composición nos permite evitar la lambda:
fuente
La versión corta y dulce:
($)
llama a la función que es su argumento izquierdo sobre el valor que es su argumento derecho.(.)
compone la función que es su argumento de la izquierda sobre la función que es su argumento de la derecha.fuente
Una aplicación que es útil y me llevó algo de tiempo comprender la breve descripción de aprender un haskell : Desde:
y entre paréntesis el lado derecho de una expresión que contiene un operador infijo lo convierte en una función de prefijo, se puede escribir de forma
($ 3) (4+)
análoga(++", world") "hello"
.¿Por qué alguien haría esto? Para listas de funciones, por ejemplo. Ambos:
y:
son más cortos que
map (\x -> x ++ ", world") ...
omap (\f -> f 3) ...
. Obviamente, las últimas variantes serían más legibles para la mayoría de las personas.fuente
$3
sin el espacio. Si Template Haskell está habilitado, esto se analizará como un empalme, mientras que$ 3
siempre significa lo que dijiste. En general, parece haber una tendencia en Haskell a "robar" bits de sintaxis al insistir en que ciertos operadores tienen espacios a su alrededor para ser tratados como tales.Son sin azúcar sintáctica para no tener que paréntesis de uso - que son funciones, - infijo, por lo tanto les podemos llamar operadores.
Componer,
(.)
y cuándo usarlo.(.)
es la función componer Entonceses lo mismo que construir una función que pasa el resultado de su argumento pasado
g
af
.Úselo
(.)
cuando no tenga los argumentos disponibles para pasar a las funciones que desea componer.Aplicativo asociativo correcto
($)
, y cuándo usarlo($)
es una función de aplicación asociativa a la derecha con baja precedencia de enlace. Por lo tanto, simplemente calcula primero las cosas a la derecha. Así,es lo mismo, procesalmente (lo que importa ya que Haskell se evalúa perezosamente, comenzará a evaluar
f
primero):o más concisamente:
Úselo
($)
cuando tenga todas las variables para evaluar antes de aplicar la función anterior al resultado.Podemos ver esto leyendo la fuente de cada función.
Lee la fuente
Aquí está la fuente de
(.)
:Y aquí está la fuente de
($)
:Conclusión
Use la composición cuando no necesite evaluar inmediatamente la función. Tal vez desee pasar la función que resulta de la composición a otra función.
Use la aplicación cuando proporcione todos los argumentos para una evaluación completa.
Entonces, para nuestro ejemplo, sería semánticamente preferible hacer
cuando tenemos
x
(o más bien,g
los argumentos de), y hacemos:cuando no lo hacemos
fuente
... o podría evitar las construcciones
.
y$
utilizando la canalización :Eso es después de haber agregado en la función auxiliar:
fuente
$
operador realmente funciona más como F # 's<|
de lo que hace|>
, por lo general en Haskell que iba a escribir la función anterior como esto:third xs = head $ tail $ tail $ xs
o tal vez incluso comothird = head . tail . tail
, que en Fa # sintaxis al estilo sería algo como esto:let third = List.head << List.tail << List.tail
$
ya está disponible, y se llama&
hackage.haskell.org/package/base-4.8.0.0/docs/…¡Una excelente manera de aprender más sobre cualquier cosa (cualquier función) es recordar que todo es una función! Ese mantra general ayuda, pero en casos específicos como operadores, ayuda recordar este pequeño truco:
y
¡Solo recuerde usar
:t
generosamente y envolver a sus operadores()
!fuente
Mi regla es simple (yo también soy principiante):
.
si desea pasar el parámetro (llame a la función), y$
si aún no hay ningún parámetro (componer una función)Es decir
pero nunca:
fuente
Creo que un breve ejemplo de dónde usarías
.
y no$
ayudaría a aclarar las cosas.Tenga en cuenta que
times6
es una función que se crea a partir de la composición de funciones.fuente
Todas las otras respuestas son bastante buenas. Pero hay un detalle de usabilidad importante sobre cómo trata ghc $, que el verificador de tipo ghc permite la instalación con tipos cuantificados / de rango superior. Si observa el tipo de,
$ id
por ejemplo, encontrará que tomará una función cuyo argumento es en sí mismo una función polimórfica. Pequeñas cosas como esa no tienen la misma flexibilidad con un operador molesto equivalente. (Esto realmente me hace preguntarme si $! Merece el mismo tratamiento o no)fuente