Designing complex Haskell programs

I'm trying to take the training wheels of and moving more of my code base to Haskell from C++ but finding it increasingly tricky. I have a subset of a gossip protocol written in C++. When a server comes online it connects to 1 or more nodes already in the cluster and get data from them about other nodes they know of. The new node merges the information and keeps a copy of the merged view. Every so often it contacts the nodes it knows about and refreshes the merged view. It also must have the up to date view ready to be sent in response to a new node joining. I currently can't wrap my head around how to maintain this state. How would a more experienced Haskeller approach this problem? Code is OK if it demonstrates a particular point but I'm more interested in the line of thought that would go into designing a solution as I suspect that'll be more useful as I get further into the migration. As a gauge to you for my current level in Haskell. I read and understand most Haskell programs fine. I write some but currently heavily rely on hackage/hoogle docs for APIs, even some common ones. Thanks

Generally speaking, state lives on the call stack in functional programming
languages that have tail call elimination. Modification of the state is
done by recursion with a new value for the state. This is more or less
equivalent to a "do while" loop in imperative programming.
myServer :: State -> IO ()
myServer state = do
state' <- updateState state
myServer state'
For the concurrency, Control.Concurrent or Cloud Haskell (for a higher
level Erlang-like approach) is probably the way to go here. Parallel and
Concurrent Programming in Haskell is a great resource:
http://chimera.labs.oreilly.com/books/1230000000929
On Fri, Jan 3, 2014 at 8:45 AM, Courtney Robinson
I'm trying to take the training wheels of and moving more of my code base to Haskell from C++ but finding it increasingly tricky.
I have a subset of a gossip protocol written in C++. When a server comes online it connects to 1 or more nodes already in the cluster and get data from them about other nodes they know of.
The new node merges the information and keeps a copy of the merged view. Every so often it contacts the nodes it knows about and refreshes the merged view. It also must have the up to date view ready to be sent in response to a new node joining.
I currently can't wrap my head around how to maintain this state. How would a more experienced Haskeller approach this problem? Code is OK if it demonstrates a particular point but I'm more interested in the line of thought that would go into designing a solution as I suspect that'll be more useful as I get further into the migration.
As a gauge to you for my current level in Haskell. I read and understand most Haskell programs fine. I write some but currently heavily rely on hackage/hoogle docs for APIs, even some common ones.
Thanks
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

Thanks to both of you for your reply.
I have something similar to your example Bob, wasn't sure if it was a good
way forward. Plus it fell apart when I tried contacting multiple hosts on
different threads using forkIO. But with Daniel's response I'll look into
MVars.
Thanks again
On Fri, Jan 3, 2014 at 5:16 PM, Bob Ippolito
Generally speaking, state lives on the call stack in functional programming languages that have tail call elimination. Modification of the state is done by recursion with a new value for the state. This is more or less equivalent to a "do while" loop in imperative programming.
myServer :: State -> IO () myServer state = do state' <- updateState state myServer state'
For the concurrency, Control.Concurrent or Cloud Haskell (for a higher level Erlang-like approach) is probably the way to go here. Parallel and Concurrent Programming in Haskell is a great resource: http://chimera.labs.oreilly.com/books/1230000000929
On Fri, Jan 3, 2014 at 8:45 AM, Courtney Robinson
wrote: I'm trying to take the training wheels of and moving more of my code base to Haskell from C++ but finding it increasingly tricky.
I have a subset of a gossip protocol written in C++. When a server comes online it connects to 1 or more nodes already in the cluster and get data from them about other nodes they know of.
The new node merges the information and keeps a copy of the merged view. Every so often it contacts the nodes it knows about and refreshes the merged view. It also must have the up to date view ready to be sent in response to a new node joining.
I currently can't wrap my head around how to maintain this state. How would a more experienced Haskeller approach this problem? Code is OK if it demonstrates a particular point but I'm more interested in the line of thought that would go into designing a solution as I suspect that'll be more useful as I get further into the migration.
As a gauge to you for my current level in Haskell. I read and understand most Haskell programs fine. I write some but currently heavily rely on hackage/hoogle docs for APIs, even some common ones.
Thanks
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
-- Courtney Robinson courtney@crlog.info http://crlog.info 07535691628 (No private #s)

