无点镜头创建不进行类型检查

2024-05-11

在函数中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"]

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'是一种排名较高的类型,类型推断非常脆弱,并且基本上仅在所有采用较高排名参数的函数都具有显式签名时才有效。这对于使用无点代码非常糟糕.等等,没有这样的签名。 (仅有的$有时有一个特殊的技巧可以解决这个问题。)

The lens库本身通过确保所有函数都可以解决这个问题use镜头参数没有完全通用的镜头类型,而只有一种表明它们的精确镜头特征的类型use.

在你的情况下,这是printHandle函数是造成这种情况的罪魁祸首。如果您将其签名更改为更精确,您的代码将会编译

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

我通过删除原始签名并使用找到了这个签名:t printHandle.

编辑(并再次编辑以添加ALens'):如果您认为“治疗比疾病更糟糕”,那么根据您的需要,还有另一种选择,它不需要您更改函数签名,但需要更改does要求你做一些显式转换,就是使用ALens'改为键入。然后您需要更改两行:

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

ALens'是一种非更高级别的类型,经过巧妙构造,因此它包含从中提取通用镜头所需的所有信息cloneLens。但它仍然is镜头的特殊子类型(Functor刚刚被特别巧妙地选择)所以你只需要显式转换from ALens' to Lens',而不是相反。

第三种选择,对于镜头来说可能不是最好的,但通常适用于更高级别的类型general,就是把你的PlayerLens into a newtype:

newtype PlayerLens = PL (Lens' GameState Player)

当然,现在需要在代码中的多个位置进行包装和解包。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
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

无点镜头创建不进行类型检查 的相关文章

随机推荐