les animo a buscar una solución más funcional , por ejemplo, cargando los encabezados que necesitas por adelantado y pasándolos en alguna estructura de datos como, por ejemplo, Map
. Si no es conveniente pasarlo explícitamente, puede usar un transformador de mónada Reader
o State
para manejarlo por usted.
Dicho esto, puede lograrlo de la manera que desea utilizando usando unsafePerformIO
para crear una referencia global mutable para mantener su estructura de datos.
import Control.Concurrent.MVar
import qualified Data.Map as Map
import System.IO.Unsafe (unsafePerformIO)
memo :: MVar (Map.Map FilePath String)
memo = unsafePerformIO (newMVar Map.empty)
{-# NOINLINE memo #-}
getHeader :: FilePath -> IO String
getHeader fn = modifyMVar memo $ \m -> do
case Map.lookup fn m of
Just header -> return (m, header)
Nothing -> do header <- take 13 `fmap` readFile fn
return (Map.insert fn header m, header)
He utilizado un MVar
aquí para la seguridad hilo. Si no lo necesita, puede salirse con la suya usando un IORef
.
Además, tenga en cuenta NOINLINE
pragma en memo
para asegurarse de que la referencia solo se crea una vez. Sin esto, el compilador puede alinearlo en getHeader
, proporcionándole una nueva referencia cada vez.
Gracias. Si quisiera evitar la forma de riesgo insegura para que 'nota' devuelva una acción IO, ¿seguirá funcionando? Supongo que ahora se llamará cada vez que se deba evaluar. –
@DavidUnric: No, el memoization no funcionaría entonces, ya que se obtendría un nuevo vacío 'Map' cada vez, por lo que sería cargar el texto del archivo cada vez. Podrías crear el 'MVar' en un lugar y luego pasarlo, pero también podrías pasar el' Map' directamente. – hammar
hammar> Thx para la explicación. Reformé el código para que el encabezado ahora pase de la primera función IO justo antes de que se use. Aunque voy a marcar Your asnwer ya que muestra otro enfoque del que no tenía conocimiento. –