Tho*_*ing 5 haskell pointfree haskell-lens
在函数中test,我遍历一个列表,从它的成员生成镜头,然后打印一些数据.当我使用有点的调用样式时,这可以工作.当我点免费时,它无法进行类型检查.
为什么会这样,我该如何解决这个问题呢?
对我来说,GHC并没有保留使用无点风格时排名较高f(镜头中)的信息Functor,但我不太确定.
我正在使用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
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
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"]
Run Code Online (Sandbox Code Playgroud)
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.
Run Code Online (Sandbox Code Playgroud)
Lens'是一个排名较高的类型,类型推断是非常脆弱的,并且基本上只有当所有采用更高级别参数的函数都有明确的签名时才会起作用..对于没有这种签名的无点代码使用等,这非常糟糕.(只有$特殊的黑客才能使用它.)
该lens库本身通过确保所有的功能来解决这个用镜头参数没有为它完全通用的镜头类型,但只有一个类型的指示,他们的精确镜头功能使用.
在你的情况下,它的printHandle功能是罪魁祸首.如果您将其签名更改为更精确,则代码将编译
printHandle :: s -> Getting Player s Player -> IO ()
Run Code Online (Sandbox Code Playgroud)
我通过删除原始签名并使用来找到此签名:t printHandle.
编辑(再次编辑补充ALens'):如果你认为"治疗比疾病更糟糕",然后根据自己的需要的另一种选择,它不需要你改变你的函数签名,但不要求你做一些显式转换,就是改用ALens'类型.然后,您需要更改两行:
type PlayerLens = ALens' GameState Player
...
printHandle st playerLens = do
let player = st^.cloneLens playerLens
...
Run Code Online (Sandbox Code Playgroud)
ALens'是一种非高级别的类型,它巧妙地构造,以便它包含从中提取普通镜头所需的所有信息cloneLens.但它仍然是一个镜头的特殊亚型(在Functor刚刚尤其是巧妙地选用),所以你只需要显式转换从 ALens'到Lens',而不是相反.
第三个选项,可能不是最好的镜头,但通常适用于更高级别的类型,一般是将你PlayerLens变成newtype:
newtype PlayerLens = PL (Lens' GameState Player)
Run Code Online (Sandbox Code Playgroud)
当然,现在需要在代码中的几个位置包装和解包.getPlayerLens特别中断了:
getPlayerLens :: PlayerHandle -> PlayerLens
getPlayerLens 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
Run Code Online (Sandbox Code Playgroud)