Pregunta Cuál es la diferencia entre . (punto) y $ (signo de dólar)?


Cuál es la diferencia entre el punto (.) y el signo de dólar ($)?. Según lo entiendo, ambos son azúcar sintáctico para no tener que usar paréntesis.


607
2018-06-02 16:06


origen


Respuestas:


los $ operador es para evitar paréntesis. Cualquier cosa que aparezca después tendrá prioridad sobre cualquier cosa que venga antes.

Por ejemplo, supongamos que tienes 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 de la . el operador no debe evitar paréntesis, sino a funciones de cadena. Te permite vincular la salida de lo que aparezca a la derecha con la entrada de lo que aparezca a la izquierda. Esto generalmente también resulta en menos paréntesis, pero funciona de manera diferente.

Volviendo al mismo ejemplo:

putStrLn (show (1 + 1))
  1. (1 + 1) no tiene una entrada, y por lo tanto no se puede usar con . operador.
  2. show puede tomar un Int y devuelve un String.
  3. putStrLn puede tomar una String y devuelve un IO ().

Puedes encadenar show a putStrLn Me gusta esto:

(putStrLn . show) (1 + 1)

Si son demasiados paréntesis para su gusto, deshágase de ellos con el $ operador:

putStrLn . show $ 1 + 1

1091
2017-08-17 22:01



Tienen diferentes tipos y diferentes definiciones:

infixr 9 .
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(f . g) x = f (g x)

infixr 0 $
($) :: (a -> b) -> a -> b
f $ x = f x

($) 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 hacer una nueva función.

En algunos casos son intercambiables, pero esto no es cierto en general. El ejemplo típico donde están es:

f $ g $ h $ x

==>

f . g . h $ x

En otras palabras, en una cadena de $s, todos menos el final pueden ser reemplazados por .


170
2018-06-02 16:14



También tenga en cuenta que ($) es la función de identidad especializada para tipos de funciones. La función de identidad se ve así:

id :: a -> a
id x = x

Mientras ($) Se ve como esto:

($) :: (a -> b) -> (a -> b)
($) = id

Tenga en cuenta que he agregado intencionalmente paréntesis adicionales en la firma de tipo.

Usos de ($) generalmente se puede eliminar agregando paréntesis (a menos que el operador se use en una sección). P.ej.: f $ g x se convierte f (g x).

Usos de (.) a menudo son un poco más difíciles de reemplazar; generalmente necesitan una lambda o la introducción de un parámetro de función explícito. Por ejemplo:

f = g . h

se convierte

f x = (g . h) x

se convierte

f x = g (h x)

¡Espero que esto ayude!


111
2018-06-02 16:32



($) permite que las funciones se encadenen juntas sin agregar paréntesis para controlar el orden de evaluación:

Prelude> head (tail "asdf")
's'

Prelude> head $ tail "asdf"
's'

El operador de componer (.) crea una nueva función sin especificar los argumentos:

Prelude> let second x = head $ tail x
Prelude> second "asdf"
's'

Prelude> let second = head . tail
Prelude> second "asdf"
's'

El ejemplo anterior es discutiblemente ilustrativo, pero realmente no muestra la conveniencia de usar la composición. Aquí hay otra analogía:

Prelude> let third x = head $ tail $ tail x
Prelude> map third ["asdf", "qwer", "1234"]
"de3"

Si solo utilizamos una tercera vez, podemos evitar nombrarlo usando una lambda:

Prelude> map (\x -> head $ tail $ tail x) ["asdf", "qwer", "1234"]
"de3"

Finalmente, la composición nos permite evitar el lambda:

Prelude> map (head . tail . tail) ["asdf", "qwer", "1234"]
"de3"

68
2018-01-07 01:43



La versión corta y dulce:

  • ($) llama a la función que es su argumento de la izquierda en el valor que es su argumento de la derecha.
  • (.) compone la función que es su argumento de la izquierda sobre la función que es su argumento de la derecha.

57
2018-06-10 20:11



Una aplicación que es útil y me llevó algo de tiempo descifrar a partir de la breve descripción al aprender un haskell: Ya que:

f $ x = f x

y el paréntesis del lado derecho de una expresión que contiene un operador infijo lo convierte en una función de prefijo, se puede escribir ($ 3) (4+) análogo a (++", world") "hello".

¿Por qué alguien haría esto? Para listas de funciones, por ejemplo. Ambos:

map (++", world") ["hello","goodbye"]`

y:

map ($ 3) [(4+),(3*)]

son más cortos que map (\x -> x ++ ", world") ... o map (\f -> f 3) .... Obviamente, las últimas variantes serían más legibles para la mayoría de las personas.


28
2018-01-31 12:12



... o podrías evitar el . y $ construcciones mediante el uso pipelining:

third xs = xs |> tail |> tail |> head

Eso es después de haber agregado en la función auxiliar:

(|>) x y = y x

12
2017-12-04 10:24



Una excelente forma 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 los operadores, ayuda recordar este pequeño truco:

:t (.)
(.) :: (b -> c) -> (a -> b) -> a -> c

y

:t ($)
($) :: (a -> b) -> a -> b

Solo recuerda usar :t generosamente, y envuelva a sus operadores en ()!


11
2018-04-15 06:10



Mi regla es simple (yo también soy principiante):

  • no utilice . si quieres pasar el parámetro (llamar a la función), y
  • no utilice $ si aún no hay ningún parámetro (componga una función)

Es decir

show $ head [1, 2]

pero nunca:

show . head [1, 2]

10
2017-08-20 15:53