Transfer the lens to fanciton

How to properly transfer the lens to a state function? Consider the following 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 ab | 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 () 

A marked line results in the following 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 you replace the string with something like armies.ix 0.troops.=0 , the code usually compiles. Are there standard tools to solve this problem? And is it possible to implement the same algorithm without using FlexibleContexts ?

+2
source share
1 answer

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>

+2
source

Source: https://habr.com/ru/post/1273129/


All Articles