Question:
How do I give an "IO SDL.Surface" to a function that expects an "SDL.Surface"?
I'd rather rethink my entire approach than resort to using something like "unsafePerformIO", unless this is actually the correct time to use it (which I doubt).
Further info:
I had a file filled with numbers and filepaths and I've parsed this file and loaded the images located at these paths into a list [(Int, IO SDL.Surface)]. Problem is, that the SDL.blitSurface function expects a normal SDL.Surface.
Error message:
Couldn't match type `IO SDL.Surface'
with `GHC.ForeignPtr.ForeignPtr SDL.SurfaceStruct'
Expected type: SDL.Surface
Actual type: IO SDL.Surface
I'm not sure that source code is necessary to answer the question, but I'll provide some anyway just in case it helps:
To load an image file I use:
loadImage :: FilePath -> IO SDL.Surface
loadImage [] = error "empty list"
loadImage a =
SDL.loadBMP a
To create the list of numbers and images I use:
createIDImageList :: [Tiletype] -> [(Int, IO SDL.Surface)]
createIDImageList a =
if null a then []
else [(tiletypeid $ a !! 0, loadImage (C8.unpack ( tiletypeimage ( a !! 0))))] ++ createIDImageList (tail a)
To retrieve the correct picture from this list, I use this function:
imageFromID :: Int -> [(Int, IO SDL.Surface)] -> Maybe (IO SDL.Surface)
imageFromID a b =
if null b then Nothing
else if a == (fst $ b !! 0) then Just (snd $ b !! 0)
else imageFromID a (tail b)
And finally I use the imageFromID with the SDL.blitSurface to draw the image, except that I can't due to IO.
Any time you end up with
[IO Foobar], what you probably want is actuallyIO [Foobar]. Thesequencefunction transforms one into the other. Or you can usemapMinstead ofmapwhen creating the list in the first place.In your example, it's a little more complicated, since we have
[(Int, IO Surface)]. Let me see what I can suggest...loadImageis an I/O action. It takes a filename and returns an IO action to load the image. YourcreateIDImageListfunction is reallyWhat you probably want to do is change
fto have typeIO (Int, Surface)rather than(Int, IO Surface). And then you canmapM f, yielding a single I/O action that returns a list of stuff.Regarding
imageFromID: what you probably want to do is something like this:The type of
imageFromIDthen becomes(Since
imagesnow has type[(Int, SDL.Surface)], with noIOin it, thanks to<-.)What you're doing here is that
createIDImageListis actually loading everything off disk, and then you can useimageFromID(which has no I/O in it) whenever you want to get the surface you're interested in.