How to pass properly a lens into a function with state? Let us consider the next code:
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE FlexibleContexts #-}
import Control.Lens
import Control.Monad.State
data Game = Game { _armies :: [Army]
} deriving (Show)
data Army = Army { _troops :: Int
} deriving (Show)
makeLenses ''Game
makeLenses ''Army
data BattleResult = Win | Defeat deriving (Show)
offend offender defender = do
Just ot <- preuse $ offender.troops
Just dt <- preuse $ defender.troops
defender.troops.=0 -- doesn't work
let eval a b
| a >= b = return Win
| otherwise = return Defeat
eval ot dt
game :: State Game ()
game = do
armies %= (:) (Army 100)
armies %= (:) (Army 200)
q <- offend (armies.ix 0) (armies.ix 1)
return ()
The marked line leads to the next error:
Lens.hs:21:3:
Couldn't match type ‘Const (Data.Monoid.First Int) s’
with ‘Identity s’
Expected type: (Army -> Const (Data.Monoid.First Int) Army)
-> s -> Identity s
Actual type: (Army -> Const (Data.Monoid.First Int) Army)
-> s -> Const (Data.Monoid.First Int) s
Relevant bindings include
defender :: (Army -> Const (Data.Monoid.First Int) Army)
-> s -> Const (Data.Monoid.First Int) s
(bound at Lens.hs:18:17)
offender :: (Army -> Const (Data.Monoid.First Int) Army)
-> s -> Const (Data.Monoid.First Int) s
(bound at Lens.hs:18:8)
offend :: ((Army -> Const (Data.Monoid.First Int) Army)
-> s -> Const (Data.Monoid.First Int) s)
-> ((Army -> Const (Data.Monoid.First Int) Army)
-> s -> Const (Data.Monoid.First Int) s)
-> m BattleResult
(bound at Lens.hs:18:1)
In the first argument of ‘(.)’, namely ‘defender’
In the first argument of ‘(.=)’, namely ‘defender . troops’
Lens.hs:21:12:
Couldn't match type ‘Identity Integer’
with ‘Const (Data.Monoid.First Int) Int’
Expected type: (Int -> Identity Integer)
-> Army -> Const (Data.Monoid.First Int) Army
Actual type: (Int -> Const (Data.Monoid.First Int) Int)
-> Army -> Const (Data.Monoid.First Int) Army
In the second argument of ‘(.)’, namely ‘troops’
In the first argument of ‘(.=)’, namely ‘defender . troops’
If replace the line with something like armies.ix 0.troops.=0
the code is normally compiled. Are there some standard tools to walk around the problem? And could the same algorithm be implemented without using FlexibleContexts
?