I'm new to Haskell, so this is more a high-level conceptual question. I've read this: https://wiki.haskell.org/Real_World_Applications/Event_Driven_Applications and it has this:

run :: Domain -> [Event] -> IO ()

run dm [] = do
  events <- uiUpdate dm
  run dm events

run _ (EventExit:_) =
  return ()

run dm (e:es) =
  run (dmUpdate dm e) es

So uiUpdate would "generate" events in this case.

I am trying to understand how this works in an application where you need to push events. An example - say you have a GUI where you have a single int counter and three types of event sources:

  • Filesystem
  • Network
  • Human interaction

For simplicity, say the counter needs to be increased or decreased whenever any kind of event happens (e.g. new file added or deleted, HTTP call succeeded or failed, human typed on a keyboard or clicked with a mouse).

How do you push these events into the event loop? Most importantly, I'm not asking for "here's how you can do it", but "here's how real world Haskell applications work". Especially if there are different options that are used in practice.

  • 1
    I suspect the key is that in the linked article uiUpdate returns IO [Event], so that function apparently must do the I/O you are looking for, e.g. waiting for network events, scanning a filesystem, etc. Each time it does to, uiEvent brings zero or more events, that is, changes in the outside world to react upon.
    – 9000
    May 9, 2016 at 14:11
  • @9000 Agreed, but I guess what I'm asking for is what if I have multiple sources as above? Think 3 threads. So now I have 3 uiUpdate methods and need to get them pushed "into" 1 loop somehow. Just asking how this is done in real life. May 10, 2016 at 18:37

2 Answers 2


Let's suppose you have implementations of each of your three sources, updateUi, updateFS and updateNet, and further suppose they are all of type Domain -> IO [Event]. To gather them all into one list you can just concat the result of each

evUpdate :: Domain -> IO [Event]
evUpdate dm = do
  uis <- uiUpdate dm
  fss <- fsUpdate dm
  nets <- netUpdate dm
  concat [uis, fss, nets]

The pattern run dm [] is the event loop, in the form of tail recursion.

In this model you don't pass the intermediate Domains to uiUpdate whilst handling events.

Instead, if you wanted to get updates after every event, you'd have something like

run dm (e:es) = do
  dm' <- dmUpdate dm e
  es' <- evUpdate dm' es
  run dm' es'

evUpdate :: Domain -> [Event] -> IO [Event]
evUpdate dm es = do
  uis <- uiUpdate dm
  fss <- fsUpdate dm
  nets <- netUpdate dm
  concat [es, uis, fss, nets]

Or whatever strategy you want for polling events

  • The question is: how can I then push this into the event loop? I.e. I don't have events up front, but the loop is running and then I need to push the events into the loop as they happen. Jan 5, 2019 at 4:20
  • @levantpied updateAll replaces updateUi in your example. The "next batch" of events is pulled when the current is empty
    – Caleth
    Jan 5, 2019 at 9:58

In a realworld application, you're at the mercy of whatever event system you're using. Normally, the GUI library provides the event loop. So you just call into the API to generate the event and there is no difference to any API call.

For example in this Gtk tutorial, you could send an event in the button callback. https://www.stackbuilders.com/tutorials/haskell/gui-application/ Or you could add a callback using the Gtk API that catches all the events.

If you're building your own system from scratch with multiple threads, you just have to design your event loop to include functions for adding events. It will be the same as any other language or paradigm; you use a mutex and lock something, then send the event to wherever your events go. You can't really fill out the picture without knowing a lot more; you have to get into the weeds and actually be implementing something, because there isn't a single way to do it, and an event loop is more of an idea than a thing. It is just a function that looks at its arguments and calls other functions using callbacks.

The Gtk example is probably the most "real-world," because in the real world you're using an imperative GUI library that is maintaining (at least a copy of) most of your application state.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.