哈斯克尔懒惰 - 我如何迫使IO更快地发生? [英] Haskell laziness - how do I force the IO to happen sooner?
问题描述
import Network
import System.IO
import Network.HTTP.Headers
acceptLoop :: Socket - > IO()
acceptLoop s = do
(handle,hostname,_)< - 接受s
putStrLn(来自++主机名的接受连接)
text< - hGetContents处理
让lns =行文本
hds =尾部lns
打印$ parseHeaders hds
h关闭句柄
acceptLoop s
main :: IO()
main = do
s< - listenOn(PortNumber 8080)
acceptLoop s
感谢,
Rob
跟进
全部答案是有帮助的。下面的代码工作,但没有使用bytestrings,建议,但。一个后续问题:可以使用标准库中的一些函数替代 ioTakeWhile
,可能在Control.Monad中?
ioTakeWhile ::(a - > Bool) - > [IO a] - > IO [a]
ioTakeWhile pred actions = do
x< - 头部动作
if pred x
then(ioTakeWhile pred(tail actions))>> = \xs - > return(x:xs)
else return []
acceptLoop :: Socket - > IO()
acceptLoop s = do
(handle,hostname,_)< - 接受s
putStrLn(来自++主机名的接受连接)
let lineActions = repeat (hGetLine句柄)
行< - ioTakeWhile(/ =\r)lineActions
打印行
h关闭句柄
你的问题是使用 hGetContents
套接字关闭。您可以尝试解析输入的最后一行,直到连接终止才会知道该输入。
解决方案:获取与您一样多的数据需要(或可用),然后终止连接。
这很晚了,我很累,但这是一个我不知道的解决方案(阅读:丑陋的罪过) :你可以移动到字节串(无论如何应该这样做)并使用 hGetNonBlocking 或 hGetSome
而不是 hGetContents
。或者,您可以持续<
import Network
import System.IO
import Network.HTTP.Headers
Import Control.Monad
将合格的Data.ByteString.Char8导入为B
导入Data.ByteString(hGetSome)
acceptLoop :: Socket - > IO()
acceptLoop s = do
(handle,hostname,_)< - 接受s
putStrLn(来自++主机名的接受连接)
printHeaders句柄B.空
h关闭句柄
其中
printHeaders hs = do
t < - hGetSome h 4096
let str = B.append st - 低效!
loop = printHeaders h str
case(parseHeaders。tail。lines)(B.unpack str)of
Left _ - >循环
Right x
|长度x < 3 - >循环
|否则 - > print x
main :: IO()
main = do
hSetBuffering stdin NoBuffering
s< - listenOn(PortNumber 8080)
forever $ acceptLoop s
I just started learning Haskell. Below is some code written in an imperative style that implements a simple server -- it prints out the HTTP request headers. Besides the fact that I need to rethink it in Haskell, to work with lazy lists and higher order functions, I'd like to see clearly why it does not do what I intended. It is always one behind -- I hit it with a request, nothing happens, hit it again, it prints the first request, hit it the 3rd time, it prints the 2nd request, etc. Why is that? And what is a minimal change to this code that would cause it to print right when the request came in?
import Network
import System.IO
import Network.HTTP.Headers
acceptLoop :: Socket -> IO ()
acceptLoop s = do
(handle, hostname, _) <- accept s
putStrLn ("Accepted connection from " ++ hostname)
text <- hGetContents handle
let lns = lines text
hds = tail lns
print $ parseHeaders hds
hClose handle
acceptLoop s
main :: IO ()
main = do
s <- listenOn (PortNumber 8080)
acceptLoop s
thanks, Rob
Followup
All the answers were helpful. The code below works, but does not use bytestrings, as suggested, yet. A followup question: can ioTakeWhile
be replaced by using some functions from the standard libraries, maybe in Control.Monad?
ioTakeWhile :: (a -> Bool) -> [IO a] -> IO [a]
ioTakeWhile pred actions = do
x <- head actions
if pred x
then (ioTakeWhile pred (tail actions)) >>= \xs -> return (x:xs)
else return []
acceptLoop :: Socket -> IO ()
acceptLoop s = do
(handle, hostname, _) <- accept s
putStrLn ("Accepted connection from " ++ hostname)
let lineActions = repeat (hGetLine handle)
lines <- ioTakeWhile (/= "\r") lineActions
print lines
hClose handle
Your problem is using hGetContents
will get all contents on the handle till the socket closes. You follow this call by trying to parse the last line of the input, which won't be known till the connection terminates.
The solution: get as much data as you need (or is available) then terminate the connection.
It's late and I'm tired but here's a solution I know is non-optimal (read: ugly as sin): You can move to bytestrings (should do this anyway) and use hGetNonBlocking or hGetSome
instead of hGetContents
. Alternatively, you can hGetLine
(blocking) continually till the parse succeeds to your satisfaction:
import Network
import System.IO
import Network.HTTP.Headers
import Control.Monad
import qualified Data.ByteString.Char8 as B
import Data.ByteString (hGetSome)
acceptLoop :: Socket -> IO ()
acceptLoop s = do
(handle, hostname, _) <- accept s
putStrLn ("Accepted connection from " ++ hostname)
printHeaders handle B.empty
hClose handle
where
printHeaders h s = do
t <- hGetSome h 4096
let str = B.append s t -- inefficient!
loop = printHeaders h str
case (parseHeaders . tail . lines) (B.unpack str) of
Left _ -> loop
Right x
| length x < 3 -> loop
| otherwise -> print x
main :: IO ()
main = do
hSetBuffering stdin NoBuffering
s <- listenOn (PortNumber 8080)
forever $ acceptLoop s
这篇关于哈斯克尔懒惰 - 我如何迫使IO更快地发生?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!