Что не так с использованием IORefs здесь? Вы в любом случае в IO с сетевыми операциями. IORef не всегда самое чистое решение, но, похоже, в этом случае они хорошо справляются со своей задачей.
В любом случае, ради ответа на вопрос, давайте удалим IORef. Эти ссылки служат способом сохранения состояния, поэтому нам придется придумать альтернативный способ хранения информации о состоянии.
Псевдокод того, что мы хотим сделать, таков:
open the connection
10000 times:
send a message
receive the response
(keep track of how many responses are the message "PING")
print how many responses were the message "PING"
Блок с отступом 1000 times
можно абстрагировать в свою собственную функцию. Если мы хотим избежать IORef, то эта функция должна будет перейти в предыдущее состояние и создать следующее состояние.
main = withSocketsDo $ do
s <- socket AF_INET Datagram defaultProtocol
hostAddr <- inet_addr host
let sendMsg = sendAllTo s (B.pack "ping") (SockAddrInet port hostAddr)
recvMsg = fst `fmap` recvFrom s 1024
(c,f) <- ???
print c
sClose s
Итак, вопрос в следующем: что мы поместим в ???
место? Нам нужно определить какой-то способ «выполнить» действие ввода-вывода, получить его результат и каким-либо образом изменить состояние с этим результатом. Нам также нужно знать, сколько раз это сделать.
performRepeatedlyWithState :: a -- some state
-> IO b -- some IO action that yields a value
-> (a -> b -> a) -- some way to produce a new state
-> Int -- how many times to do it
-> IO a -- the resultant state, as an IO action
performRepeatedlyWithState s _ _ 0 = return s
performRepeatedlyWithState someState someAction produceNewState timesToDoIt = do
actionresult <- someAction
let newState = produceNewState someState actionResult
doWithState newState someAction produceNewState (pred timesToDoIt)
Все, что я сделал здесь, это записал сигнатуру типа, которая соответствовала тому, что я сказал выше, и произвела относительно очевидную реализацию. Я дал всем очень многословное имя, чтобы, надеюсь, было ясно, что именно означает эта функция. Оборудованный этой простой функцией, нам просто нужно ее использовать.
let origState = (0,0)
action = ???
mkNewState = ???
times = 10000
(c,f) <- performRepeatedlyWithState origState action mkNewState times
Я заполнил простые параметры здесь. Исходное состояние (c,f) = (0,0)
, и мы хотим выполнить это 10000 раз. (Или это 10001?) Но как должны выглядеть action
и mkNewState
? action
должен иметь тип IO b
; это какое-то IO-действие, которое производит что-то .
action = sendMsg >> recvMsg
Я связал sendMsg
и recvMsg
с выражениями из вашего кода ранее. Действие, которое мы хотим выполнить, это отправить сообщение, а затем получить сообщение. Значение, которое производит это действие, является полученным сообщением.
Теперь, как должен выглядеть mkNewState
? Он должен иметь тип a -> b -> a
, где a
- это тип состояния, а b
- тип результата действия.
mkNewState (c,f) val = if (B.unpack val) == "PING"
then (succ c, f)
else (c, succ f)
Это не самое чистое решение, но у вас есть общее представление? Вы можете заменить IORefs, написав функцию, которая рекурсивно вызывает себя, передавая дополнительные параметры для отслеживания состояния. Точно такая же идея воплощена в предложении foldM , предложенном по аналогичному вопросу.
Схемы взрыва, как предполагает Натан Хауэлл, были бы мудрыми, чтобы не создавать большого толпа в succ (succ (succ ...)))
в вашем штате:
mkNewState (!c, !f) val = ...