I wouldn't recommend going down the path of using IORef or MVar for
everything, it's not easy to build robust systems that way. Do you mind
showing the code that you tried that "fell apart"? I'm sure there's a
slightly different way to structure it that would work just fine, probably
using some kind of message passing.
On Fri, Jan 3, 2014 at 9:57 AM, Courtney Robinson
Thanks to both of you for your reply. I have something similar to your example Bob, wasn't sure if it was a good way forward. Plus it fell apart when I tried contacting multiple hosts on different threads using forkIO. But with Daniel's response I'll look into MVars.
Thanks again
On Fri, Jan 3, 2014 at 5:16 PM, Bob Ippolito
wrote: Generally speaking, state lives on the call stack in functional programming languages that have tail call elimination. Modification of the state is done by recursion with a new value for the state. This is more or less equivalent to a "do while" loop in imperative programming.
myServer :: State -> IO () myServer state = do state' <- updateState state myServer state'
For the concurrency, Control.Concurrent or Cloud Haskell (for a higher level Erlang-like approach) is probably the way to go here. Parallel and Concurrent Programming in Haskell is a great resource: http://chimera.labs.oreilly.com/books/1230000000929
On Fri, Jan 3, 2014 at 8:45 AM, Courtney Robinson
wrote: I'm trying to take the training wheels of and moving more of my code base to Haskell from C++ but finding it increasingly tricky.
I have a subset of a gossip protocol written in C++. When a server comes online it connects to 1 or more nodes already in the cluster and get data from them about other nodes they know of.
The new node merges the information and keeps a copy of the merged view. Every so often it contacts the nodes it knows about and refreshes the merged view. It also must have the up to date view ready to be sent in response to a new node joining.
I currently can't wrap my head around how to maintain this state. How would a more experienced Haskeller approach this problem? Code is OK if it demonstrates a particular point but I'm more interested in the line of thought that would go into designing a solution as I suspect that'll be more useful as I get further into the migration.
As a gauge to you for my current level in Haskell. I read and understand most Haskell programs fine. I write some but currently heavily rely on hackage/hoogle docs for APIs, even some common ones.
Thanks
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
-- Courtney Robinson courtney@crlog.info http://crlog.info 07535691628 (No private #s)
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

