I have the following code. I'd like to be able to modify the active player's life when given a game state. I came up with an activePlayer lens, but when I try and use it in combination with the -= operator I receive the following error:
> over (activePlayer.life) (+1) initialState
<interactive>:2:7:
No instance for (Contravariant Mutator)
arising from a use of `activePlayer'
Possible fix:
add an instance declaration for (Contravariant Mutator)
In the first argument of `(.)', namely `activePlayer'
In the first argument of `over', namely `(activePlayer . life)'
In the expression: over (activePlayer . life) (+ 1) initialState``
and the code in question:
{-# LANGUAGE TemplateHaskell #-}
module Scratch where
import Control.Lens
import Control.Monad.Trans.Class
import Control.Monad.Trans.State
import Data.Sequence (Seq)
import qualified Data.Sequence as S
data Game = Game
{ _players :: (Int, Seq Player) -- active player, list of players
, _winners :: Seq Player
}
deriving (Show)
initialState = Game
{ _players = (0, S.fromList [player1, player2])
, _winners = S.empty
}
data Player = Player
{ _life :: Integer
}
deriving (Show, Eq)
player1 = Player
{ _life = 10
}
player2 = Player
{ _life = 10
}
makeLenses ''Game
makeLenses ''Player
activePlayer
:: (Functor f, Contravariant f) =>
(Player -> f Player) -> Game -> f Game
activePlayer = players.to (\(i, ps) -> S.index ps i)
Each player takes their turn in order. I need to keep track of all the players at once as well as which is currently active, which is the reason for how I structured that, although I am open to different structures since I probably don't have the right one yet.
When you compose various items in the lens library with
(.)they may lose capabilities according to a kind of subtyping (see below). In this case, you've composed aLens(players) with aGetter(to ffor some functionf) and thus the combination is just aGetterwhileoveracts on lenses that can both get and set.activePlayershould form a valid lens, though, so you can just write it manually as a getter/setter pair. I'm writing it partially below under the assumption that the index can never be invalid.To better understand the subtyping that's occurring in the
lenslibrary we can use the Big Lattice Diagram from HackageWhenever you combine two lens types with
(.)you end up with their first common descendent in that chart. So if you combineLensandPrismyou can see that their arrows converge onTraversal. If you combineLensandGetter(of whichto fis) then you get aGettersinceGetteris a direct descendent ofLens.