2010-06-26 27 views
30

Estoy tratando de verificar algo para mí sobre el operador y la precedencia de función en Haskell. Por ejemplo, el siguiente códigoHaskell operador vs función precedencia

list = map foo $ xs 

se puede reescribir como

list = (map foo) $ (xs) 

y con el tiempo será

list = map foo xs 

Mi pregunta solía ser, ¿por qué la primera formulación no se puede reescribir como

list = (map foo $) xs 

ya que la precedencia de la función siempre es mayor que la precedencia del operador, pero creo que he encontrado la respuesta: simplemente no se permite que los operadores sean argumentos de funciones (excepto, por supuesto, si los rodean con paréntesis). ¿Es esto correcto? Si es así, me parece extraño que no haya mención de esta mecánica/regla en RWH o Learn You a Haskell, o en cualquiera de los otros lugares que he buscado. Entonces, si conoce un lugar, donde se establece la regla, por favor enlace a él.

- editar: Gracias por sus respuestas rápidas. Creo que mi confusión vino de pensar que un operador literal de alguna manera evaluaría algo, que podría ser consumido por una función como argumento. Me ayudó a recordar que un operador infijo se puede traducir mecánicamente a funciones de prefijo. Hacer esto a los primeros rendimientos de formulación

($) (map foo) (xs) 

donde no hay duda de que ($) es la función que consume, y puesto que las dos formulaciones son equivalentes, entonces los $ literal en la primera formulación no puede ser consumido por el mapa.

Respuesta

20

Usted está correcto. Esta regla es parte de la sintaxis de Haskell definida por Haskell Report. En particular, tenga en cuenta en la Sección 3, Expresiones, que el argumento para la aplicación de función (un fexp) debe ser un aexp. Un aexp permite a los operadores como parte de secciones, y también dentro de una expresión entre paréntesis, pero no a operadores desnudos.

En map foo $ xs, la sintaxis de Haskell significa que esto se analiza como dos expresiones que se aplican al operador binario $. Como sepp2k notas, la sintaxis (map foo $) es una sección izquierda y tiene un significado diferente.

Tengo que confesar que nunca he pensado demasiado sobre esto y de hecho tuve que buscarlo en el Informe para ver por qué los operadores tienen el comportamiento que tienen.

8

Los operadores se pueden pasar como argumentos de funciones si los rodea con paréntesis (es decir, map foo ($) xs, que de hecho pasará como (map foo ($)) xs). Sin embargo, si no los rodea con paréntesis, tiene razón en que no se pueden pasar como argumento (o asignados a variables).

También tenga en cuenta que la sintaxis (someValue $) (donde $ podría ser cualquier operador) en realidad significa algo diferente: es equivalente a \x -> someValue $ x, es decir, parcialmente se aplica el operador a su operando de la izquierda (que en caso de $ es un noop por supuesto) Del mismo modo, ($ x) aplica parcialmente el operador al operando derecho. Entonces map ($ x) [f, g, h] evaluaría a [f x, g x, h x].

27

En primer lugar, la aplicación (espacios en blanco) es el "operador" de mayor precedencia.

En segundo lugar, en Haskell, no existe realmente ninguna distinción entre operadores y funciones, salvo que los operadores son infijos por defecto, mientras que las funciones no lo son. Puede convertir funciones a INFIX con acentos abiertos

2 `f` x 

y convertir los operadores prefijar con parens:

(+) 2 3 

Por lo tanto, su pregunta es un poco confuso.

Ahora, funciones y operadores específicos se han declarado precedencia, que se puede encontrar en GHCi con ": info":

Prelude> :info ($) 
($) :: (a -> b) -> a -> b -- Defined in GHC.Base 

infixr 0 $ 

class (Eq a, Show a) => Num a where 
    (+) :: a -> a -> a 

infixl 6 + 

Mostrando tanto precedencia y asociatividad.

+0

Gracias. Después de leer tu respuesta, cuando dices que mi pregunta es un poco confusa, entiendo que mi mención de "función de precedencia" es incorrecta, y que las funciones en sí mismas no tienen ninguna precedencia, sino que pueden considerarse como argumentos para el operador de la aplicación, espacios en blanco. ¿Es esto correcto? Todavía no estoy del todo claro sobre cómo caben los operadores de infijo en esto. He editado mi pregunta para reflejar cómo puedo pensarlo mejor. ¿Es este entendimiento correcto? – Boris

+0

No, no del todo. Las funciones y los operadores son indistinguibles en Haskell. Ambos pueden tener diferentes niveles de precedencia, especificados por el usuario. –

+3

Ok. Todavía estoy intentando descubrir la regla general, que lleva a list = (map foo) $ (xs) en lugar de list = (map foo $) xs, que sé que es una sección. Podría decirse que, al decidir, para una expresión dada, qué argumentos pertenecen a qué funciones y operadores, las funciones consumen, comenzando desde la izquierda, todos los argumentos que pueden, hasta que llegan a un operador.Después de esto, los operadores consumen sus argumentos, de acuerdo con su precedencia y asociatividad (Esto es un poco vago, pero espero que lo entiendas). Lo siento, si esto parece obvio, pero nunca fue muy claro para mí. – Boris

10

La diferencia es que los operadores infijos quedan colocados entre sus argumentos, por lo que este

list = map foo $ xs 

pueden reescribirse en forma de prefijo que

list = ($) (map foo) xs 

que, por definición del operador $, es simplemente

list = (map foo) xs 
9

Además de la información proporcionada por otras respuestas, tenga en cuenta que diferentes operadores pueden tener diferentes precedencias sobre otros operadores, además de ser izquierda/derecha o no asociativa. Puede encontrar estas propiedades para los operadores Prelude en el Haskell 98 Report fixity section.

 
+--------+----------------------+-----------------------+-------------------+ 
| Prec- | Left associative | Non-associative | Right associative | 
| edence |  operators  |  operators  | operators  | 
+--------+----------------------+-----------------------+-------------------+ 
| 9  | !!     |      | .     | 
| 8  |      |      | ^, ^^, **   | 
| 7  | *, /, `div`,   |      |     | 
|  | `mod`, `rem`, `quot` |      |     | 
| 6  | +, -     |      |     | 
| 5  |      |      | :, ++    | 
| 4  |      | ==, /=, <, <=, >, >=, |     | 
|  |      | `elem`, `notElem`  |     | 
| 3  |      |      | &&    | 
| 2  |      |      | ||    | 
| 1  | >>, >>=    |      |     | 
| 0  |      |      | $, $!, `seq`  | 
+--------+----------------------+-----------------------+-------------------+ 

Cualquier operador que carecen de una declaración de fijeza se supone que es asociativos por la izquierda con precedencia 9.

Recuerde, aplicación de función tiene precedencia más alta (piense de precedencia 10 en comparación con las otras precedencias en la tabla) [1].

+0

¿Cómo es que 'f g. h' es equivalente a 'f (g.h)'? ¿Eso hace que la precedencia de la aplicación de función sea menor o igual que la precedencia de la composición de la función? – CMCDragonkai

+0

@CMCDragonkai He actualizado la respuesta con la precedencia de la aplicación de función. 'f g. h' es equivalente a '(f g). h' como la aplicación de función tiene mayor precedencia que cualquier precedencia de operador (incluida la composición de funciones). – mucaho