Creating a lens without a lens does not require verification

In the test function, I traverse the list, generate lenses from it, and then print some data. This works when I use the point invocation style. It cannot be checked by typecheck when I do this without dots.

Why is this so, and how can I solve this problem?

It seems to me that the GHC does not store information that a higher f rating (in the lens) is Functor when using the dot style, but I'm not sure.

I am using GHC 7.8.3

 {-# LANGUAGE RankNTypes #-} {-# LANGUAGE TemplateHaskell #-} import Control.Lens import Control.Monad import Data.List import Data.Maybe type PlayerHandle = String data Player = Player { _playerHandle :: PlayerHandle } makeLenses ''Player data GameState = GameState { _gamePlayers :: [Player] } makeLenses ''GameState type PlayerLens = Lens' GameState Player getPlayerLens :: PlayerHandle -> PlayerLens getPlayerLens handle f st = fmap put' get' where players = st^.gamePlayers put' player = let gp = case p^.playerHandle == handle of True -> player False -> p in set gamePlayers (map g players) st get' = f $ fromJust $ find (\p -> p^.playerHandle == handle) players printHandle :: GameState -> PlayerLens -> IO () printHandle st playerLens = do let player = st^.playerLens print $ player^.playerHandle test :: GameState -> IO () test st = do let handles = toListOf (gamePlayers.traversed.playerHandle) st -- -- Works: Pointful --forM_ handles $ \handle -> printHandle st $ getPlayerLens handle -- -- Does not work: Point-free forM_ handles $ printHandle st . getPlayerLens main :: IO () main = test $ GameState [Player "Bob", Player "Joe"] 

 Test.hs:45:38: Couldn't match type `(Player -> f0 Player) -> GameState -> f0 GameState' with `forall (f :: * -> *). Functor f => (Player -> f Player) -> GameState -> f GameState' Expected type: PlayerHandle -> PlayerLens Actual type: PlayerHandle -> (Player -> f0 Player) -> GameState -> f0 GameState In the second argument of `(.)', namely `getPlayerLens' In the second argument of `($)', namely `printHandle st . getPlayerLens' Failed, modules loaded: none. 
+5
source share
1 answer

Lens' is a more ranked type, and type inference is very fragile with them and basically only works when all functions that take arguments of a higher rank have explicit signatures for this. This works very poorly with contactless code using . and the like, which do not have such signatures. (Only $ has a special hack to work with it sometimes.)

The lens library itself goes around this, making sure that all functions that use the lens argument do not have a completely common lens type for it, but only a type that indicates the exact lens that they use.

In your case, this is the printHandle function, which is the culprit of this. Your code will be compiled if you change your signature to a more accurate one.

 printHandle :: s -> Getting Player s Player -> IO () 

I found this signature by deleting the original one and using :t printHandle .

EDIT (and add ALens' again EDIT): if you think that “treatment is worse than the disease”, then depending on your needs there is another option that does not require you to change the signature of your function, but which requires you to do what This is an explicit conversion, use the ALens' type ALens' . Then you need to change two lines:

 type PlayerLens = ALens' GameState Player ... printHandle st playerLens = do let player = st^.cloneLens playerLens ... 

ALens' is a type of no higher rank that has been thought out, so that it contains all the information needed to extract a common lens from it using cloneLens . But it is still a special subtype of the lens (only Functor was specifically selected), so you only need an explicit conversion from ALens' to Lens' , and not vice versa.

The third option, which may not be the best for lenses, but which usually works for higher rank types in general, is to turn your PlayerLens into a newtype :

 newtype PlayerLens = PL (Lens' GameState Player) 

Of course, now this requires both packaging and deployment in several places of your code. getPlayerLens was especially broken:

 getPlayerLens :: PlayerHandle -> PlayerLens getPlayerLens handle = PL playerLens where playerLens f st = fmap put' get' where players = st^.gamePlayers put' player = let gp = case p^.playerHandle == handle of True -> player False -> p in set gamePlayers (map g players) st get' = f $ fromJust $ find (\p -> p^.playerHandle == handle) players 
+8
source

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


All Articles