1

我从 Haskell 开始,我需要帮助使用ReaderTStateTMonads 将函数拼接在一起。

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

下面的代码应该openClientSocket使用初始环境简单地打开一个 Socket ,在链中initialNetwork注入socketand ,然后调用作为参数传递的“下一个”函数(在这种情况下是函数)。addressfuncsendMsg

但是我面临多个问题:

  • a)我应该如何调用传入funcopenClientConnection

  • b) HNTMonad 与其他 Monad 组成(?)。我如何阅读和操作内部 Monads ?

那么有人可以帮我修复这段代码,如果可能的话,派生/解释“理想”模式以实现主题中描述的目标。

非常感谢

湾。

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 
4

1 回答 1

1

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

从我所有的阅读中,真正帮助我的是Martin Grabmüller 的这篇论文,并意识到程序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 )     
于 2012-10-09T15:36:33.100 回答