How could I refactor this so that eventually IORefs would not be necessary?
inc :: IORef Int -> IO ()
inc ref = modifyIORef ref (+1)
main = withSocketsDo $ do
s <- socket AF_INET Datagram defaultProtocol
c <- newIORef 0
f <- newIORef 0
hostAddr <- inet_addr host
time $ forM [0 .. 10000] $ \i -> do
sendAllTo s (B.pack "ping") (SockAddrInet port hostAddr)
(r, _) <- recvFrom s 1024
if (B.unpack r) == "PING" then (inc c) else (inc f)
c' <- readIORef c
print (c')
sClose s
return()
What's wrong with using IORefs here? You're in IO anyways with the networking operations. IORefs aren't always the cleanest solution, but they seem to do the job well in this case.
Regardless, for the sake of answering the question, let's remove the IORefs. These references serve as a way of keeping state, so we'll have to come up with an alternate way to keep the stateful information.
The pseudocode for what we want to do is this:
The chunk that is indented under
1000 times
can be abstracted into its own function. If we are to avoid IORefs, then this function will have to take in a previous state and produce a next state.So the question is this: what do we put at the
???
place? We need to define some way to "perform" an IO action, take its result, and modify state with that result somehow. We also need to know how many times to do it.All I did here was write down the type signature that matched what I said above, and produced the relatively obvious implementation. I gave everything a very verbose name to hopefully make it apparent exactly what this function means. Equipped with this simple function, we just need to use it.
I've filled in the easy parameters here. The original state is
(c,f) = (0,0)
, and we want to perform this 10000 times. (Or is it 10001?) But what shouldaction
andmkNewState
look like? Theaction
should have typeIO b
; it's some IO action that produces something.I bound
sendMsg
andrecvMsg
to expressions from your code earlier. The action we want to perform is to send a message, and then receive a message. The value this action produces is the message received.Now, what should
mkNewState
look like? It should have the typea -> b -> a
, wherea
is the type of the State, andb
is the type of the action result.This isn't the cleanest solution, but do you get the general idea? You can replace IORefs by writing a function that recursively calls itself, passing extra parameters along in order to keep track of state. The exact same idea is embodied in the foldM solution suggested on the similar question.
Bang patterns, as Nathan Howell suggests, would be wise, to avoid building up a large thunk of
succ (succ (succ ...)))
in your state: