Considere el siguiente programa de Haskell. Estoy intentando programar en un "estilo de secuencia" donde las funciones operan en transmisiones (implementadas aquí simplemente como listas). Cosas como normalStreamFunc funcionan muy bien con listas perezosas. Puedo pasar una lista infinita a normalStreamFunc y sacar efectivamente otra lista infinita, pero con una función asignada a cada valor. Cosas como effectfulStreamFunc no funcionan tan bien. La acción IO significa que necesito evaluar toda la lista antes de poder obtener valores individuales. Por ejemplo, la salida del programa es la siguiente:Haskell arroyos con efectos IO
a
b
c
d
"[\"a\",\"b\"]"
pero lo que quiero es una manera de escribir effectfulStreamFunc para que el programa produce esto:
a
b
"[\"a\",\"b\"]"
dejando el resto de acciones sin evaluar. Puedo imaginar una solución que use inseguroPerformIO, pero digamos que lo estoy sacando de la mesa. Este es el programa:
import IO
normalStreamFunc :: [String] -> [String]
normalStreamFunc (x:xs) = reverse(x) : normalStreamFunc xs
effectfulStreamFunc :: [String] -> IO [String]
effectfulStreamFunc [] = return []
effectfulStreamFunc (x:xs) = do
putStrLn x
rest <- effectfulStreamFunc xs
return (reverse(x):rest)
main :: IO()
main = do
let fns = ["a", "b", "c", "d"]
es <- effectfulStreamFunc fns
print $ show $ take 2 es
Actualización:
Gracias a todos por los comentarios útiles y reflexivo. No había visto el operador sequence
antes, eso es útil para saber. Yo había pensado en una forma (menos elegante) para pasar alrededor de IO (String) valores en lugar de cadenas, sino por el estilo de programación que es de utilidad limitada, ya que quiero otras funciones de flujo para actuar sobre las cuerdas a sí mismos, no en acciones que pueden producir una cadena. Pero, basado en pensar a través de las otras respuestas, creo que veo por qué esto es irresoluble en general. En el caso simple presenté, lo que realmente quería era el operador sequence
, ya que yo estaba pensando que el ordenamiento corriente implicó un ordenamiento de las acciones. De hecho, ningún pedido de este tipo está necesariamente implícito. Esto se vuelve más claro para mí cuando pienso en una función de transmisión que toma dos flujos como entrada (por ejemplo, dos secuencias por pares). Si ambas transmisiones "entrantes" realizan IO, el orden de esas acciones IO no está definido (a menos que, por supuesto, lo definamos mediante secuenciación nosotros mismos en la mónada IO). Problema resuelto, ¡gracias a todos!
Si aún así como la notación hacerlo, se podría escribir la segunda parte de effectfulStreamFunc como: effectfulStreamFunc (x: xs) = let putAction = do putStrLn x return x en putAction: effectfulStreamFunc xs Creo que se lee un poco mejor. –
Buen punto. Supongo que la sintaxis do es ortogonal al problema de realmente ejecutar la mónada. –