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]
. Thesequence
function transforms one into the other. Or you can usemapM
instead ofmap
when 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...loadImage
is an I/O action. It takes a filename and returns an IO action to load the image. YourcreateIDImageList
function is reallyWhat you probably want to do is change
f
to 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
imageFromID
then becomes(Since
images
now has type[(Int, SDL.Surface)]
, with noIO
in it, thanks to<-
.)What you're doing here is that
createIDImageList
is 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.