Didn't see your response, my gmail auto filter marks my haskell messages as
read and puts them in a folder so I have to explicitly check to see. Anyway:
The bit of code I'm stuck on is:
startServer :: NodeSettings -> IO ()
startServer conf = do
print "Bootstrapping, trying to reach configured seed nodes"
let cluster = G.bootstrapNode $ seedNodes conf
print "Initializing Gossip"
gossip cluster conf
print "Listening for client connections"
listenForClients cluster conf
print "Server shutting down..."
The recursive version I had that was similar to yours has long since gone
because I couldn't get it to work.
bootstrapNode returns [RemoteNode].
So at the moment the cluster info is fetched once and that's it, where it
falls apart in my head is when I try to change this so that cluster is
updated every n seconds.
listenForClients is on the main thread with each accepted connection run
with forkIO.
All I came up with was after "gossip conf" , I could do
forkIO someFn where
someFn = do
threadDelay n
let cluster = G.bootstrapNode $ seedNodes conf
but obviously this doesn't work because my "gossip" and "listenForClients"
functions already have an immutable version of cluster. So I'm not sure how
to get the updated version to those fns.
Bare in mind that listenForClients and gossip are doing something similar to
withSocketsDo $ do
sock <- listenOn $ PortNumber(fromInteger gossipPort)
so only accepting a new connection causes those threads to do anything, but
when a connection is accepted, if the request demands the cluster data
those fns shouldn't go off gathering the data and shouldn't send the
(probably) out dated one from the initialization but instead the one that's
been updated every n in the background.
Thanks
On Fri, Jan 3, 2014 at 6:17 PM, Bob Ippolito
I wouldn't recommend going down the path of using IORef or MVar for everything, it's not easy to build robust systems that way. Do you mind showing the code that you tried that "fell apart"? I'm sure there's a slightly different way to structure it that would work just fine, probably using some kind of message passing.
On Fri, Jan 3, 2014 at 9:57 AM, Courtney Robinson
wrote: Thanks to both of you for your reply. I have something similar to your example Bob, wasn't sure if it was a good way forward. Plus it fell apart when I tried contacting multiple hosts on different threads using forkIO. But with Daniel's response I'll look into MVars.
Thanks again
On Fri, Jan 3, 2014 at 5:16 PM, Bob Ippolito
wrote: Generally speaking, state lives on the call stack in functional programming languages that have tail call elimination. Modification of the state is done by recursion with a new value for the state. This is more or less equivalent to a "do while" loop in imperative programming.
myServer :: State -> IO () myServer state = do state' <- updateState state myServer state'
For the concurrency, Control.Concurrent or Cloud Haskell (for a higher level Erlang-like approach) is probably the way to go here. Parallel and Concurrent Programming in Haskell is a great resource: http://chimera.labs.oreilly.com/books/1230000000929
On Fri, Jan 3, 2014 at 8:45 AM, Courtney Robinson
wrote: I'm trying to take the training wheels of and moving more of my code base to Haskell from C++ but finding it increasingly tricky.
I have a subset of a gossip protocol written in C++. When a server comes online it connects to 1 or more nodes already in the cluster and get data from them about other nodes they know of.
The new node merges the information and keeps a copy of the merged view. Every so often it contacts the nodes it knows about and refreshes the merged view. It also must have the up to date view ready to be sent in response to a new node joining.
I currently can't wrap my head around how to maintain this state. How would a more experienced Haskeller approach this problem? Code is OK if it demonstrates a particular point but I'm more interested in the line of thought that would go into designing a solution as I suspect that'll be more useful as I get further into the migration.
As a gauge to you for my current level in Haskell. I read and understand most Haskell programs fine. I write some but currently heavily rely on hackage/hoogle docs for APIs, even some common ones.
Thanks
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
-- Courtney Robinson courtney@crlog.info http://crlog.info 07535691628 (No private #s)
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginnhttp://www.haskell.org/mailman/listinfo/beginners

