2

I built a small game engine to manage a board of squares (currently used for playing a Conway's game of life). All the data is accessed throught lenses from fclabels and State. The engine couples user input and graphic rendering (usual game loop).

The computations between frames can sometimes be slow and long to execute. So I would like to use concurrency to manage the squares, using STM's TVar.

My data is currently represented like this:

data World = World {
    … -- window configuration, not important
    , _squares :: TVar [Square]
}

mkLabels [''World] -- creates labels, similar to mkLenses

My functions run in the Game Monad, which is defined as follow:

type Game a = StateT World IO a

Using the monadic versions of labels. I use Getters & Setters inside my monad.

I would like to know if there is a way to somehow write new labels that behave like these:

gets :: MonadState f m => Lens (->) f o -> m o 
…
puts :: MonadState f m => Lens (->) f o -> o -> m () 

But that takes care of STM (gets would involve readTVar, puts would involve writeTvar, etc.).

qleguennec
  • 544
  • 4
  • 12
  • 1
    Before looking at concurrency, you might wanna look for your bottlenecks first. If you're just doing game of life, you should easily be able to get a few hundred updates per second even on modest hardware. – Cubic Aug 06 '15 at 19:19
  • @Cubic the game of life is currently only for testing the engine. This is no optimization problem, but being able to process inputs in the same time than computing game mechanics. – qleguennec Aug 06 '15 at 20:13
  • @Cubic the frequency also depends on the size of the board. – qleguennec Aug 06 '15 at 20:23

1 Answers1

3

If I understand you correctly, you want to define a lens tlens s.t.:

gets tlens

is the same as:

do tvar <- gets squares
   sqs <- liftIO $ atomically $ readTVar tvar
   return sqs

and where puts tlens sqs is the same as:

do tvar <- gets squares
   liftIO $ atomically $ writeTVar tvar sqs 

I think this can be answered by looking at the type of gets:

gets :: MonadState f m => Lens (->) f o -> m o

The lens parameter is pure and not monadic. To get at the contents of the TVar you'll need to run code in the IO-monad.

Moreover, the definition of gets in Data.Label.Monadic is (link) is:

gets lens = State.gets (Total.get lens)

where State is Control.Monad.State and Total is Data.Label.Total.

But State.gets takes a pure function, so again you're not going to be able to create a lens which will work with gets.

ErikR
  • 51,541
  • 9
  • 73
  • 124
  • I was aware of that, and I thought I could somehow get around by using [Data.Label.Point](https://hackage.haskell.org/package/fclabels-2.0.2.2/docs/Data-Label-Point.html). It uses Arrows, which are (somewhat?) related to Monads. Should I give it a try, or am I just talking nonsense? I don't quite understand Points yet, but they surely seems extensible. – qleguennec Aug 06 '15 at 20:10
  • 1
    Have a look at this answer: http://stackoverflow.com/questions/18794745/can-i-make-a-lens-with-a-monad-constraint - it even has an STM example. – ErikR Aug 06 '15 at 20:49