2010-06-13 10 views
119

He estado leyendo Real World Haskell, y estoy llegando al final, pero una cuestión de estilo ha sido difícil para mí con el (.) y ($) operadores.

Cuando se escribe una función que es una composición de otras funciones que lo escribe como:

f = g . h 

Pero cuando se aplica algo al final de aquellas funciones que lo escribe así:

k = a $ b $ c $ value 

Pero el libro escribiría así:

k = a . b . c $ value 

Ahora, me miran función aliado equivalente, hacen exactamente lo mismo en mis ojos. Sin embargo, cuanto más miro, más veo que las personas escriben sus funciones de la manera que lo hace el libro: componer con (.) primero y luego solo al final usar ($) para agregar un valor para evaluar el lote (nadie lo hace con muchos dólares composiciones).

¿Hay alguna razón para usar los libros de una forma mucho mejor que usar todos los símbolos ($)? ¿O hay alguna mejor práctica aquí que no estoy recibiendo? ¿O es superfluo y no debería preocuparme por eso en absoluto?

+3

Tenga en cuenta que el segundo ejemplo se puede hacer como 'k = a $ b $ c value' –

+1

Sí, puede, como Zifre menciona a continuación, pero decidí dejarlo allí ya que no daña nada y hace su (y ahora tus) comentarios tienen sentido. Gracias de cualquier forma. +1 :) –

+2

Otro enfoque común es 'a. b $ c value', que no me gusta tanto como el tercer ejemplo, pero guarda algunos caracteres. –

Respuesta

135

Supongo que puedo responder a esto desde la autoridad.

¿Hay alguna razón para usar los libros de una forma mucho mejor que usar todos los símbolos ($)?

No hay motivo especial. Bryan y yo preferimos reducir el ruido de la línea. . es más silencioso que $. Como resultado, el libro usa la sintaxis f . g . h $ x.

+2

Bueno, no puedo esperar una mejor respuesta que ésta; de uno de los autores mismo. :) Y eso tiene sentido, se ve mucho más silencioso en la página a medida que leo. Marcar esto como la respuesta porque responde directamente la pregunta. Gracias por la respuesta; y, de hecho, el libro. –

+33

No estoy en desacuerdo con el autor, pero creo que hay otra razón más importante en el modelo mental de * crear * algo en lugar de * usar * it. Los usuarios de Haskell tienden a querer pensar en 'f.g.h' como una nueva creación inteligente en lugar de' f (g (h())) '. Ahora están llamando a una función nueva, aunque anónima, que crearon en lugar de simplemente encadenar un gran diccionario de llamadas a funciones prefabricadas, como un usuario de PHP. –

+1

Esa es una declaración interesante: 'reduce el ruido de la línea' y 'más silencioso que'. Nunca pensé acerca de los lenguajes de programación en esta terminología, pero tiene mucho sentido. – Rabarberski

3

Es solo una cuestión de estilo. Sin embargo, la forma en que el libro lo hace tiene más sentido para mí. Compone todas las funciones y luego las aplica al valor.

Su método simplemente parece extraño, y el último $ es innecesario.

Sin embargo, realmente no importa. En Haskell, generalmente hay muchas, muchas formas correctas de hacer lo mismo.

+3

No me di cuenta de que los últimos $ no eran necesarios, pero debería haberlo hecho. Gracias y lo dejaré allí para que la gente sepa lo que significa este comentario. –

51

Son de hecho equivalentes: tenga en cuenta que el operador $ hace, esencialmente, nada. f $ x evalúa a f x. El propósito de $ es su comportamiento de fijeza: derecho de asociación y precedencia mínima. Extracción $ y el uso de paréntesis para agrupar en lugar de precedencia infija, los fragmentos de código siguiente aspecto:

k = a (b (c (value))) 

y

k = (a . b . c) value 

La razón para preferir la versión . sobre la versión $ es la misma razón por la prefiriendo ambos sobre la misma versión entre paréntesis: atractivo estético.

Aunque, algunos podrían preguntarse si el uso de operadores de infijo en lugar de paréntesis se basa en algún impulso subconsciente para evitar cualquier parecido posible con Lisp (es broma ... ¿no?).

18

Para mí, creo que la respuesta es (a) la pulcritud, como Don said; y (b) me parece que cuando estoy editando código, mi función puede terminar en un estilo sin puntos, y luego todo lo que tengo que hacer es eliminar el último $ en lugar de volver y cambiar todo. Un punto menor, sin duda, pero una gracia.

+0

Sí, esa es una buena razón para escribir así, si quieres terminar en una composición de funciones, entonces es más rápido. :) Sí por guardar las pulsaciones de teclas, pero más importante aún, el tiempo. +1 –

13

Hay una discusión interesante de esta pregunta en this haskell-cafe thread. Aparentemente hay un punto de vista minoritario que sostiene que la asociatividad correcta de $ es "just plain wrong", y elegir f . g . h $ x sobre f $ g $ h $ x es una forma de evitar el problema.

+0

Oye, encontraste una discusión completa sobre el asunto. Eso es asombroso, estoy teniendo una lectura ahora. +1 –

+2

Argumentos similares aquí: https://ghc.haskell.org/trac/haskell-prime/wiki/ChangeDollarAssociativity – enrique

+0

Tenga en cuenta que '$!' También tiene la asociatividad correcta "incorrecta" - [¿Por qué es $! ¿operador right-asociative?] (http://stackoverflow.com/q/23570501/94687). –

35

Yo agregaría que en f . g $ x, f . g es una unidad sintáctica significativa.

Mientras tanto, en f $ g $ x, f $ g no es una unidad significativa. Una cadena de $ es posiblemente más imprescindible - primera obtener el resultado de g de x, a continuación, hacer f a ella, a continuación, hacer foo a ella, a continuación, etc.

Mientras tanto una cadena de . es posiblemente más declarativo y, en cierto sentido, más cercano a una vista centrada en el flujo de datos: componga una serie de funciones y, finalmente, aplíquelas a algo.

+1

Estoy aprendiendo Haskell (no he codificado nada significativo) pero me gusta pensar en $ como una especie de operador de "tubería". Es muy parecido a la terminal | canalización (excepto que los datos fluyen de derecha a izquierda), pero al igual que los procesos terminales, cada unidad es explícitamente independiente. – LostSalad

+1

El operador '$' parece comportarse de manera similar al operador '<|' en F #. Creo que ambos se definen igual, de hecho, en F #, '(<|) a b = a b' y en Haskell, 'a $ b = a b', que son esencialmente equivalentes. – Quackmatic

+0

@Qalkmatic Bastante seguro de que es '(<|) b a = a b' en F #, ya que se usa como' [1; 2; 3; 4; 5] <| List.map ((+) 1) ', si la memoria sirve. – BalinKingOfMoria

0

Me doy cuenta de que esta es una pregunta muy antigua, pero creo que hay otra razón para esto que no se ha mencionado.

Si declara una nueva función sin puntos f . g . h, el valor que pase se aplicará automáticamente. Sin embargo, si escribe f $ g $ h, no funcionará.

Creo que la razón por la que el autor prefiere el método de composición es porque conduce a una buena práctica de creación de funciones.