Okay, for that kind of usage I think an MVar or something like it is your
best bet, since it really does sound like global state:
startServer :: NodeSettings -> IO ()
startServer conf = do
print "Bootstrapping, trying to reach configured seed nodes"
clusterVar <- newMVar . G.bootstrapNode $ seedNodes conf
print "Initializing Gossip"
gossip clusterVar conf
print "Listening for client connections"
listenForClients clusterVar conf
print "Server shutting down..."
In the clients when you need to read the latest state of the cluster, you
would use:
cluster <- readMVar clusterVar
To update the state, you might have something like this:
modifyMVar_ clusterVar (\_oldCluster -> return newCluster)
On Sun, Jan 5, 2014 at 12:42 AM, Courtney Robinson
Didn't see your response, my gmail auto filter marks my haskell messages as read and puts them in a folder so I have to explicitly check to see. Anyway:
The bit of code I'm stuck on is: startServer :: NodeSettings -> IO () startServer conf = do print "Bootstrapping, trying to reach configured seed nodes" let cluster = G.bootstrapNode $ seedNodes conf print "Initializing Gossip" gossip cluster conf print "Listening for client connections" listenForClients cluster conf print "Server shutting down..."
The recursive version I had that was similar to yours has long since gone because I couldn't get it to work. bootstrapNode returns [RemoteNode]. So at the moment the cluster info is fetched once and that's it, where it falls apart in my head is when I try to change this so that cluster is updated every n seconds.
listenForClients is on the main thread with each accepted connection run with forkIO.
All I came up with was after "gossip conf" , I could do forkIO someFn where
someFn = do threadDelay n let cluster = G.bootstrapNode $ seedNodes conf
but obviously this doesn't work because my "gossip" and "listenForClients" functions already have an immutable version of cluster. So I'm not sure how to get the updated version to those fns.
Bare in mind that listenForClients and gossip are doing something similar to
withSocketsDo $ do sock <- listenOn $ PortNumber(fromInteger gossipPort)
so only accepting a new connection causes those threads to do anything, but when a connection is accepted, if the request demands the cluster data those fns shouldn't go off gathering the data and shouldn't send the (probably) out dated one from the initialization but instead the one that's been updated every n in the background.
Thanks
On Fri, Jan 3, 2014 at 6:17 PM, Bob Ippolito
wrote: I wouldn't recommend going down the path of using IORef or MVar for everything, it's not easy to build robust systems that way. Do you mind showing the code that you tried that "fell apart"? I'm sure there's a slightly different way to structure it that would work just fine, probably using some kind of message passing.
On Fri, Jan 3, 2014 at 9:57 AM, Courtney Robinson
wrote: Thanks to both of you for your reply. I have something similar to your example Bob, wasn't sure if it was a good way forward. Plus it fell apart when I tried contacting multiple hosts on different threads using forkIO. But with Daniel's response I'll look into MVars.
Thanks again
On Fri, Jan 3, 2014 at 5:16 PM, Bob Ippolito
wrote: Generally speaking, state lives on the call stack in functional programming languages that have tail call elimination. Modification of the state is done by recursion with a new value for the state. This is more or less equivalent to a "do while" loop in imperative programming.
myServer :: State -> IO () myServer state = do state' <- updateState state myServer state'
For the concurrency, Control.Concurrent or Cloud Haskell (for a higher level Erlang-like approach) is probably the way to go here. Parallel and Concurrent Programming in Haskell is a great resource: http://chimera.labs.oreilly.com/books/1230000000929
On Fri, Jan 3, 2014 at 8:45 AM, Courtney Robinson
wrote: I'm trying to take the training wheels of and moving more of my code base to Haskell from C++ but finding it increasingly tricky.
I have a subset of a gossip protocol written in C++. When a server comes online it connects to 1 or more nodes already in the cluster and get data from them about other nodes they know of.
The new node merges the information and keeps a copy of the merged view. Every so often it contacts the nodes it knows about and refreshes the merged view. It also must have the up to date view ready to be sent in response to a new node joining.
I currently can't wrap my head around how to maintain this state. How would a more experienced Haskeller approach this problem? Code is OK if it demonstrates a particular point but I'm more interested in the line of thought that would go into designing a solution as I suspect that'll be more useful as I get further into the migration.
As a gauge to you for my current level in Haskell. I read and understand most Haskell programs fine. I write some but currently heavily rely on hackage/hoogle docs for APIs, even some common ones.
Thanks
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
-- Courtney Robinson courtney@crlog.info http://crlog.info 07535691628 (No private #s)
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginnhttp://www.haskell.org/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners

