Home > front end >  Can you set the field of a record using an IO function (inside a do statement)?
Can you set the field of a record using an IO function (inside a do statement)?

Time:11-04

In my main function for my haskell game, I start by loading each sprite. I decided to make a data type called SpriteCollection which holds all the sprites (typed Picture) so I can access them later by their name.

loadBMP is a Gloss function typed String -> IO Picture. This code works:

main = do
  -- Load sprites
  playerBmp <- loadBMP "assets/mario.bmp"
  goombaBmp <- loadBMP "assets/goomba.bmp"
  brickBmp <- loadBMP "assets/brick.bmp"
  let sprites = SpriteCollection { 
      marioSprite = playerBmp,
      goombaSprite = goombaBmp
      brickSprite = brickBmp
    }
  playIO (... sprites ...)

So as you can see, as I load more sprites, it feels like I am writing double the code that I should need. Is there a way to get rid of the intermediate variables? For example:

main = do
  -- Load sprites
  let sprites = SpriteCollection { 
      marioSprite <- loadBMP "assets/mario.bmp",
      goombaSprite <- loadBMP "assets/goomba.bmp",
      brickSprite <- loadBMP "assets/brick.bmp"
    }
  playIO (... sprites ...)

but that does not work.

CodePudding user response:

Applicative is the tool for combining multiple values that are in the same sort of context.

main = do
  sprites <- SpriteCollection <$> 
    loadBMP "mario" <*> 
    loadBMP "goomba" <*> 
    loadBMP "brick"
  ...

In general,

do
  x <- mx
  y <- my
  return (f x y)

can be written as

f <$> mx <*> my

but only if the final call to f is the only thing that references x and y. This approach scales to arbitrarily many arguments. For small numbers of arguments, you can try something from the liftA family of functions, such as liftA2 f mx my.

CodePudding user response:

You can use RecordWildCards for that:

{-# LANGUAGE RecordWildCards #-}

main = do
  -- Load sprites
  marioSprite <- loadBMP "assets/mario.bmp"
  goombaSprite <- loadBMP "assets/goomba.bmp"
  brickSprite <- loadBMP "assets/brick.bmp"
  let sprites = SpriteCollection {..}
  playIO (... sprites ...)
  • Related