拆分IO和计算

J F*_*sch 1 haskell

我想接收一个字符串,将其转换为列表并将列表的每个元素写入TChan.对于收到的每个字符串,我想分叉一个新进程.

我的问题是,当我尝试编译时,我得到与IO相关的错误:

Couldn't match expected type `IO ()' with actual type `[()]'
Run Code Online (Sandbox Code Playgroud)

要么

Couldn't match expected type `IO ()' with actual type `[IO ()]'
Run Code Online (Sandbox Code Playgroud)

虽然我完全理解错误并知道它的起源(至少是第一个错误 - )但我现在已经没有想法如何在我的示例中拆分IO计算并仍然实现我尝试做的事情.

write2TChan msg mtch = do
        let mymessages = words msg
        map (\x -> atomically $ writeTChan mtch x) mymessages
        return ()

main = withSocketsDo $ do
        s <- socket AF_INET Datagram defaultProtocol
        bindAddr <- inet_addr host
        bindSocket s (SockAddrInet port bindAddr)
        mtch <- newTChanIO
        let forever socket hosts = do
                (msg, host) <- receiveMessage socket
                return ()
                return (forkIO $ write2TChan msg mtch)
                --forkIO $ write2TChan msg mtch
                --tried w return () and above, same problem
                forever socket hosts
        forever s []
        sClose s
Run Code Online (Sandbox Code Playgroud)

ham*_*mar 6

map有类型map :: (a -> b) -> [a] -> [b],所以它总是返回一个列表.但是,monad中do块中的每个语句IO都必须具有IO a某些类型a.

你想要mapM :: Monad m => (a -> m b) -> [a] -> m [b]一个在monad中做同样事情的相关函数.由于您要忽略结果,我们可以使用变体mapM_ :: Monad m => (a -> m b) -> [a] -> m ().

write2TChan msg mtch = do
    let mymessages = words msg
    mapM_ (\x -> atomically $ writeTChan mtch x) mymessages
Run Code Online (Sandbox Code Playgroud)

对于您的其他功能,您应该可以forkIO直接使用.包装它return根本没有任何意义,因为它会产生类型IO (IO a),即一个动作返回一个动作.另外,return ()当它不是do块中的最后一个语句时,是一个无操作,所以你不需要它.

这应该工作

let forever socket hosts = do
         (msg, host) <- receiveMessage socket
         forkIO $ write2TChan msg mtch
         forever socket hosts
Run Code Online (Sandbox Code Playgroud)

但是,这个hosts论点目前总是[]如此,所以除非你打算对此做些什么,否则你也可以摆脱它.此外,我会避免使用该名称,forever因为该名称已经有一个常用的功能Control.Monad.