12

Si bien entiendo un poco sobre currying en el sentido matemático, parcialmente aplicando una función de infijo fue un nuevo concepto que descubrí después de bucear en el libro Learn You a Haskell for Great Good.Parcial Application with Infix Functions

Dada esta función:

applyTwice :: (a -> a) -> a -> a 
applyTwice f x = f (f x) 

El autor utiliza de una manera interesante:

ghci> applyTwice (++ [0]) [1] 
[1,0,0] 
ghci> applyTwice ([0] ++) [1] 
[0,0,1] 

Aquí puedo ver claramente que la función resultante tenía diferentes parámetros pasaron, lo que no sucedería de forma normal, teniendo en cuenta que es una función cursiva (¿o sí?). Entonces, ¿hay algún tratamiento especial en el seccionamiento infijo por Haskell? ¿Es genérico para todas las funciones de infijo?


Como nota al margen, esta es mi primera semana con Haskell y la programación funcional, y todavía estoy leyendo el libro.

Respuesta

18

Sí, puede aplicar parcialmente un operador infijo especificando su operando izquierdo o derecho, simplemente dejando el otro en blanco (exactamente en los dos ejemplos que escribió).

Así, ([0] ++) es lo mismo que (++) [0] o \x -> [0] ++ x (que recuerde puede convertir un operador infijo en una función estándar por medio de paréntesis), mientras que (++ [0]) es igual a \x -> x ++ [0].

Es útil conocer también el uso de acentos abiertos, (``), que le permiten convertir cualquier función estándar con dos argumentos en un operador infijo:

Prelude> elem 2 [1,2,3] 
True 
Prelude> 2 `elem` [1,2,3] -- this is the same as before 
True 
Prelude> let f = (`elem` [1,2,3]) -- partial application, second operand 
Prelude> f 1 
True 
Prelude> f 4 
False 
Prelude> let g = (1 `elem`) -- partial application, first operand 
Prelude> g [1,2] 
True 
Prelude> g [2,3] 
False 
+0

Por lo tanto, no sé Haskell, pero es '(1 \' elem \ ')' 'lo mismo que elem 1'? – Neil

+1

@Neil: Sí, lo es. –

+4

Me gusta pensar en '(++)' como una sección donde omites ambas entradas. –

5

Todos los operadores infijos se pueden utilizar en secciones en Haskell - a excepción de - debido a la extrañeza con negación unaria. Esto incluso incluye funciones no infíciles convertidas a infijo mediante el uso de palos de retroceso. Incluso se puede pensar en la formulación para la fabricación de los operadores en las funciones normales como de doble cara sección:

(x + y) ->(+ y) ->(+)

secciones son (en su mayoría, con algunos casos de esquina raras) tratado como simple lambdas. (/ 2) es lo mismo que:

\x -> (x/2)

y (2 /) es la misma que \x -> (2/x), para un ejemplo con un operador no conmutativo.

No hay nada profundamente interesante aquí teóricamente. Es solo azúcar sintáctico para la aplicación parcial de operadores de infijo. Hace que el código sea un poco más bonito, a menudo. (Hay contraejemplos, por supuesto.)

+2

Tenga en cuenta que la "extrañeza con negación unaria" se debe al hecho de que es ambiguo si '(- 1)' debe interpretarse como el literal numérico -1 o como la función '\ x -> x - 1'. La elección de los creadores de Haskell fue interpretarlo como un literal numérico, y proporcionar la función 'resta ', que satisface' restar x = \ y -> y - x'. También proporcionan 'negate' que actúa como la función unaria minus, es decir' negar x = -x' –

+0

@ChrisTaylor: Casi. '(- x)' se traduce como 'negar x', entonces' (- 1) 'es' negate (fromInteger 1) ', no' fromInteger (-1) '. Por supuesto, los dos deberían ser equivalentes para las instancias 'Num' de buen comportamiento. – hammar

+0

@hammar Ah ok, un poco de lógica hacia atrás allí. ¡Gracias por la corrección! –

15

Sí, esto es the section syntax en el trabajo.

Las secciones se escriben como (op e) o (e op), donde op es un operador binario y e es una expresión. Las secciones son una sintaxis conveniente para la aplicación parcial de operadores binarios.

Los siguientes identidades sostienen:

(op e) = \ x -> x op e 
(e op) = \ x -> e op x