ventajas tipos funciones ejemplos composicion haskell function-composition associativity

tipos - if en haskell ejemplos



¿Por qué la composición de funciones en Haskell es asociativa? (1)

Matemáticamente la operación de composición de funciones es asociativa. Por lo tanto:

f . (g . h) = (f . g) . h

Por lo tanto, la operación de composición de la función puede definirse como asociativa a la izquierda o asociativa a la derecha.

Dado que la aplicación de la función normal en Haskell (es decir, la yuxtaposición de términos, no la operación $ ) se deja asociativa en mi opinión, la composición de la función también debería dejarse asociativa. Después de todo, la mayoría de las personas en el mundo (incluido yo mismo) están acostumbradas a leer de izquierda a derecha.

Sin embargo la composición de funciones en Haskell es asociativa:

infixr 9 .

Sé que realmente no hace una diferencia si la operación de composición de funciones se deja asociativa o asociativa derecha. Sin embargo tengo curiosidad por saber por qué no queda asociativo. Dos razones vienen a mi mente para esta decisión de diseño:

  1. Los creadores de Haskell querían que la composición de funciones fuera lógicamente tan similar a la operación $ .
  2. Uno de los creadores de Haskell fue un japonés a quien le pareció más intuitivo hacer que la composición de funciones fuera asociativa en lugar de asociativa a la izquierda.

Bromas aparte, ¿hay alguna razón beneficiosa para que la composición de funciones sea asociativa correcta en Haskell? ¿Haría alguna diferencia si la composición de la función en Haskell se dejara asociativa?


En presencia de una evaluación no estricta, la asociatividad por el derecho es útil. Veamos un ejemplo muy tonto:

foo :: Int -> Int foo = const 5 . (+3) . (`div` 10)

Ok, ¿qué sucede cuando esta función se evalúa en 0 cuando . es infixr ?

foo 0 => (const 5 . ((+3) . (`div` 10))) 0 => (/x -> const 5 (((+3) . (`div` 10)) x)) 0 => const 5 (((+3) . (`div` 10)) 0) => 5

Ahora, y si . fue infixl ?

foo 0 => ((const 5 . (+3)) . (`div` 10)) 0 => (/x -> (const 5 . (+3)) (x `div` 10)) 0 => (const 5 . (+3)) (0 `div` 10) => (/x -> const 5 (x + 3)) (0 `div` 10) => const 5 ((0 `div` 10) + 3) => 5

(Estoy un poco cansado. Si cometí algún error en estos pasos de reducción, avíseme o simplemente corríjalo ...)

Tienen el mismo resultado, sí. Pero el número de pasos de reducción no es el mismo. Cuando es asociativo a la izquierda, la operación de composición puede necesitar reducirse más veces, en particular, si una función anterior en la cadena decide realizar un acceso directo de manera que no necesite el resultado del cálculo anidado. Los peores casos son los mismos, pero en el mejor de los casos, la asociatividad por el derecho puede ser una victoria. Entonces, elija la opción que a veces es mejor, en lugar de la opción que a veces es peor.