Okay, thanks. I'll do that.
On Sun, Jan 5, 2014 at 10:56 AM, Bob Ippolito
Okay, for that kind of usage I think an MVar or something like it is your best bet, since it really does sound like global state:
startServer :: NodeSettings -> IO () startServer conf = do print "Bootstrapping, trying to reach configured seed nodes" clusterVar <- newMVar . G.bootstrapNode $ seedNodes conf print "Initializing Gossip" gossip clusterVar conf print "Listening for client connections" listenForClients clusterVar conf print "Server shutting down..."
In the clients when you need to read the latest state of the cluster, you would use:
cluster <- readMVar clusterVar
To update the state, you might have something like this:
modifyMVar_ clusterVar (\_oldCluster -> return newCluster)
On Sun, Jan 5, 2014 at 12:42 AM, Courtney Robinson
wrote: Didn't see your response, my gmail auto filter marks my haskell messages as read and puts them in a folder so I have to explicitly check to see. Anyway:
The bit of code I'm stuck on is: startServer :: NodeSettings -> IO () startServer conf = do print "Bootstrapping, trying to reach configured seed nodes" let cluster = G.bootstrapNode $ seedNodes conf print "Initializing Gossip" gossip cluster conf print "Listening for client connections" listenForClients cluster conf print "Server shutting down..."
The recursive version I had that was similar to yours has long since gone because I couldn't get it to work. bootstrapNode returns [RemoteNode]. So at the moment the cluster info is fetched once and that's it, where it falls apart in my head is when I try to change this so that cluster is updated every n seconds.
listenForClients is on the main thread with each accepted connection run with forkIO.
All I came up with was after "gossip conf" , I could do forkIO someFn where
someFn = do threadDelay n let cluster = G.bootstrapNode $ seedNodes conf
but obviously this doesn't work because my "gossip" and "listenForClients" functions already have an immutable version of cluster. So I'm not sure how to get the updated version to those fns.
Bare in mind that listenForClients and gossip are doing something similar to
withSocketsDo $ do sock <- listenOn $ PortNumber(fromInteger gossipPort)
so only accepting a new connection causes those threads to do anything, but when a connection is accepted, if the request demands the cluster data those fns shouldn't go off gathering the data and shouldn't send the (probably) out dated one from the initialization but instead the one that's been updated every n in the background.
Thanks
On Fri, Jan 3, 2014 at 6:17 PM, Bob Ippolito
wrote: I wouldn't recommend going down the path of using IORef or MVar for everything, it's not easy to build robust systems that way. Do you mind showing the code that you tried that "fell apart"? I'm sure there's a slightly different way to structure it that would work just fine, probably using some kind of message passing.
On Fri, Jan 3, 2014 at 9:57 AM, Courtney Robinson
wrote: Thanks to both of you for your reply. I have something similar to your example Bob, wasn't sure if it was a good way forward. Plus it fell apart when I tried contacting multiple hosts on different threads using forkIO. But with Daniel's response I'll look into MVars.
Thanks again
On Fri, Jan 3, 2014 at 5:16 PM, Bob Ippolito
wrote: Generally speaking, state lives on the call stack in functional programming languages that have tail call elimination. Modification of the state is done by recursion with a new value for the state. This is more or less equivalent to a "do while" loop in imperative programming.
myServer :: State -> IO () myServer state = do state' <- updateState state myServer state'
For the concurrency, Control.Concurrent or Cloud Haskell (for a higher level Erlang-like approach) is probably the way to go here. Parallel and Concurrent Programming in Haskell is a great resource: http://chimera.labs.oreilly.com/books/1230000000929
On Fri, Jan 3, 2014 at 8:45 AM, Courtney Robinson
wrote:
I'm trying to take the training wheels of and moving more of my code base to Haskell from C++ but finding it increasingly tricky.
I have a subset of a gossip protocol written in C++. When a server comes online it connects to 1 or more nodes already in the cluster and get data from them about other nodes they know of.
The new node merges the information and keeps a copy of the merged view. Every so often it contacts the nodes it knows about and refreshes the merged view. It also must have the up to date view ready to be sent in response to a new node joining.
I currently can't wrap my head around how to maintain this state. How would a more experienced Haskeller approach this problem? Code is OK if it demonstrates a particular point but I'm more interested in the line of thought that would go into designing a solution as I suspect that'll be more useful as I get further into the migration.
As a gauge to you for my current level in Haskell. I read and understand most Haskell programs fine. I write some but currently heavily rely on hackage/hoogle docs for APIs, even some common ones.
Thanks
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
-- Courtney Robinson courtney@crlog.info http://crlog.info 07535691628 (No private #s)
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginnhttp://www.haskell.org/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
_______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
-- Courtney Robinson courtney@crlog.info http://crlog.info 07535691628 (No private #s)

