IO si verifica in modo non corretto quando si utilizza getLine e putStr

Sono un principiante Haskell, sto appena cominciando a girarmi intorno alla Monads, ma non lo capisco ancora. Sto scrivendo un gioco che consiste nel chiedere all’utente l’input e la risposta. Ecco una versione semplificata della mia funzione:

getPoint :: IO Point getPoint = do putStr "Enter x: " xStr <- getLine putStr "Enter y: " yStr  IO (Board, Player) completeUserTurn (board, player) = do putStr $ "Enter some value: " var1 <- getLine putStr $ "Enter another value: " var2 <- getLine putStr $ "Enter a point this time: " point <- getPoint if (... the player entered legal values ...) then do putStr $ "This is what would happen if you did that: {stuff} do you want to do that? (y/n) " continue <- getLine if continue == "y" then return (...updated board..., ...updated player...) else completeUserTurn (board, player) else do putStr "Invalid Move!\n" completeUserTurn (board, player) 

Quello che sta succedendo è che i prompt appariranno fuori ordine con il testo che dovrebbe apparire prima del prompt.

Ecco un esempio di cosa sta succedendo dopo aver compilato il codice qui sopra:

1
Inserisci un valore: inserisci un altro valore: 2
3
4
Immettere un punto questa volta: immettere x: immettere y: y
È corretto? (Y / n):

Le audaci sono le cose che ho digitato.

Ovviamente, ho qualche errore concettuale importante, ma non so cosa. Si noti che funziona correttamente nell’interprete e fallisce una volta compilato.

Come ha detto Michael, il problema è il buffering. Per impostazione predefinita, l’output viene memorizzato nel buffer fino a quando non si stampa una nuova riga (o finché il buffer non è pieno se si dispone di righe molto lunghe), quindi si vedrà più spesso questo problema quando si tenta di eseguire prompt della stessa riga usando putStr come si sta facendo .

Suggerisco di definire una piccola funzione di aiuto come questa per occuparsi di fare il flushing per te:

 import System.IO prompt :: String -> IO String prompt text = do putStr text hFlush stdout getLine 

Ora puoi semplicemente farlo

 getPoint = do xStr <- prompt "Enter x: " yStr <- prompt "Enter y: " return $ Point (read xStr) (read yStr) 

L’IO sta accadendo nell’ordine corretto. Il problema è il buffering. Se svuoti lo stdout dopo ogni putStr, dovrebbe funzionare come previsto. Dovrai importare hFlush e stdout da System.IO .

Il problema non era con l’ordine delle operazioni nel codice IO. Il problema era l’input e l’output è di default bufferizzato quando si usa stdin e stdout. Ciò aumenta le prestazioni dell’IO in un’app, ma può causare l’apparizione di operazioni non funzionanti quando vengono utilizzati sia stdin che stdout.

Ci sono due soluzioni a questo. Puoi usare il metodo hFlush per forzare un handle (stdin o stdout) da svuotare. Ad esempio hFlush stdout , hFlush stdin . Una soluzione più semplice (che funziona bene per le app interattive) consiste nel disabilitare completamente il buffering. Puoi farlo chiamando i metodi hSetBuffering stdout NoBuffering e hSetBuffering stdin NoBuffering prima di iniziare il tuo programma (cioè metti quelle linee nel tuo metodo principale.