function - Operador Haskell vs precedencia de funciones
operator-keyword precedence (5)
Estoy tratando de verificar algo para mí sobre el operador y la precedencia de la función en Haskell. Por ejemplo, el siguiente código
list = map foo $ xs
puede ser reescrito como
list = (map foo) $ (xs)
y eventualmente será
list = map foo xs
Mi pregunta solía ser, por qué la primera formulación no se reescribía 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: los operadores simplemente no pueden ser 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. Haciendo esto con los rendimientos de la primera formulación
($) (map foo) (xs)
donde no hay duda de que ($) es la función consumidora, y dado que las dos formulaciones son equivalentes, entonces el $ literal en la primera formulación no puede ser consumido por el mapa.
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 de Prelude
en la sección Fixing del Informe Haskell 98 .
+--------+----------------------+-----------------------+-------------------+ | 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` | +--------+----------------------+-----------------------+-------------------+
Se supone que cualquier operador que carezca de una declaración de fijeza se asocia con la precedencia 9 .
Recuerde, la aplicación de función tiene la más alta prioridad (piense en la precedencia 10
comparación con las otras precedencias en la tabla) [1] .
En primer lugar, la aplicación (espacio en blanco) es el "operador" de mayor precedencia.
En segundo lugar, en Haskell, realmente no hay distinción entre operadores y funciones, salvo que los operadores son infijos por defecto, mientras que las funciones no lo son. Puede convertir funciones en infijo con palos de retroceso
2 `f` x
y convierte a los operadores al prefijo con parens:
(+) 2 3
Entonces, tu pregunta está un poco confundida.
Ahora, las funciones y operadores específicos tendrán precedencia declarada, que 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 precedencia y asociatividad.
Estás en lo correcto. Esta regla es parte de la sintaxis de Haskell definida por el Informe Haskell . En particular, tenga en cuenta en la Sección 3, Expresiones, que el argumento para la aplicación de la 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 el map foo $ xs
, la sintaxis de Haskell significa que esto se analiza como dos expresiones que se aplican al operador binario $
. Como notas sepp2k, 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.
La diferencia es que los operadores de infijo se colocan entre sus argumentos, por lo que este
list = map foo $ xs
puede ser reescrito en forma de prefijo como
list = ($) (map foo) xs
que, según la definición del operador $, es simplemente
list = (map foo) xs
Los operadores se pueden pasar como argumentos de función 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) realmente significa algo diferente: es equivalente a /x -> someValue $ x
, es decir, aplica parcialmente el operador a su operando izquierdo (que en el caso de $
es un noop por supuesto). Del mismo modo ($ x)
aplica parcialmente el operador al operando derecho. Entonces el map ($ x) [f, g, h]
evaluaría a [fx, gx, hx]
.