Haskell: Como tubulação o conteúdo de uma alça para outra em tempo real
Pergunta
Eu estou escrevendo um programa que executa um processo sub externo de forma interativa e eu preciso o conteúdo da alça de saída para ser a saída para stdout assim que ele estiver disponível. Eu tentei algo como isto:
main = do processInfo <- createProcess (proc "ghci" []){std_out = CreatePipe,
std_in = CreatePipe }
case processInfo of
(Just hIn, Just hOut, _, _) -> do mainloop hIn hOut
hClose hIn
hClose hOut
_ -> do error "Unable to start process"
mainloop :: Handle -> Handle -> IO ()
mainloop inh outh =
do ineof <- hIsEOF outh
if ineof
then return ()
else do inpStr <- hGetLine outh
putStrLn inpStr
mainloop inh outh
Mas isso não funciona, uma vez que só reconhece linha de saída por linha, de modo que qualquer saída na alça de saída processos que não é terminada por uma nova linha não aparecer. Eu tentei a mesma coisa com hGetContents mas produz o mesmo resultado. Eu li a documentação de ambos System.Process e System.IO e realmente não encontrei nada conclusivo.
Solução
hSetBuffering
é o que você está procurando, o padrão (em Unix, pelo menos) é a linha de buffering. Use-o sobre a stdin antes de iniciar o loop principal
hSetBuffering hIn NoBuffering
e opcionalmente também no punho saída se você quiser ver os resultados imediatamente no lado de saída. Note, no entanto, que a desativação buffer pode diminuir drasticamente o desempenho.
Outras dicas
Eu sou muito mais um novato Haskell, mas lembro-me vir através de um exemplo recente de processamento do personagem-by-entrada. É hSetBuffering possivelmente o que você está procurando?
Buffer é uma coisa, mas você também está usando gGetLine
que irá esperar por uma linha inteira (ou fim de arquivo). Considere o uso de hGetChar
se você realmente quer ler um caractere no momento.
E btw, uma outra maneira de superar o buffer é usar hFlush
.