Considerare il seguente programma Haskell. Sto provando a programmare in uno "stile di flusso" in cui le funzioni operano su flussi (implementati qui semplicemente come elenchi). Cose come NormalStreamFunc funzionano alla grande con liste pigre. Posso passare una lista infinita a NormalStreamFunc ed estrarre efficacemente un'altra lista infinita, ma con una funzione mappata su ciascun valore. Cose come effectfulStreamFunc non funzionano così bene. L'azione IO significa che ho bisogno di valutare l'intera lista prima di poter estrarre i singoli valori. Ad esempio, l'output del programma è questo:Stream Haskell con effetti IO
a
b
c
d
"[\"a\",\"b\"]"
ma quello che voglio è un modo per scrivere effectfulStreamFunc modo che il programma produce questo:
a
b
"[\"a\",\"b\"]"
lasciando le azioni rimanenti non valutata. Posso immaginare una soluzione usando unsafePerformIO, ma diciamo che sto prendendo quello fuori dal tavolo. Ecco il programma:
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
Aggiornamento:
Grazie a tutti per il feedback disponibile e premuroso. Non avevo mai visto l'operatore sequence
, che è utile sapere. Avevo pensato ad un modo (meno elegante) di passare attorno ai valori IO (String) invece che alle stringhe, ma per lo stile di programmazione che è di utilità limitata, dal momento che voglio che altre funzioni di streaming agiscano sulle stringhe stesse, non su azioni che possono produrre una stringa. Ma, basandomi sul pensare attraverso le altre risposte, penso di capire perché questo sia irrisolvibile in generale. Nel caso semplice che ho presentato, quello che volevo veramente era l'operatore sequence
, poiché stavo pensando che l'ordinamento dei flussi implicasse un ordinamento sulle azioni. In realtà, nessun ordine di questo tipo è necessariamente implicito. Ciò diventa più chiaro per me quando penso a una funzione di flusso che richiede due flussi come input (ad esempio, l'aggiunta a coppie di due flussi). Se entrambi i flussi "in entrata" eseguono IO, l'ordinamento di quelle azioni IO non è definito (a meno che, ovviamente, non lo definiamo sequenziandolo noi stessi nella monade IO). Problema risolto, grazie a tutti!
Se ti piace ancora la notazione fare, si potrebbe scrivere la seconda clausola di effectfulStreamFunc come: effectfulStreamFunc (x: xs) = let putAction = do putStrLn x restituisci x in putAction: effectfulStreamFunc xs Penso che legga un po 'meglio. –
Buon punto. Suppongo che la sintassi di do sia ortogonale al problema di eseguire effettivamente la monade. –