Search code examples
haskellhaskell-lens

How to define settable lens


I have a record type in my Haskell code to which I want to define a lens that can be used as a getter and as a setter. The code looks like this:

data Players = Players { _white :: Player
                       , _black :: Player
                       } deriving (Show, Eq)
makeLenses ''Players
_byColor :: Color -> Players -> Player
_byColor White = _white
_byColor Black = _black
byColor col = to (_byColor col)

Players is a record holding the white and the black player. I want to be able to get a player by color in a lens fashion. E.g.

players ^. byColor White . hp  -- Access health-points of player

However, I also want to be able to set a property of a player chosen by color. E.g. within a state-monad holding a Game record with a _players field.

let current = White
players . byColor current . hp %= (-1)

With my current definition of byColor however the code fails to compile with the following error message:

No instance for (Contravariant Identity)
  arising from a use of `byColor'
Possible fix:
  add an instance declaration for (Contravariant Identity)

What am I doing wrong?


Solution

  • The to combinator only makes a getter, you haven't defined a full lens. However, defining the full lens is pretty straightforward, you just need to return the correct lens based on the color passed in:

    byColor :: Color -> Lens' Players Player
    byColor White = white
    byColor Black = black