newtype PlayerLens = PL (Lens' GameState Player)当然,现在这需要在代码中的多个位置进行包装和展开. getPlayerLens特别受到干扰:getPlayerLens :: PlayerHandle -> PlayerLensgetPlayerLens handle = PL playerLens where playerLens f st = fmap put' get' where players = st^.gamePlayers put' player = let g p = case p^.playerHandle == handle of True -> player False -> p in set gamePlayers (map g players) st get' = f $ fromJust $ find (\p -> p^.playerHandle == handle) playersIn the function test, I traverse over a list, generate lenses from it's members, and then print some data. This works when I use a pointful call style. It fails to typecheck when I make it point-free.Why is this the case, and how can I solve this problem?It looks like to me that GHC is not retaining the information that the higher-ranked f (in the lens) is a Functor when using point-free style, but I'm not too sure.I'm using GHC 7.8.3{-# LANGUAGE RankNTypes #-}{-# LANGUAGE TemplateHaskell #-}import Control.Lensimport Control.Monadimport Data.Listimport Data.Maybetype PlayerHandle = Stringdata Player = Player { _playerHandle :: PlayerHandle }makeLenses ''Playerdata GameState = GameState { _gamePlayers :: [Player] }makeLenses ''GameStatetype PlayerLens = Lens' GameState PlayergetPlayerLens :: PlayerHandle -> PlayerLensgetPlayerLens handle f st = fmap put' get' where players = st^.gamePlayers put' player = let g p = case p^.playerHandle == handle of True -> player False -> p in set gamePlayers (map g players) st get' = f $ fromJust $ find (\p -> p^.playerHandle == handle) playersprintHandle :: GameState -> PlayerLens -> IO ()printHandle st playerLens = do let player = st^.playerLens print $ player^.playerHandletest :: 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 . getPlayerLensmain :: 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. 解决方案 Lens' is a higher ranked type, and type inference is very brittle with those, and basically only works when all functions that take higher-rank arguments have explicit signatures to do so. This works very badly with point-free code using . and the like, which don't have such signatures. (Only $ has a special hack to sometimes work with this.)The lens library itself gets around this by making sure that all functions that use a lens argument don't have a fully general lens type for it, but only a type which indicates the precise lens feature they use.In your case, it's the printHandle function which is the culprit for this. Your code will compile if you change its signature to the more preciseprintHandle :: s -> Getting Player s Player -> IO ()I found this signature by deleting the original one and using :t printHandle.EDIT (and EDIT again to add ALens'): If you think the "cure is worse than the illness", then depending on your needs another option, which doesn't require you to change your function signatures, but which does require you to do some explicit conversion, is to use the ALens' type instead. You then need to change two lines:type PlayerLens = ALens' GameState Player...printHandle st playerLens = do let player = st^.cloneLens playerLens...ALens' is a non-higher rank type that has been cleverly constructed so that it contains all the information needed to extract a general lens from it with cloneLens. But it still is a special subtype of a lens (the Functor has just been particularly cleverly chosen) so you only need explicit conversion from ALens' to Lens', not the other way.A 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 this now needs both wrapping and unwrapping in several places in your code. getPlayerLens was particularly disrupted:getPlayerLens :: PlayerHandle -> PlayerLensgetPlayerLens handle = PL playerLens where playerLens f st = fmap put' get' where players = st^.gamePlayers put' player = let g p = 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 这篇关于无点镜头创建无法进行类型检查的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!
10-11 09:37