Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Haskell: "how much" of a type should functions receive? and avoiding complete "reconstruction"

I've got these data types:

data PointPlus = PointPlus
    { coords :: Point
    , velocity :: Vector
    } deriving (Eq)

data BodyGeo = BodyGeo
    { pointPlus :: PointPlus
    , size :: Point
    } deriving (Eq)

data Body = Body
    { geo :: BodyGeo
    , pict :: Color
    } deriving (Eq)

It's the base datatype for characters, enemies, objects, etc. in my game (well, I just have two rectangles as the player and the ground right now :p).

When a key, the characters moves right, left or jumps by changing its velocity. Moving is done by adding the velocity to the coords. Currently, it's written as follows:

move (PointPlus (x, y) (xi, yi)) = PointPlus (x + xi, y + yi) (xi, yi)

I'm just taking the PointPlus part of my Body and not the entire Body, otherwise it would be:

move (Body (BodyGeo (PointPlus (x, y) (xi, yi)) wh) col) = (Body (BodyGeo (PointPlus (x + xi, y + yi) (xi, yi)) wh) col)

Is the first version of move better? Anyway, if move only changes PointPlus, there must be another function that calls it inside a new Body. I explain: there's a function update which is called to update the game state; it is passed the current game state, a single Body for now, and returns the updated Body.

update (Body (BodyGeo (PointPlus xy (xi, yi)) wh) pict) = (Body (BodyGeo (move (PointPlus xy (xi, yi))) wh) pict)

That tickles me. Everything is kept the same within Body except the PointPlus. Is there a way to avoid this complete "reconstruction" by hand? Like in:

update body = backInBody $ move $ pointPlus body

Without having to define backInBody, of course.

like image 520
L01man Avatar asked Jun 10 '12 10:06

L01man


1 Answers

You're looking for "lenses". There are several different packages for lenses; here is a good summary of them.

My understanding is that a lens on a data type a for some field b provides two operations: a way to get the value of b and a way to get a new a with a different value of b. So you would just use a lens to work with the deeply nested PointPlus.

The lens packages provide useful functions for working with lenses as well as ways of generating lenses automatically (with template Haskell) which could be very convenient.

I think they are worth looking into for your project, especially because you are likely to encounter similar problems with nesting in other places thanks to the structure of your data types.

like image 171
Tikhon Jelvis Avatar answered Nov 07 '22 13:11

Tikhon Jelvis