2014-12-22 7 views
0

Вопрос:Haskell с помощью ввода-вывода на функцию, которая не ожидает, что

Как я даю «IO SDL.Surface» в функцию, которая ожидает «SDL.Surface»?

Я предпочел бы переосмыслить весь свой подход, чем использовать что-то вроде «unsafePerformIO», если только на самом деле это не правильное время для его использования (что я сомневаюсь).

Дополнительная информация:

У меня был файл, заполненный числами и путей файлов и я разобран этот файл и загрузить изображения, расположенные на этих путях в список [(Int, IO SDL.Surface)] , Проблема в том, что функция SDL.blitSurface ожидает нормальной SDL.Surface.

Сообщение об ошибке:

Couldn't match type `IO SDL.Surface' 
       with `GHC.ForeignPtr.ForeignPtr SDL.SurfaceStruct' 
Expected type: SDL.Surface 
Actual type: IO SDL.Surface 

Я не уверен, что исходный код необходим, чтобы ответить на этот вопрос, но я приведу некоторые все равно только в случае, если это помогает:

Чтобы загрузить изображение файл я использую:

loadImage :: FilePath -> IO SDL.Surface 
loadImage [] = error "empty list" 
loadImage a = 
    SDL.loadBMP a 

Чтобы создать список номеров и изображений, которые я использую:

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) 

Чтобы получить правильную картинку из этого списка, я использую эту функцию:

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) 

И, наконец, я использую imageFromID с SDL.blitSurface нарисовать изображение, за исключением того, что я не могу из-за ввод-вывод ,

ответ

2

Любое время, когда вы закончите с [IO Foobar], что вы возможно хотите на самом деле IO [Foobar]. Функция sequence преобразует одну в другую. Или вы можете использовать mapM вместо map при создании списка в первую очередь.

В вашем примере это немного сложнее, так как у нас есть [(Int, IO Surface)]. Позвольте мне посмотреть, что я могу предложить ...

loadImage - это действие ввода-вывода. Он принимает имя файла и возвращает действие IO для загрузки изображения. Ваша createIDImageList функция действительно

createIDImageList = map f 
    where 
    f a = (tiletypeid a, loadImage (C8.unpack (tiletypeimage a))) 

То, что вы, вероятно, хотите сделать, это изменить f иметь тип IO (Int, Surface), а не (Int, IO Surface). И тогда вы можете mapM f, давая одно действие ввода-вывода, которое возвращает список вещей.

createIDImageList :: [Tiletype] -> IO [(Int, SDL.Surface)] 
createIDImageList = mapM f 
    where 
    f a = do 
     surface <- loadImage (C8.unpack (tiletypeimage a)) 
     return (tiletypeid a, surface) 

Что касается imageFromID: то, что вы вероятно хотите сделать что-то вроде этого:

main = do 
    images <- createIDImageList (...) 
    ... 
    let image5 = imageFromID 5 images 
    SDL.blitSurface image5 ... 

Тип imageFromID становится

imageFromID :: `Int -> [(Int, SDL.Surface)] -> Maybe SDL.Surface 

images сейчас имеет тип [(Int, SDL.Surface)] с в нем нет IO, благодаря <-.)

Что вы здесь делаете, что createIDImageList на самом деле загружается всё с диска, а затем вы можете использовать imageFromID (который не имеет никакого ввода/вывода в него) всякий раз, когда вы хотите получить интересующую вас поверхность.

+0

Thanks MathematicalOrchid, похоже, это действительно в правильном направлении. Мне нужно также изменить функцию imageFromID, не так ли? – user3638162

+0

@ user3638162 Да, это так. Можете ли вы это исправить, или вам нужен намек? – MathematicalOrchid

+0

К сожалению, мне нужен намек. Я знаю, что функция должна стать «imageFromID :: Int -> IO [(Int, SDL.Surface)] -> Может быть (SDL.Surface)', но я не уверен, как это записать. Мне нужно использовать карту для этого? – user3638162

0
do image <- loadImage "imagefile" 
    blitSurface image rect1 dest rect2