una tipos parametros opciones mandar lista leer imprimir hacer funciones funcion foldl elem dropwhile datos como clases haskell iterate

tipos - take haskell



¿Qué pasa si un enumerador intenta consumir información? (2)

No hay nada de malo en que un enumerador consuma datos. Es un transformador iterativo, que bien puede introducir su propia entrada en su iteración. Mira la forma en que aplicas un enumerador a una iterada. También puede aplicar otro enumerador a una iteración aplicada a un enumerador.

La definición de Enumerator es:

type Enumerator a m b = Step a m b -> Iteratee a m b

La documentación indica que mientras que los datos de consumo de Iteratee, Enumerator s lo producen. Puedo entender cómo se pueden producir datos con este tipo:

enumStream :: (Monad m) => Stream a -> Enumerator a m b enumStream stream step = case step of Continue k -> k stream _ -> returnI step -- Note: ''stream'' is discarded

( enumEOF es más complicado que esto ... aparentemente verifica que el Iteratee no Continue después de recibir EOF , lo que genera un error si lo hace).

A saber, un Iteratee produce un Step cuando se ejecuta con runIteratee . Este Step se envía a mi enumerador, que lo suministra con un Stream para que pueda continuar. Mi enumerador devuelve la continuación resultante.

Una cosa se destaca en mí: este código se ejecuta en la mónada Iteratee . Eso significa que puede consumir datos, ¿verdad?

-- | Like ''enumStream'', but consume and discard a chunk from the input stream -- simply because we can. enumStreamWeird :: (Monad m) => Stream a -> Enumerator a m b enumStreamWeird stream step = do _ <- continue return -- Look, mommy, I''m consuming input! case step of Continue k -> k stream _ -> returnI step

La documentación indica que cuando un enumerador actúa como fuente y sumidero, se debe utilizar Enumeratee su lugar:

type Enumeratee ao ai m b = Step ai m b -> Iteratee ao m (Step ai m b)

Sin embargo, al parecer no tenía que hacerlo; Podría consumir información en la definición de un Enumerator , como lo demuestra mi función enumStreamWeird .

Mis preguntas son:

  • ¿Qué sucede si intenta "consumir" datos dentro de un enumStreamWeird , como hace enumStreamWeird ? ¿De dónde vienen los datos?

  • Incluso si no estamos lo suficientemente locos como para consumir datos en un enumerador, ¿es válido realizar acciones en la mónada subyacente en nombre del enumerador, en lugar de en nombre de la iteración que lee los datos que estamos produciendo?

La última pregunta podría estar menos relacionada con mi pregunta principal, pero estoy tratando de entender cómo un Enumerator hace lo que hace.


Sí, un enumerador puede consumir datos. Básicamente, un enumerador toma una iteración y la transforma en la misma iteración después de haber alimentado algunos elementos. Si el enumerador solicita una entrada, la iteración resultante solicitará una entrada.

Cómo se alimenta un enumerador a un iterado

Veamos cómo se alimenta un enumerador a una iterada:

-- | Feed an Enumerator to an Iteratee. feed :: Monad m => Iteratee a m b -> Enumerator a m b -> Iteratee a m b feed iteratee enumerator = Iteratee $ do step <- runIteratee iteratee runIteratee $ enumerator step

Nota: feed es un caso especial de >>== .

Primero, feed ejecuta el iterador hasta que esté listo para la entrada. Luego, pasa el primer Step del iterante al enumerador. El enumerador toma el relevo desde allí.

Esa última frase es muy importante. El enumerador puede hacer lo que quiera con su iterado. Puede descartar el iterado por completo si así lo desea. Sin embargo, un enumerador generalmente proporciona al iterado la entrada que tiene, y luego le devuelve el control al iterado.

Ejemplo 1: alimentar a los enumeradores a una iterada

Supongamos que tenemos una iteración que solicita tres cadenas y las imprime:

iter3 :: Iteratee String IO () iter3 = do lift $ putStrLn "Gimmie a string!" a <- head_ lift $ putStrLn a lift $ putStrLn "Gimmie another string!" b <- head_ lift $ putStrLn b lift $ putStrLn "Gimmie one more string!" c <- head_ lift $ putStrLn c lift $ putStrLn "Thank you!"

head_ se define en Data.Enumerator.List .

y un enumerador que alimenta su iterado una sola cadena:

getString :: Enumerator String IO a getString (Continue k) = do line <- lift getLine k (Chunks [line]) getString step = Iteratee $ return step

Cuando getString recibe una iteración que necesita más de un elemento, alimenta la iteración con el primer elemento. Entonces, getString necesitará los elementos restantes.

  • iter3 necesita tres elementos antes de que pueda regresar () .

  • iter3 `feed` getString necesita dos elementos.

  • iter3 `feed` getString `feed` getString necesita un elemento.

  • iter3 `feed` getString `feed` getString `feed` getString no necesita más elementos.

  • iter3 `feed` getString `feed` getString `feed` getString `feed` getString es equivalente a lo anterior. Esto es manejado por el segundo caso de getString .

Ejemplo 2: un enumerador que consume entrada

Considere un enumerador que consume entrada:

consumeEnum :: Enumerator String IO a consumeEnum step = do lift $ putStrLn "I take without giving" _ <- head_ Iteratee $ return step

¿Qué hace iter3 `feed` consumeEnum ? Eso puede ser respondido mirando la propia implementación de consumeEnum . Primero necesita un artículo y lo descarta. Luego entrega la antorcha a iter3 , que necesita tres elementos más.

Sin embargo, vuelva a mirar el combinador de feed . Comienza ejecutando iter3 , luego pasa su Step a consumeEnum . Esto significa "Gimmie a string!" Se imprimirá antes de que el control llegue a consumeEnum .