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 f
for some functionf
) and thus the combination is just aGetter
whileover
acts on lenses that can both get and set.activePlayer
should 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
lens
library 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 combineLens
andPrism
you can see that their arrows converge onTraversal
. If you combineLens
andGetter
(of whichto f
is) then you get aGetter
sinceGetter
is a direct descendent ofLens
.