Search code examples
haskellhaskell-lens

Lens access Map Key


I have some problems using Lens library to acces Map Data type.

data Card
  = Ferme
  | Boulangerie

data PlayerState = PlayerState {
  _psCards      :: Map Card Int,
  } deriving (Show)


data GameState = GameState {
  _gsPlayers      :: [PlayerState]
                 } deriving (Show)

I have difficulties accessing the Map

step :: (MonadState s m, HasGameState s, MonadIO m) => m ()
step = do
    i <- use $ gsPlayers . ix 0 . psCards . ix Ferme

with the following error:

    • Could not deduce (Monoid Int) arising from a use of ‘ix’
      from the context: (MonadState s m, HasGameState s, MonadIO m)
        bound by the type signature for:
                   step :: forall s (m :: * -> *).
                           (MonadState s m, HasGameState s, MonadIO m) =>
                           m ()

Is this because I use ix with different argument in the same line?


Solution

  • It's because ix is a Traversal, but use expects a Lens. The difference is that a Lens always has exactly one target. A Traversal can have zero or more. The combinators that expect lenses when retrieving a value sort of accidentally try to combine multiple values together as a Monoid when given a Traversal. (It comes from the Applicative instance for Const, in particular.) That attempt doesn't type check in your case because no such instance exists for the target type, so you get that error message.

    You probably want the preuse combinator instead of use, to account for the value potentially not being there.