Collecting IO outputs into list
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 = S开发者_运维技巧DL.pollEvent
}
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
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
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.
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]
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!
精彩评论