Bob Ippolito
writes: I wouldn't recommend going down the path of using IORef or MVar for everything, it's not easy to build robust systems that way. ...
On Fri, Jan 3, 2014 at 9:57 AM, Courtney Robinson wrote: ... it fell apart when I tried contacting multiple hosts on different threads using forkIO. ...
Hi Bob and Daniel, Out of interest, is there some reason you don't mention TVar's? I thought they are the (modern) way to "build robust systems" with "different threads using forkIO"(?) Cheers AntC

Hi AntC,
Out of interest, is there some reason you don't mention TVar's?
The use case here seemed to be more about having shared data which is only read most of the time and updated occasionally. So the MVar seems to be a quite good fit for this.
I thought they are the (modern) way to "build robust systems" with "different threads using forkIO"(?)
I think it really depends how your threads are accessing the shared data. Sometimes the rollbacks of a STM might hurt you more than waiting for the lock of a MVar. You could use a TVar here, but you have to be aware, that your threads might operate on outdated data - if one thread is currently updating the data - and this might be an issue for your system. If I'm getting it correctly, then a MVar can't be read by mutliple threads at once, even if all threads are only reading from it. That's an advantage of the TVar. So thinking again about it, having only occasionally writes from favorably only one thread and reads from multiple threads, then indeed the TVar might be a quite good fit for this use case. Greetings, Daniel

Hi all, In my case I don't really care which version of the data a thread sees, from the apps point of view the data is correct at the time it is fetched. I was playing around with this idea and it's actually what the multi-version concurrency control technique was developed for* (more or less anyway). I ended up writing http://hackage.haskell.org/package/Stasis to experiment with using an MVCC style in Haskell. Docs aren't currently being built on hackage, trying to figure out why but have a look at the source on github to see what I was thinking On Tue, Jan 14, 2014 at 12:23 PM, Daniel Trstenjak < daniel.trstenjak@gmail.com> wrote:
Hi AntC,
Out of interest, is there some reason you don't mention TVar's?
The use case here seemed to be more about having shared data which is only read most of the time and updated occasionally. So the MVar seems to be a quite good fit for this.
I thought they are the (modern) way to "build robust systems" with "different threads using forkIO"(?)
I think it really depends how your threads are accessing the shared data. Sometimes the rollbacks of a STM might hurt you more than waiting for the lock of a MVar.
You could use a TVar here, but you have to be aware, that your threads might operate on outdated data - if one thread is currently updating the data - and this might be an issue for your system.
If I'm getting it correctly, then a MVar can't be read by mutliple threads at once, even if all threads are only reading from it. That's an advantage of the TVar.
So thinking again about it, having only occasionally writes from favorably only one thread and reads from multiple threads, then indeed the TVar might be a quite good fit for this use case.
Greetings, Daniel _______________________________________________ Beginners mailing list Beginners@haskell.org http://www.haskell.org/mailman/listinfo/beginners
-- Courtney Robinson courtney@crlog.info http://crlog.info 07535691628 (No private #s)

Hi Courtney, On Fri, Jan 03, 2014 at 04:45:08PM +0000, Courtney Robinson wrote:
I currently can't wrap my head around how to maintain this state. How would a more experienced Haskeller approach this problem?
I think that it mostly boils down how your application loop should look like or which implementation options you have. If you're able to poll the received data, then you might just have something like: appLoop :: AppState -> IO () appLoop appState = do appState' <- pollData appState appLoop appState' You're reading the received data and modifying your state and then recursive call appLoop. If you want or have to use callbacks, then one option is to put your application state into an IORef (a mutable variable), which is shared by your application loop and your callback code e.g.: main :: IO () main = do appRef <- newIORef AppState { ... } setWhateverCallback $ \receivedData -> modifyIORef appRef $ \appData -> -- modify your appData by receivedData appLoop appRef appLoop :: IORef AppState -> IO () appLoop appRef = do modifyIORef appRef $ \appData -> -- do whatever you want with your appData appLoop appRef If you're using multiple threads, then you might want to use a MVar instead of an IORef. Greetings, Daniel
participants (4)
-
AntC
-
Bob Ippolito
-
Courtney Robinson
-
Daniel Trstenjak