No use unsafeInterleaveIO
o cualquier IO perezosa para ese asunto. Este es precisamente el problema que las iteraciones fueron creadas para abordar: evitar la IO lenta, lo que proporciona una administración de recursos impredecible. El truco está en . Nunca compile la lista y transmítala constantemente utilizando repeticiones hasta que haya terminado de usarla. Usaré ejemplos de mi propia biblioteca, pipes
, para demostrar esto.
En primer lugar, definir:
import Control.Monad
import Control.Monad.Trans
import Control.Pipe
-- Demand only 'n' elements
take' :: (Monad m) => Int -> Pipe a a m()
take' n = replicateM_ n $ do
a <- await
yield a
-- Print all incoming elements
printer :: (Show a) => Consumer a IO r
printer = forever $ do
a <- await
lift $ print a
Ahora vamos a ser malo con nuestro usuario y la demanda que producen la lista muy grande para nosotros:
prompt100 :: Producer Int IO()
prompt100 = replicateM_ 1000 $ do
lift $ putStrLn "Enter an integer: "
n <- lift readLn
yield n
Ahora, vamos a ejecutarlo:
>>> runPipe $ printer <+< take' 1 <+< prompt100
Enter an integer:
3<Enter>
3
Solo solicita al usuario un entero, ¡ya que solo pedimos un entero!
Si desea reemplazar prompt100
con salida de getLargeList
, que acaba de escribir:
yourProducer :: Producer b IO()
yourProducer = do
xs <- lift getLargeList
mapM_ yield xs
... y luego ejecutar:
>>> runPipe $ printer <+< take' 1 <+< yourProducer
Esto se perezosamente transmitir la lista y nunca construir el lista en la memoria, todo sin usar hacks inseguros IO
. Para cambiar la cantidad de elementos que exige, simplemente cambie el valor que pase a take'
Para obtener más ejemplos como este, lea pipes
tutorial en Control.Pipe.Tutorial
.
Para obtener más información acerca de por qué la IO lenta causa problemas, lea las diapositivas originales de Oleg sobre el tema, que puede encontrar en here.Él hace un gran trabajo explicando los problemas con el uso de IO perezoso. Cada vez que te sientas obligado a usar IO perezoso, lo que realmente quieres es una biblioteca iteratee.
Quizás esto ayude. http://stackoverflow.com/questions/3270255/is-haskells-mapm-not-lazy –
Anton, he leído este tema, pero no encontré la respuesta: ¿hay una alternativa de mapM para cálculos perezosos? –
@DmitryBespalov No con el mismo tipo de firma. 'Monad' no tiene una abstracción para diferir los efectos hasta más tarde, y eso es lo que tendrías que hacer para que' mapM' sea más flojo. – Carl