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 ...)