haskell lazy-evaluation

¿Hasta qué punto es perezoso Haskell?



lazy-evaluation (4)

A menos que sea necesario obtener el valor de esa variable, no se evaluará. Básicamente, Haskell es tan perezoso a menos que se le diga que no lo sea.

Puedes confirmar esto, así

Prelude> :set +m Prelude> let anotherFunction = (100, 1 `div` 0) Prelude| Prelude> let myFunction arg Prelude| | arg == 1 = a Prelude| | otherwise = b Prelude| where Prelude| (a, b) = anotherFunction Prelude|

Aquí, 1 `div` 0 generará divide by zero error de divide by zero . Si evalúa todos los elementos, incluso cuando invoque myFunction con 1 , habría recibido ese error, pero

Prelude> myFunction 1 100

solo cuando lo invoca con cualquier otro valor, es necesario evaluar el segundo valor de la tupla, y fallará con el error de divide by zero .

Prelude> myFunction 2 *** Exception: divide by zero

Necesito una aclaración sobre la pereza con Haskell.

Si tengo esta función:

myFunction arg | arg == 1 = a | arg == 2 = a*b | arg == 3 = b+c | otherwise = (a+b)*c where a = ... b = ... c = ... d = ...

Cuando llamo a mi myFunction 1 , Haskell evaluará solo la función a a = ... , ni b ni c , ni d .

Pero si escribo

myFunction arg | arg == 1 = a | arg == 2 = a*b | arg == 3 = b+c | otherwise = (a+b)*c where (a,b,c,d) = anotherFunction arg

¿Cuál será el comportamiento de Haskell?

  • ¿Evaluará solo una y ''propagará'' la pereza a otra anotherFunction ?
  • O, ¿evaluará la tupla entera (a, b, c, d) como resultado de otra anotherFunction ?

Bueno, solo está interesado en a , así que eso significa que hay una función implícita :

thea :: (a,b,c,d) -> a thea (a,_,_,_) = a

En otras palabras, Haskell no está interesado en los otros elementos de la tupla. A veces, sin embargo, los elementos de la tupla comparten alguna estructura. Decir otra función se define como:

anotherFunction :: Int -> (Int,Int,Int,Int) anotherFunction x = (z,t,f,g) where f = x*x g = f+x z = g-2 t = 3

En ese caso, para evaluar el primer elemento, también se evaluará el tercer y cuarto elemento. Pero como no haces nada con ellos, a Haskell no le interesará específicamente su resultado.


Como otros ya han señalado, solo a será evaluada.

Sin embargo, tenga en cuenta que, para aprovechar la pereza, es crucial que anotherFunction devuelva una tupla antes de evaluar sus componentes. Por ejemplo, considere

anotherFunction n = if p > 1000 then (n, p) else (n, 0) where p = product [1..n]

Lo anterior siempre evaluará el product [1..n] , incluso si la persona que llama solo exige el primer componente del par (que es n ). Esto se debe a que el if debe evaluarse antes de que se pueda devolver el par, y eso fuerza a p . Por el contrario,

anotherFunction n = (n, if p > 1000 then p else 0) where p = product [1..n]

Inmediatamente devolverá el par. Si solo se evalúa su primer componente, entonces p no se computará en absoluto.


En ambos casos, no evaluará nada a menos que se solicite el valor. Una forma de exigir el valor es llamar a la función en ghci (que imprime el valor en ghci y, por lo tanto, lo exige). Suponiendo que está ejecutando la función, en su segundo caso evaluará la forma normal de la tupla a cabeza débil (WHNF) y luego evaluará el primer elemento en (a,b,c,d) porque solo se exige ese valor. Los otros elementos b , d estarán en la forma thunk. De hecho puedes verificar que tú mismo:

myFunction arg | arg == 1 = a | arg == 2 = a*b | arg == 3 = b+c | otherwise = (a+b)*c where (a,b,c,d) = anotherFunction arg anotherFunction x = (x, undefined, undefined, undefined)

Demostración en ghci:

λ> myFunction 1 1