Take the 2-minute tour ×

How can I issue multiple calls to SDL.pollEvent :: IO Event until the output is SDL.NoEvent and collect all the results into a list?

In imperative terms something like this:

events = []
event = SDL.pollEvent
while ( event != SDL.NoEvent ) {
        events.add( event )
        event = SDL.pollEvent
share|improve this question
add comment

5 Answers

up vote 4 down vote accepted

James Cook was so kind to extend monad-loops with this function:

unfoldWhileM  :: Monad  m => (a -> Bool) -> m a -> m [a]

used with SDL:

events <- unfoldWhileM (/= SDL.NoEvent) SDL.pollEvent
share|improve this answer
add comment

You could use something like:

takeWhileM :: (a -> Bool) -> IO a -> IO [a]
takeWhileM p act = do
  x <- act
  if p x
    then do
      xs <- takeWhileM p act
      return (x : xs)
      return []

Instead of:

  xs <- takeWhileM p act
  return (x : xs)

you can also use:

liftM (x:) (takeWhileM p act) yielding:

takeWhileM p act = do
  x <- act
  if p x
    then liftM (x:) (takeWhileM p act)
    else return []

Then you can use: takeWhileM (/=SDL.NoEvent) SDL.pollEvent

share|improve this answer
I'd suggest takeUntilM :: Monad m => (a -> Bool) -> m a -> m [a] (with appropriate return [x] when p x is false) to avoid information loss (especially from IO monads). That may look normal when it just SDL.NoEvent but it might be wrong for Left "system crash" :: Either String a. –  ony May 16 '10 at 17:49
A lot of variants of takeWhileM: stackoverflow.com/questions/1133800/haskell-monadic-takewhile/… –  KennyTM May 16 '10 at 18:47
@peaker: imho using monadic lists is more modular –  yairchu May 16 '10 at 20:44
@ony: Why lazy? This is polling events in the main loop of an SDL application. Logically, it's better to think of SDL pushing events to the program, not the program pulling events on demand. In fact, I think SDL will fall over if you don't clear the event queue quickly enough. Sometimes laziness doesn't make sense. –  C. A. McCann May 16 '10 at 22:42
@ony: lazy-IO is bug prone. But if one would still insist on doing it, I don't think that creating a lazy IO version of takeWhileM is a nice and modular approach. Transforming to a lazy list should be done as a separate step from takeWhile. this could be achieved using monadic lists. (shameless plug for my own answer below) –  yairchu May 16 '10 at 23:38
show 4 more comments

You can use monadic lists:

import Control.Monad.ListT (ListT)
import Control.Monad.Trans.Class (lift) -- transformers, not mtl
import Data.List.Class (takeWhile, repeat, toList)
import Prelude hiding (takeWhile, repeat)

getEvents :: IO [Event]
getEvents = 
    toList . takeWhile (/= NoEvent) $ do
        repeat ()
        lift pollEvent :: ListT IO Event

ListT from the "List" package on hackage.

share|improve this answer
Why "repeat ()"? –  Peaker May 16 '10 at 15:22
@peaker: repeat () :: ListT IO () is an infinite IO-monadic list containing values that don't matter (()). then we (>>) it with lift pollEvent so that for each element of the infinite list we pollEvent. takeWhile makes it a finite monadic list and then toList makes it :: IO [Event]. –  yairchu May 16 '10 at 15:54
That seems a bit weird.. Maybe makes more sense to use something like "repeatM (lift pollEvent)" ? –  Peaker May 17 '10 at 6:29
@peaker: yeah, repeatM pollEvent. I added repeatM to the github tree and it will be there in the next hackage version –  yairchu May 18 '10 at 7:00
add comment

Using these stubs for Event and pollEvent

data Event = NoEvent | SomeEvent
  deriving (Show,Eq)

instance Random Event where
  randomIO = randomRIO (0,1) >>= return . ([NoEvent,SomeEvent] !!)

pollEvent :: IO Event
pollEvent = randomIO

and a combinator, borrowed and adapted from an earlier answer, that stops evaluating the first time the predicate fails

spanM p a = do
  x <- a
  if p x then do xs <- spanM p a
                 return (x:xs)
         else return [x]

allows this ghci session, for example:

*Main> spanM (/= NoEvent) pollEvent 
share|improve this answer
very newbie friendly version, thank you too :) –  user341228 May 17 '10 at 8:09
add comment

i eventually stumbled over this code snippet in an actual SDL game from hackage

getEvents :: IO Event -> [Event] -> IO [Event]
getEvents pEvent es = do
  e <- pEvent
  let hasEvent = e /= NoEvent
  if hasEvent
   then getEvents pEvent (e:es)
   else return (reverse es)

thanks for your answers btw!

share|improve this answer
If that is so popular approach to pull out all events waiting in queue in one shot without processing it, than why SDL API doesn't provide it directly? That may help to avoid some sync. overhead for thread-safe queue. –  ony May 17 '10 at 13:06
add comment

Your Answer


