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
.