resueltos puntos polaca notacion inversa entre ejemplos distancia circulo arboles haskell memory-leaks ghci

haskell - puntos - notacion polaca ejemplos resueltos



El operador de asignación IO/Monadic hace que ghci explote para una lista infinita (1)

Considere el siguiente programa. Se ejecuta para siempre y no hace nada útil, pero el consumo de memoria en ghci es constante:

--NoExplode.hs module Main (main) where test :: [Int] -> IO() test lst = do print "test" rList lst rList :: [Int] -> IO () rList [] = return () rList (x:xs) = do rList xs main = do test [1..]

Ahora considere la siguiente versión modificada trivialmente de lo anterior. Cuando este programa se ejecuta en ghci, la memoria explota. La única diferencia es que print "test" ahora está asignada a x en el bloque de test do .

--Explode.hs module Main (main) where test :: [Int] -> IO() test lst = do x <- print "test" rList lst rList :: [Int] -> IO () rList [] = return () rList (x:xs) = do rList xs main = do test [1..]

¿Por qué cambiar print "test" a x <- print "test" hace que ghci explote?

P: Encontré esto cuando intentaba entender que la memoria explotaba al escribir un bytest perezoso para archivar en ghci , y el problema allí (creo) esencialmente se reduce a lo anterior. Gracias


Descargo de responsabilidad : no soy un experto en GHCi, y tampoco soy tan bueno con el núcleo de GHC. Ahora que he perdido mi credibilidad, intentemos entender qué sucede:

GHCi y CAFs

GHCi retiene todos los CAF evaluados :

Normalmente, cualquier evaluación de las expresiones de nivel superior (también conocidas como CAF o Formularios de aplicación constante) en los módulos cargados se conserva entre las evaluaciones.

Ahora puede que se pregunte por qué hay una gran diferencia entre ambas versiones. -ddump-simpl el núcleo con -ddump-simpl . Tenga en cuenta que es posible que desee eliminar -dsuppress-all cuando -dsuppress-all los programas usted mismo.

Volcados de tus programas.

Versión no explosiva:

❯ ghc SO.hs -ddump-simpl -fforce-recomp -O0 -dsuppress-all [1 of 1] Compiling Main ( SO.hs, SO.o ) ==================== Tidy Core ==================== Result size of Tidy Core = {terms: 29, types: 28, coercions: 0} $dShow_rq2 $dShow_rq2 = $fShow[] $fShowChar Rec { rList_reI rList_reI = / ds_dpU -> case ds_dpU of _ { [] -> return $fMonadIO (); : x_aho xs_ahp -> rList_reI xs_ahp } end Rec } main main = >> $fMonadIO (print $dShow_rq2 (unpackCString# "test")) (rList_reI (enumFrom $fEnumInt (I# 1))) main main = runMainIO main

La parte importante es la ubicación de [1..] , casi al final:

enumFrom $fEnumInt (I# 1))

Como puede ver, la lista no es un CAF. Pero, ¿qué pasa si en cambio usamos la versión de explosión?

Versión explosiva

❯ ghc SO.hs -ddump-simpl -fforce-recomp -O0 -dsuppress-all [1 of 1] Compiling Main ( SO.hs, SO.o ) ==================== Tidy Core ==================== Result size of Tidy Core = {terms: 32, types: 31, coercions: 0} $dShow_rq3 $dShow_rq3 = $fShow[] $fShowChar Rec { rList_reI rList_reI = / ds_dpV -> case ds_dpV of _ { [] -> return $fMonadIO (); : x_ahp xs_ahq -> rList_reI xs_ahq } end Rec } lst_rq4 lst_rq4 = enumFrom $fEnumInt (I# 1) main main = >>= $fMonadIO (print $dShow_rq3 (unpackCString# "test")) (/ _ -> rList_reI lst_rq4) main main = runMainIO main

De repente, hay una nueva expresión de nivel superior, a saber, lst_rq4 , que genera la lista. Y como se vio antes, GHCi retiene las evaluaciones de las expresiones de nivel superior, por lo que lst_rq4 también se mantendrá.

Ahora hay una opción para descartar las evaluaciones:

Al activar +r , todas las evaluaciones de las expresiones de nivel superior se descartan después de cada evaluación (aún se conservan durante una única evaluación).

Pero como "aún se conservan durante una única evaluación", incluso :set +r no te ayudará en este caso. Desafortunadamente, no puedo responder por qué GHC introduce una nueva expresión de nivel superior.

¿Por qué esto sucede incluso en código optimizado?

La lista sigue siendo una expresión de nivel superior:

main2 main2 = eftInt 1 2147483647

Curiosamente, GHC en realidad no crea una lista infinita, ya que Int está delimitado.

¿Cómo se puede deshacerse de la fuga?

En este caso, puede deshacerse de él si coloca la lista en la prueba:

test = do x <- print "test" rList [1..]

Esto evitará que GHC cree una expresión de nivel superior.

Sin embargo, realmente no puedo dar un consejo general sobre esto. Desafortunadamente, mi Haskell-fu aún no es lo suficientemente bueno.