Just use type signatures!
What happens here: if you don't give a signature, the GHC will be able to infer the & dagger 1 type . In this example, you use defender.troops as getter & ddagger; ; therefore, the compiler introduces a getter type for defender . This is the ugly thing in the error message with Const in it.
However, you also want to use it as a setter. This is only possible if defender is polymorphic (so you can use the Identity functor instead of Const ), and for an argument that should be polymorphic, you need Rank-2 polymorphism.
You really don't need to worry about this category theory magic, as the lens library provides easy-to-use synonyms. Just write a signature, as you should always do anyway,
offend :: Traversal' Game Army -> Traversal' Game Army -> State Game BattleResult
and you get the correct polymorphic arguments. Ah, and of course you need the extension -XRankNTypes . -XFlexibleContexts is not actually required (it is completely harmless, although it makes no sense not to use it).
& dagger; Hindley Milner is still a miracle if you ask me, but it only works because there is a well-defined most common signature possible for any expression. This only applies to the Rank-1 code, though: with Rank-N you can always toss up another level of universal quantification. The compiler cannot know when this will end!
& ddagger; Actually a Getting , which is a crawler. The difference between Getter and Getting is that the latter may be partial (which is necessary because you are using ix , the compiler cannot prove that there really is an element in index 1 in the list of armies). Sub>
source share