Creo que este ejercicio genera dudas porque hay mucho alto orden; tenemos una lista de funciones, la consumimos con un foldl
que entonces debe tener un argumento que combine funciones (la composición, en este caso), entonces el resultado es una función, que luego podemos aplicar a 10.
Siempre en estos casos una solución posible es empezar a evaluar paso a paso, aunque puede que existan "atajos". En este caso puntual no importa qué estrategia de reducción usamos porque no hay ninguna computación que pueda no terminar.
Recordemos la definición de foldl:
foldl f v [] = v
foldl f v (x : xs) = foldl f (f v x) xs
Reduciendo un paso:
foo 10 =
(foldl (.) (+2) [(+1),(*2),(+4)]) 10 =
(foldl (.) ((+2) . (+1)) [(*2),(+4)]) 10
En la primer igualdad desplegué la definición de foo (usando el caso recursivo) en la segunda damos el paso de cómputo, escribimos la composición infija para que sea más legible.
Podemos continuar:
(foldl (.) ((+2) . (+1)) [(*2),(+4)]) 10 =
(foldl (.) (((+2) . (+1)) . (*2)) [(+4)]) 10 =
(foldl (.) ((((+2) . (+1)) . (*2)) . (+4)) []) 10
Finalmente, la próxima reducción es aplicando la primera cláusula de la definición, con el pattern de la lista vacía. Obtenemos:
((((+2) . (+1)) . (*2)) . (+4)) 10
Además de reducir paso a paso hasta aqui, otra opción (un "atajo") sería pensar directamente en la semántica del foldl para reescribir la lista (sabiendo que se va a aplicar f
asociando a la izquierda).
Para calcular el resultado podemos seguir reduciendo o podemos pensar en el resultado ignorando los paréntesis internos porque la composición es asociativa, en cualquier caso, lo que nos da es: al 10 sumamos 4, luego multiplicamos por dos, luego sumamos 1 y luego 2 (31).
¿Se entiende? Cualquier cosa la seguimos.