I am mucking around with free monads and lens, using the free monad to create my own version of the IO monad:
data MyIO next
= LogMsg String next
| GetInput (String -> next)
deriving (Functor)
I am stacking this on top of a state monad like so: FreeT MyIO (State GameState) a
where GameState
is:
data GameState = GameState { _players :: [PlayerState] }
Now, what I would like to have is a way to "zoom-into" a PlayerState
from a GameState
context. Something like this:
zoomPlayer :: Int -> FreeT MyIO (State PlayerState) a -> FreeT MyIO (State GameState) a
zoomPlayer i prog = hoistFreeT (zoom (players . element i)) prog
But I'm getting this error:
No instance for (Data.Monoid.Monoid a1)
arising from a use of ‘_head’
This error seems related to the fact that players . element i
is a traversal; if I remove the list aspect from _players
and use normal lens then the code works.
Any ideas on how to write this function?
If you are sure you'll never index into a non-existing player and don't mind a little unsafety, you can use the unsafeSingular
combinator to turn a Traversal
into a Lens
, like this:
zoomPlayer :: Int -> FreeT MyIO (State PlayerState) a -> FreeT MyIO (State GameState) a
zoomPlayer i prog = hoistFreeT (zoom (players . unsafeSingular (element i))) prog
(Also, perhaps I would use ix
instead of element
, but that's unrelated to the problem.)
We can also construct safe indexing lenses for always-infinite sequences, like streams defined using Cofree
from the free
package:
import Control.Lens (Lens', _Wrapped')
import Control.Comonad.Cofree (Cofree, telescoped)
import Data.Functor.Identity
import Control
sureIx :: Int -> Lens' (Cofree Identity a) a
sureIx i = telescoped $ replicate i _Wrapped'
But a game is unlikely to have infinite players.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With