使用ReaderT和StateT Monads拼接功能

时间:2012-10-09 07:48:41

标签: haskell monad-transformers state-monad

我从Haskell开始,我需要使用ReaderTStateT Monads帮助拼接功能。

理想情况下,如果可能的话,所有函数都将具有相同的签名(我理解Monads应该帮助的那些),并且能够读取环境并改变状态。

下面的代码应该只使用初始环境openClientSocketinitialNetwork中打开一个套接字,在链中注入socketaddress,然后调用作为参数传递的“next”函数func(在本例中为函数sendMsg)。

但是我面临着多个问题:

  • a)如何调用func中传递的openClientConnection

  • b)HNT Monad与其他Monad组成(?)。我如何阅读和操纵内蒙纳德?

有人可以请帮我修复此代码,如果可能的话,导出/解释“理想”模式以实现主题中描述的目标。

非常感谢

import Control.Monad.Reader
import Control.Monad.State

import Network.Socket
import Network.Multicast

_MULTICAST_IP_ADDR_ :: HostName
_MULTICAST_IP_ADDR_ = "224.0.0.99"

_MULTICAST_PORT_ :: PortNumber
_MULTICAST_PORT_ = 9999

data NetworkEnv = NetworkEnv {
    getMulticastIP :: HostName,
    getMulticastPort :: PortNumber
} deriving (Show)
type NetworkEnvT = ReaderT NetworkEnv

data ClientSocket = ClientSocket { 
    getClientSocket :: Socket, 
    getAddress:: SockAddr 
} deriving (Show)
type ClientSocketT = StateT ClientSocket

type HNT m = ClientSocketT (NetworkEnvT m)

initialNetwork :: NetworkEnv
initialNetwork = NetworkEnv { getMulticastIP = _MULTICAST_IP_ADDR_, getMulticastPort = _MULTICAST_PORT_ } 


openClientConnection :: HNT m a -> m a 
openClientConnection func = do
    env <- ask
    (sock, addr) <- liftIO $ multicastSender (getMulticastIP env) (getMulticastPort env)
    put $ ClientSocket sock addr
    func   -- <== How do I call func (sendMsg) here ??


sendMsg :: String -> HNT IO ()
sendMsg msg = do
    NetworkEnv ip port <- ask
    ClientSocket sock addr <- get
    _ <- liftIO $ sendTo sock msg addr
    liftIO $ print "done"

doRun = runReaderT ( openClientConnection . (sendMsg "Hello") ) initialNetwork 

1 个答案:

答案 0 :(得分:0)

我管理下面的解决方案;我确信它可以改进,错误处理不完全到位,但它确实遵守合同。

从我的所有阅读材料中,真正,真正地帮助我的是来自MartinGrabmüller的this paper并意识到该程序在Monad中运行 :它不是函数调用的参数(多年的OO编程和注入调用)

import Control.Monad.Reader
import Control.Monad.State
import Control.Monad.Writer
import Control.Monad.Error

import Network.Socket
import Network.Multicast

_MULTICAST_IP_ADDR_ :: HostName
_MULTICAST_IP_ADDR_ = "224.0.0.99"

_MULTICAST_PORT_ :: PortNumber
_MULTICAST_PORT_ = 9999

data NetworkEnv = NetworkEnv {
    getMulticastIP :: HostName,
    getMulticastPort :: PortNumber
} deriving (Show)


data ClientSocket = ClientSocket { 
    getClientSocket :: Socket, 
    getAddress:: SockAddr 
} deriving (Show)


type HNError = String
type LogMessages = [String]

type HNT a = ReaderT NetworkEnv (ErrorT HNError (WriterT LogMessages (StateT ClientSocket IO))) a

runHNT :: ClientSocket -> NetworkEnv -> HNT a -> IO( (Either HNError a, LogMessages), ClientSocket) 
runHNT st env app = runStateT (runWriterT ( runErrorT (runReaderT app env) )) st

initialNetwork :: NetworkEnv
initialNetwork = NetworkEnv { getMulticastIP = _MULTICAST_IP_ADDR_, getMulticastPort = _MULTICAST_PORT_ }

initialState :: ClientSocket
initialState = ClientSocket undefined undefined

openClientConnection :: HNT ()
openClientConnection = do
    env <- ask
    (sock, addr) <- liftIO $ multicastSender (getMulticastIP env) (getMulticastPort env)
    put $ ClientSocket sock addr


sendMsg :: String -> HNT ()
sendMsg msg = do
    ClientSocket sock addr <- get
    _ <- liftIO $ sendTo sock msg addr
    tell ["Sent " ++ msg ++ " to socket" ]

myApp :: HNT ()
myApp = do
    env <- ask
    liftIO $ print (show env)


doRun :: IO ((Either HNError (), LogMessages), ClientSocket)
doRun = runHNT initialState initialNetwork ( openClientConnection >> sendMsg "Hello" >> myApp )