7

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 } 

5 Answers 5

4

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 
Sign up to request clarification or add additional context in comments.

Comments

4

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) else return [] 

Instead of:

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

you can also use:

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

 takeWhileM :: (a -> Bool) -> IO a -> IO [a] 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

9 Comments

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.
@peaker: imho using monadic lists is more modular
@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.
@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)
|
2

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.

3 Comments

@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].
That seems a bit weird.. Maybe makes more sense to use something like "repeatM (lift pollEvent)" ?
@peaker: yeah, repeatM pollEvent. I added repeatM to the github tree and it will be there in the next hackage version
1

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 :: (Monad m) => (a -> Bool) -> m a -> m [a] 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 [SomeEvent,SomeEvent,NoEvent]

Comments

0

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!

1 Comment

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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.