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.