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 a Lens
(players
) with a Getter
(to f
for some function f
) and thus the combination is just a Getter
while over
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.
activePlayer :: Lens' Game Player
activePlayer = lens get set
where
get :: Game -> Player
get (Game { _players = (index, seq) }) = Seq.index seq index
set :: Game -> Player -> Game
set g@(Game { _players = (index, seq) }) player =
g { _players = (index, Seq.update index player seq) }
To better understand the subtyping that's occurring in the lens
library we can use the Big Lattice Diagram from Hackage
Whenever you combine two lens types with (.)
you end up with their first common descendent in that chart. So if you combine Lens
and Prism
you can see that their arrows converge on Traversal
. If you combine Lens
and Getter
(of which to f
is) then you get a Getter
since Getter
is a direct descendent of Lens
.
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