5

My program has a client and a server component that need to communicate. They each have a state that they mutate. The mutation is done explicitly in the following way:

There a relation

class Diff a b where
    commit :: a -> b -> a

That says that type b can be considered a 'chunk' of type a and that you can modify objects of type a by 'committing' object of type b to them.

So you modify the state, those chunks get logged and transmitted to the client which also has an instance of the above relation, just for a different type a.

Now, here's the deal. My server's state is a record with many field. All of them can change since, you know, it's a state. That's what it does. This leaves me having to write a separate case in my 'chunk' type for each possible field so I can transmit them over the network. It would be lovely if I could somehow transmit any state -> state function, but I don't see that happening. The client shares the servers code, so it does know about the structure of my 'chunks', and the part where it has to interpret them to update it's own, local, state is something that I can manage.

What I was wondering is if I could maybe use lens here to automate the generation of update chunks. All my 'chunk' type is, after all, is a clumsy setter with a generic structure I can serialize.

What would be the best way to avoid repeating code here?

To give you a better idea of how my code currently looks, here's an example of how the server's state looks for a game of pong.

data State = State
           { playerPaddle :: Double
           , aiPaddle :: Double
           , ball :: Vec2 Double
           , ballV :: Vec2 Double } deriving Show

data Update = BallPos (Vec2 Double)
            | BallVel (Vec2 Double)
            | PlayerPos Double
            | AIPos Double

instance Diff State Update where
    commit s (BallPos p) = s { ball = p }
    commit s (BallVel p) = s { ballV = p }
    commit s (PlayerPos p) = s { playerPaddle = p }
    commit s (AIPos p) = s { aiPaddle = p }
Luka Horvat
  • 4,283
  • 3
  • 30
  • 48
  • With `lens` you could associate an `Update` with a particular lens, then use that within `commit`, but it's just moving the association elsewhere and adding a large dependency to your project. It would look like `commit s update = updateToModifier update s` and `updateToModifier (BallPos p) = ball .~ p`; `updateToModifier (BallVel p) = ballV .~ p`; `updateToModifier (PlaerPos p) = playerPaddle .~ p`; `updateToModifier (AIPos p) = aiPaddle .~ p`. This would make `commit` simple, but you would still have all the complexity with `updateToModifier`. – bheklilr Jan 19 '15 at 19:22
  • @bheklilr Yeah. I'm hoping there's a convenient solution. – Luka Horvat Jan 19 '15 at 19:24
  • If the client and server share code, you might be able to use the new `StaticPointers` extension (ghc dev branch only for now) to serialize `state -> state` functions. – cdk Jan 19 '15 at 20:09
  • @cdk I'm not comfortable with using an unreleased version of GHC, but that does look like it would be a great solution to my issue. – Luka Horvat Jan 19 '15 at 22:27

0 Answers0